[kernel] r5783 - in dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27: . debian
Dann Frazier
dannf at costa.debian.org
Thu Feb 9 20:50:22 UTC 2006
Author: dannf
Date: Thu Feb 9 20:49:31 2006
New Revision: 5783
Added:
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/changelog (contents, props changed)
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/control (contents, props changed)
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/copyright (contents, props changed)
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/dirs (contents, props changed)
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/kpatches (contents, props changed)
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/rules (contents, props changed)
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/linux-2.4.27-arm-2.4.27-1.diff
Log:
Load kernel-patch-2.4.27-arm-2.4.27 into
dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27.
Added: dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/changelog
==============================================================================
--- (empty file)
+++ dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/changelog Thu Feb 9 20:49:31 2006
@@ -0,0 +1,14 @@
+kernel-patch-2.4.27-arm (2.4.27-1) unstable; urgency=low
+
+ * Update head code with zImage fix for nettrom bootloader
+ * Ensure patch works correctly with updated Debian kernel source
+
+ -- Vincent Sanders <vince at debian.org> Thu, 18 Nov 2004 12:51:30 +0000
+
+kernel-patch-2.4.27-arm (2.4.27) unstable; urgency=high
+
+ * Initial patch set for 2.4.27 kernels based on 2.4.27-vrs1
+ * Incorporate riscstation network driver
+ * Incorporate bast machine patch set
+
+ -- Vincent Sanders <vince at debian.org> Thu, 19 Aug 2004 16:04:31 +0100
Added: dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/control
==============================================================================
--- (empty file)
+++ dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/control Thu Feb 9 20:49:31 2006
@@ -0,0 +1,13 @@
+Source: kernel-patch-2.4.27-arm
+Section: devel
+Priority: extra
+Maintainer: Vincent Sanders <vince at debian.org>
+Build-Depends-Indep: debhelper (>> 2.0.0), dh-kpatches
+Standards-Version: 3.6.1
+
+Package: kernel-patch-2.4.27-arm
+Architecture: all
+Depends: ${kpatch:Depends}, kernel-package
+Description: Diffs to the Linux kernel source 2.4.27 for ARM
+ Some extra patches are needed to build kernels for ARM machines.
+ These are based on the upstream architecture patches.
Added: dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/copyright
==============================================================================
--- (empty file)
+++ dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/copyright Thu Feb 9 20:49:31 2006
@@ -0,0 +1,24 @@
+This is the Debian GNU/Linux prepackaged version of the Linux kernel patch
+for ARM.
+Linux was written by Linus Torvalds <Linus.Torvalds at cs.Helsinki.FI> and
+others.
+
+Linux is copyrighted by Linus Torvalds and others.
+
+ 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 with
+ the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+
+On Debian GNU/Linux systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
Added: dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/dirs
==============================================================================
--- (empty file)
+++ dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/dirs Thu Feb 9 20:49:31 2006
@@ -0,0 +1,2 @@
+usr/bin
+usr/sbin
Added: dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/kpatches
==============================================================================
--- (empty file)
+++ dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/kpatches Thu Feb 9 20:49:31 2006
@@ -0,0 +1,7 @@
+Patch-name: ARM support for Linux version 2.4.27
+Patch-id: arm-2_4_27
+Architecture: arm
+Path-strip-level: 1
+
+Patch-file: linux-2.4.27-arm-2.4.27-1.diff
+Kernel-version: 2.4.27
Added: dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/rules
==============================================================================
--- (empty file)
+++ dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/debian/rules Thu Feb 9 20:49:31 2006
@@ -0,0 +1,61 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This is the debhelper compatability version to use.
+export DH_COMPAT=2
+
+build:
+ echo nothing to build
+
+clean:
+ dh_testdir
+ dh_testroot
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+
+# Build architecture-dependent files here.
+binary-arch:
+# We have nothing to do by default.
+
+# Build architecture-independent files here.
+binary-indep: build install
+ dh_testdir
+ dh_testroot
+# dh_installdebconf
+ dh_installdocs
+ dh_installexamples
+ dh_installmenu
+# dh_installemacsen
+# dh_installpam
+# dh_installinit
+ dh_installcron
+ dh_installmanpages
+ dh_installinfo
+ dh_installkpatches
+# dh_undocumented
+ dh_installchangelogs
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+ # You may want to make some executables suid here.
+# dh_makeshlibs
+ dh_installdeb
+# dh_perl
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
Added: dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/linux-2.4.27-arm-2.4.27-1.diff
==============================================================================
--- (empty file)
+++ dists/sarge-security/kernel-2.4/arm/kernel-patch-2.4.27-arm-2.4.27/linux-2.4.27-arm-2.4.27-1.diff Thu Feb 9 20:49:31 2006
@@ -0,0 +1,124600 @@
+diff -urN kernel-source-2.4.27-8/Documentation/Configure.help kernel-source-2.4.27-8-arm-1/Documentation/Configure.help
+--- kernel-source-2.4.27-8/Documentation/Configure.help 2005-01-19 09:57:51.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/Documentation/Configure.help 2005-02-18 17:48:34.000000000 +0000
+@@ -4995,6 +4995,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
+@@ -13415,6 +13422,17 @@
+ The module will be called tmspci.o. If you want to compile it
+ as a module, say M here and read <file:Documentation/modules.txt>.
+
++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.
+@@ -15353,6 +15371,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
+@@ -19409,6 +19437,20 @@
+ <file:Documentation/modules.txt>.
+ 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.
+@@ -20551,6 +20593,17 @@
+ <file:Documentation/modules.txt>. 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.
+@@ -22212,6 +22265,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
+@@ -25906,6 +25983,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)
+@@ -25913,6 +26004,14 @@
+ <http://www.visuaide.com/pagevictor.en.html> 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
+@@ -25980,37 +26079,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.
++ 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.
++ 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.
+
+-Round robin I and D cache replacement algorithm
++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.
++
++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
+@@ -26057,6 +26184,11 @@
+ Say Y here if you are using the inhand electronics OmniMeter. See
+ <http://www.inhandelectronics.com/html/omni1.html> 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
+@@ -26069,6 +26201,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
+@@ -26097,24 +26238,44 @@
+ 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
+- 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 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.
++ This is an experimental much faster emulator which is written
++ completely in ARM assembly. All instructions that are not marked as
++ deprecated in the ARM7500FE data sheet are implemented. It supports
++ single, double and double extended precision. It does support
++ exception flags, but not raising exceptions. It gives an average
++ 5 times speed increase over NWFPE for FP only code.
++
++ FastFPE does not require long multiply instruction anymore and is
++ now suitable for all ARM cpus. The presence of the long multiply
++ instruction is detected during initialisation and used to speedup
++ multiply and divide.
++
++ Compliance to IEEE Std 754-1985 was verified using the testfloat
++ program of the SoftFloat package version 2a by John Hauser. All
++ operations except square root were reported to be compliant. However,
++ this is not a proof, and especially does not verify the instruction
++ parser.
++
++ You can compile both emulators into the kernel and choose one
++ of them at boot time by passing "fpe=fastfpe" or "fpe=nwfpe" as
++ kernel parameter.
+
+ 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
+- emulator early in the bootup. You should never change from NWFPE to
+- FASTFPE or vice versa in an active system!
++ (fastfpe.o). This was never tested. Only do it if you know what
++ you are doing!
+
+ DS1620 thermometer support
+ CONFIG_DS1620
+diff -urN kernel-source-2.4.27-8/Documentation/arm/Porting kernel-source-2.4.27-8-arm-1/Documentation/arm/Porting
+--- kernel-source-2.4.27-8/Documentation/arm/Porting 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/arm/Porting 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/Documentation/arm/mem_alignment kernel-source-2.4.27-8-arm-1/Documentation/arm/mem_alignment
+--- kernel-source-2.4.27-8/Documentation/arm/mem_alignment 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/arm/mem_alignment 2005-02-18 17:48:34.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/Documentation/arm/memory.txt kernel-source-2.4.27-8-arm-1/Documentation/arm/memory.txt
+--- kernel-source-2.4.27-8/Documentation/arm/memory.txt 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/arm/memory.txt 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,74 @@
++ Kernel Memory Layout on ARM Linux
++
++ Russell King <rmk at arm.linux.org.uk>
++ 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 kernel-source-2.4.27-8/Documentation/cpufreq/core.txt kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/core.txt
+--- kernel-source-2.4.27-8/Documentation/cpufreq/core.txt 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/core.txt 2005-02-18 17:48:34.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 <linux at brodo.de>
++ David Kimdon <dwhedon at debian.org>
++
++
++
++ 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 kernel-source-2.4.27-8/Documentation/cpufreq/cpu-drivers.txt kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/cpu-drivers.txt
+--- kernel-source-2.4.27-8/Documentation/cpufreq/cpu-drivers.txt 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/cpu-drivers.txt 2005-02-18 17:48:34.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 <linux at brodo.de>
++
++
++
++ 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 kernel-source-2.4.27-8/Documentation/cpufreq/governors.txt kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/governors.txt
+--- kernel-source-2.4.27-8/Documentation/cpufreq/governors.txt 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/governors.txt 2005-02-18 17:48:34.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 <linux at brodo.de>
++
++
++
++ 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 kernel-source-2.4.27-8/Documentation/cpufreq/index.txt kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/index.txt
+--- kernel-source-2.4.27-8/Documentation/cpufreq/index.txt 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/index.txt 2005-02-18 17:48:34.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 <linux at brodo.de>
++
++
++
++ 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 at 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 kernel-source-2.4.27-8/Documentation/cpufreq/user-guide.txt kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/user-guide.txt
+--- kernel-source-2.4.27-8/Documentation/cpufreq/user-guide.txt 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/cpufreq/user-guide.txt 2005-02-18 17:48:34.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 <linux at brodo.de>
++
++
++
++ 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 kernel-source-2.4.27-8/Documentation/cpufreq-old kernel-source-2.4.27-8-arm-1/Documentation/cpufreq-old
+--- kernel-source-2.4.27-8/Documentation/cpufreq-old 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/cpufreq-old 2005-02-18 17:48:34.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 <devel at brodo.de>
++
++
++
++ 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 at notebook:# cat /proc/sys/cpu/0/speed-min > /proc/sys/cpu/0/speed
++root at notebook:# cat /proc/sys/cpu/0/speed-max > /proc/sys/cpu/0/speed
++
++
++2.1 Sample script for command line interface
++**********************************************
++
++
++Michael Ossmann <mike at ossmann.com> 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 at 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 kernel-source-2.4.27-8/Documentation/l3/structure kernel-source-2.4.27-8-arm-1/Documentation/l3/structure
+--- kernel-source-2.4.27-8/Documentation/l3/structure 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/l3/structure 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/Documentation/serial/driver kernel-source-2.4.27-8-arm-1/Documentation/serial/driver
+--- kernel-source-2.4.27-8/Documentation/serial/driver 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/Documentation/serial/driver 2005-02-18 17:48:34.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
++<rmk at arm.linux.org.uk>
++
++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 <asm/ioctl.h>
++
++
++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 kernel-source-2.4.27-8/MAINTAINERS kernel-source-2.4.27-8-arm-1/MAINTAINERS
+--- kernel-source-2.4.27-8/MAINTAINERS 2005-01-19 09:57:53.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/MAINTAINERS 2005-02-18 17:48:34.000000000 +0000
+@@ -262,6 +262,8 @@
+ ARM/STRONGARM110 PORT
+ P: Russell King
+ M: rmk at arm.linux.org.uk
++P: Vincent Sanders
++M: vince at arm.linux.org.uk
+ L: linux-arm-kernel at lists.arm.linux.org.uk
+ W: http://www.arm.linux.org.uk/
+ S: Maintained
+diff -urN kernel-source-2.4.27-8/Makefile kernel-source-2.4.27-8-arm-1/Makefile
+--- kernel-source-2.4.27-8/Makefile 2005-01-19 09:57:51.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/Makefile 2005-02-18 17:48:34.000000000 +0000
+@@ -144,7 +144,9 @@
+
+ DRIVERS-$(CONFIG_ACPI_BOOT) += drivers/acpi/acpi.o
+ DRIVERS-$(CONFIG_PARPORT) += drivers/parport/driver.o
+-DRIVERS-y += drivers/char/char.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
+@@ -168,6 +170,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
+@@ -201,6 +204,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)
+
+@@ -281,11 +286,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 kernel-source-2.4.27-8/Rules.make kernel-source-2.4.27-8-arm-1/Rules.make
+--- kernel-source-2.4.27-8/Rules.make 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/Rules.make 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/alpha/config.in kernel-source-2.4.27-8-arm-1/arch/alpha/config.in
+--- kernel-source-2.4.27-8/arch/alpha/config.in 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/alpha/config.in 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/Makefile kernel-source-2.4.27-8-arm-1/arch/arm/Makefile
+--- kernel-source-2.4.27-8/arch/arm/Makefile 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/Makefile 2005-02-18 17:48:34.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
+@@ -164,6 +164,10 @@
+ MACHINE = omaha
+ endif
+
++ifeq ($(CONFIG_ARCH_BAST),y)
++MACHINE = bast
++endif
++
+ export MACHINE PROCESSOR TEXTADDR GZFLAGS CFLAGS_BOOT OBJCOPYFLAGS
+
+ # Only set INCDIR if its not already defined above
+diff -urN kernel-source-2.4.27-8/arch/arm/boot/Makefile kernel-source-2.4.27-8-arm-1/arch/arm/boot/Makefile
+--- kernel-source-2.4.27-8/arch/arm/boot/Makefile 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/boot/Makefile 2005-02-18 17:48:34.000000000 +0000
+@@ -86,6 +86,11 @@
+ PARAMS_PHYS = 0xc0000100
+ endif
+
++ifeq ($(CONFIG_ARCH_BAST),y)
++ZRELADDR = 0x30008000
++PARAMS_PHYS = 0x30000100
++endif
++
+ # Should probably have some agreement on these...
+ ifeq ($(CONFIG_ARCH_P720T),y)
+ INITRD_PHYS = 0xc0400000
+diff -urN kernel-source-2.4.27-8/arch/arm/boot/compressed/head.S kernel-source-2.4.27-8-arm-1/arch/arm/boot/compressed/head.S
+--- kernel-source-2.4.27-8/arch/arm/boot/compressed/head.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/boot/compressed/head.S 2005-02-18 17:48:34.000000000 +0000
+@@ -40,6 +40,22 @@
+ .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_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
+@@ -47,6 +63,13 @@
+ .macro writeb, rb
+ strb \rb, [r3, #0]
+ .endm
++#elif defined(CONFIG_ARCH_BAST)
++ .macro loadsp, rb
++ mov \rb, #0x50000000
++ .endm
++ .macro writeb, rb
++ str \rb, [ r3, #0x20 ]
++ .endm
+ #elif defined(CONFIG_ARCH_AT91RM9200)
+ .macro loadsp, rb
+ mov \rb, #0xFF000000 @ BASE_DBGU (we cannot use ldr \reg, =AT91_DBGU_BASE)
+@@ -396,6 +419,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 +517,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 +527,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 kernel-source-2.4.27-8/arch/arm/boot/compressed/vmlinux.lds kernel-source-2.4.27-8-arm-1/arch/arm/boot/compressed/vmlinux.lds
+--- kernel-source-2.4.27-8/arch/arm/boot/compressed/vmlinux.lds 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/boot/compressed/vmlinux.lds 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,60 @@
++/*
++ * linux/arch/arm/boot/compressed/vmlinux.lds.in
++ *
++ * Copyright (C) 2000 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.
++ */
++OUTPUT_ARCH(arm)
++ENTRY(_start)
++SECTIONS
++{
++ . = 0x30008000;
++ _load_addr = .;
++
++ . = 0;
++ _text = .;
++
++ .text : {
++ _start = .;
++ *(.start)
++ *(.text)
++ *(.fixup)
++ *(.gnu.warning)
++ *(.rodata)
++ *(.rodata.*)
++ *(.glue_7)
++ *(.glue_7t)
++ input_data = .;
++ piggy.o
++ input_data_end = .;
++ . = ALIGN(4);
++ }
++
++ _etext = .;
++
++ _got_start = .;
++ .got : { *(.got) }
++ _got_end = .;
++ .got.plt : { *(.got.plt) }
++ .data : { *(.data) }
++ _edata = .;
++
++ . = ALIGN(4);
++ __bss_start = .;
++ .bss : { *(.bss) }
++ _end = .;
++
++ .stack (NOLOAD) : { *(.stack) }
++
++ .stab 0 : { *(.stab) }
++ .stabstr 0 : { *(.stabstr) }
++ .stab.excl 0 : { *(.stab.excl) }
++ .stab.exclstr 0 : { *(.stab.exclstr) }
++ .stab.index 0 : { *(.stab.index) }
++ .stab.indexstr 0 : { *(.stab.indexstr) }
++ .comment 0 : { *(.comment) }
++}
++
+diff -urN kernel-source-2.4.27-8/arch/arm/config.in kernel-source-2.4.27-8-arm-1/arch/arm/config.in
+--- kernel-source-2.4.27-8/arch/arm/config.in 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/arm/config.in 2005-02-18 17:48:34.000000000 +0000
+@@ -49,6 +49,7 @@
+ RiscStation CONFIG_ARCH_RISCSTATION \
+ SA1100-based CONFIG_ARCH_SA1100 \
+ Shark CONFIG_ARCH_SHARK \
++ BAST CONFIG_ARCH_BAST \
+ AT91RM9200-based CONFIG_ARCH_AT91RM9200 " RiscPC
+
+ mainmenu_option next_comment
+@@ -62,6 +63,11 @@
+ endmenu
+
+ mainmenu_option next_comment
++comment 'BAST Implementations'
++dep_bool ' VR1000' CONFIG_MACH_VR1000 $CONFIG_ARCH_BAST
++endmenu
++
++mainmenu_option next_comment
+ comment 'Footbridge Implementations'
+ dep_bool ' CATS' CONFIG_ARCH_CATS $CONFIG_ARCH_FOOTBRIDGE
+ dep_bool ' Compaq Personal Server' CONFIG_ARCH_PERSONAL_SERVER $CONFIG_ARCH_FOOTBRIDGE
+@@ -144,6 +150,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 +196,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 \
+@@ -311,30 +324,55 @@
+ fi
+ fi
+
++# for bast, we just set ARM920T and ensure that CONFIG_CPU_S3C2410X is set
++if [ "$CONFIG_ARCH_BAST" = "y" ]; then
++ define_bool CONFIG_CPU_ARM920T y
++ define_bool CONFIG_CPU_S3C2410X y
++else
++ define_bool CONFIG_CPU_ARM920T n
++ define_bool CONFIG_CPU_S3C2410X n
++fi
++
+
+ # ARM922T
+ if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then
+ define_bool CONFIG_CPU_ARM922T y
+- define_bool CONFIG_PLD y
+ else
++ if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
++ bool 'Support ARM922T(Excalibur) processor' CONFIG_ARM922T
++ else
+ define_bool CONFIG_CPU_ARM922T n
+- define_bool CONFIG_PLD 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
+@@ -378,7 +416,8 @@
+ "$CONFIG_ARCH_INTEGRATOR" = "y" -o "$CONFIG_ARCH_SA1100" = "y" -o \
+ "$CONFIG_ARCH_L7200" = "y" -o "$CONFIG_ARCH_ANAKIN" = "y" -o \
+ "$CONFIG_ARCH_CAMELOT" = "y" -o "$CONFIG_ARCH_MX1ADS" = "y" -o \
+- "$CONFIG_ARCH_OMAHA" = "y" -o "$CONFIG_ARCH_AT91RM9200" = "y" ]; then
++ "$CONFIG_ARCH_OMAHA" = "y" -o "$CONFIG_ARCH_AT91RM9200" = "y" -o \
++ "$CONFIG_ARCH_BAST" = "y" ]; then
+ define_bool CONFIG_CPU_32v4 y
+ else
+ define_bool CONFIG_CPU_32v4 n
+@@ -388,25 +427,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
+
+@@ -446,6 +489,7 @@
+ "$CONFIG_ARCH_EBSA110" = "y" -o \
+ "$CONFIG_ARCH_CDB89712" = "y" -o \
+ "$CONFIG_ARCH_EDB7211" = "y" -o \
++ "$CONFIG_ARCH_BAST" = "y" -o \
+ "$CONFIG_ARCH_SA1100" = "y" ]; then
+ define_bool CONFIG_ISA y
+ else
+@@ -491,7 +535,7 @@
+ if [ "$CONFIG_FPE_NWFPE" != "n" ]; then
+ bool ' Support extended precision' CONFIG_FPE_NWFPE_XP
+ fi
+-if [ "$CONFIG_CPU_26" = "n" -a "$CONFIG_CPU_32v3" = "n" ]; then
++if [ "$CONFIG_CPU_26" = "n" ]; then
+ dep_tristate 'FastFPE math emulation (EXPERIMENTAL)' CONFIG_FPE_FASTFPE $CONFIG_EXPERIMENTAL
+ fi
+ choice 'Kernel core (/proc/kcore) format' \
+@@ -514,7 +558,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 +569,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
+@@ -542,6 +588,12 @@
+ define_bool CONFIG_ALIGNMENT_TRAP n
+ fi
+ fi
++
++if [ "$CONFIG_CPU_S3C2410X" = "y" ]; then
++ bool 'S3C2410 DMA /proc support' CONFIG_S3C2410_DMA_PROC
++ bool 'S3C2410 DMA Debugging' CONFIG_S3C2410_DMA_DEBUG
++fi
++
+ endmenu
+
+ source drivers/parport/Config.in
+@@ -678,6 +730,7 @@
+ "$CONFIG_ARCH_TBOX" = "y" -o \
+ "$CONFIG_ARCH_SHARK" = "y" -o \
+ "$CONFIG_ARCH_SA1100" = "y" -o \
++ "$CONFIG_ARCH_BAST" = "y" -o \
+ "$CONFIG_PCI" = "y" ]; then
+ mainmenu_option next_comment
+ comment 'Sound'
+@@ -729,10 +782,9 @@
+ 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
+-
++dep_bool ' Kernel low-level debugging messages via SC2410X UART0' CONFIG_DEBUG_S3C2410X_UART0 $CONFIG_DEBUG_KERNEL $CONFIG_ARCH_BAST
++dep_bool ' Kernel low-level debugging messages via VR1000 Serial3' CONFIG_DEBUG_VR1000_SER3 $CONFIG_DEBUG_KERNEL $CONFIG_MACH_VR1000
+ endmenu
+
+-source crypto/Config.in
+ source lib/Config.in
++
+diff -urN kernel-source-2.4.27-8/arch/arm/def-configs/at91rm9200dk kernel-source-2.4.27-8-arm-1/arch/arm/def-configs/at91rm9200dk
+--- kernel-source-2.4.27-8/arch/arm/def-configs/at91rm9200dk 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/arm/def-configs/at91rm9200dk 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/def-configs/bast kernel-source-2.4.27-8-arm-1/arch/arm/def-configs/bast
+--- kernel-source-2.4.27-8/arch/arm/def-configs/bast 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/def-configs/bast 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,1237 @@
++#
++# Automatically generated make config: 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 is not set
++# CONFIG_MODVERSIONS is not set
++# CONFIG_KMOD is not set
++
++#
++# 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_BAST=y
++# CONFIG_ARCH_AT91RM9200 is not set
++
++#
++# Archimedes/A5000 Implementations
++#
++
++#
++# Archimedes/A5000 Implementations (select only ONE)
++#
++# CONFIG_ARCH_ARC is not set
++# CONFIG_ARCH_A5K is not set
++
++#
++# BAST Implementations
++#
++# CONFIG_MACH_VR1000 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 is not set
++
++#
++# 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
++
++#
++# Processor Type
++#
++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 is not set
++CONFIG_CPU_ARM920T=y
++CONFIG_CPU_S3C2410X=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
++
++#
++# Processor Features
++#
++# 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=y
++# 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
++
++#
++# At least one math emulation must be selected
++#
++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=y
++CONFIG_BINFMT_ELF=y
++CONFIG_BINFMT_MISC=y
++# CONFIG_PM is not set
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="root=/dev/hda1 ro"
++CONFIG_ALIGNMENT_TRAP=y
++CONFIG_S3C2410_DMA_PROC=y
++# CONFIG_S3C2410_DMA_DEBUG is not set
++
++#
++# Parallel port support
++#
++CONFIG_PARPORT=y
++CONFIG_PARPORT_PC=y
++# CONFIG_PARPORT_PC_FIFO is not set
++CONFIG_PARPORT_PC_SUPERIO=y
++# CONFIG_PARPORT_PC_PCMCIA is not set
++# CONFIG_PARPORT_ARC is not set
++# CONFIG_PARPORT_IDP is not set
++# CONFIG_PARPORT_AMIGA is not set
++# CONFIG_PARPORT_MFC3 is not set
++# CONFIG_PARPORT_ATARI is not set
++# CONFIG_PARPORT_GSC is not set
++# CONFIG_PARPORT_SUNBPP is not set
++# CONFIG_PARPORT_IP22 is not set
++CONFIG_PARPORT_OTHER=y
++CONFIG_PARPORT_1284=y
++
++#
++# Memory Technology Devices (MTD)
++#
++CONFIG_MTD=y
++CONFIG_MTD_DEBUG=y
++CONFIG_MTD_DEBUG_VERBOSE=1
++CONFIG_MTD_PARTITIONS=y
++CONFIG_MTD_CONCAT=y
++CONFIG_MTD_REDBOOT_PARTS=y
++# CONFIG_MTD_CMDLINE_PARTS is not set
++CONFIG_MTD_AFS_PARTS=y
++
++#
++# User Modules And Translation Layers
++#
++CONFIG_MTD_CHAR=y
++CONFIG_MTD_BLOCK=y
++# CONFIG_FTL is not set
++CONFIG_NFTL=y
++CONFIG_NFTL_RW=y
++
++#
++# 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=y
++# CONFIG_MTD_CFI_STAA is not set
++CONFIG_MTD_RAM=y
++CONFIG_MTD_ROM=y
++CONFIG_MTD_ABSENT=y
++# 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 is not set
++# 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_BAST=y
++# CONFIG_MTD_VR1000RAM 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_MTDRAM is not set
++# CONFIG_MTD_BLKMTD is not set
++
++#
++# Disk-On-Chip Device Drivers
++#
++# CONFIG_MTD_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++CONFIG_MTD_NAND=y
++CONFIG_MTD_NAND_VERIFY_WRITE=y
++CONFIG_MTD_NAND_S3C2410=y
++CONFIG_MTD_NAND_BAST=y
++CONFIG_MTD_NAND_IDS=y
++
++#
++# 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=y
++CONFIG_BLK_DEV_NBD=y
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=4096
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_BLK_STATS=y
++
++#
++# 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=y
++CONFIG_NETLINK_DEV=y
++CONFIG_NETFILTER=y
++CONFIG_NETFILTER_DEBUG=y
++CONFIG_FILTER=y
++CONFIG_UNIX=y
++CONFIG_INET=y
++CONFIG_IP_MULTICAST=y
++# 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=y
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# CONFIG_IP_MROUTE is not set
++# CONFIG_ARPD is not set
++# CONFIG_INET_ECN is not set
++# CONFIG_SYN_COOKIES is not set
++
++#
++# IP: Netfilter Configuration
++#
++# CONFIG_IP_NF_CONNTRACK is not set
++# CONFIG_IP_NF_QUEUE is not set
++# CONFIG_IP_NF_IPTABLES is not set
++# CONFIG_IP_NF_ARPTABLES is not set
++# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
++# CONFIG_IP_NF_COMPAT_IPFWADM is not set
++
++#
++# IP: Virtual Server Configuration
++#
++# CONFIG_IP_VS is not set
++# CONFIG_IPV6 is not set
++# CONFIG_KHTTPD is not set
++
++#
++# SCTP Configuration (EXPERIMENTAL)
++#
++# 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_NE2K_BAST=y
++CONFIG_DM9000_BAST=y
++# 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_AT1700 is not set
++# CONFIG_DEPCA is not set
++# CONFIG_HP100 is not set
++CONFIG_NET_ISA=y
++# CONFIG_E2100 is not set
++# CONFIG_EWRK3 is not set
++# CONFIG_EEXPRESS is not set
++# CONFIG_EEXPRESS_PRO is not set
++# CONFIG_HPLAN_PLUS is not set
++# CONFIG_HPLAN is not set
++# CONFIG_LP486E is not set
++# CONFIG_ETH16I is not set
++CONFIG_NE2000=y
++# 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=y
++CONFIG_SLIP_COMPRESSED=y
++CONFIG_SLIP_SMART=y
++CONFIG_SLIP_MODE_SLIP6=y
++
++#
++# 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=y
++
++#
++# IrDA protocols
++#
++CONFIG_IRLAN=y
++# CONFIG_IRNET is not set
++CONFIG_IRCOMM=y
++CONFIG_IRDA_ULTRA=y
++
++#
++# IrDA options
++#
++CONFIG_IRDA_CACHE_LAST_LSAP=y
++CONFIG_IRDA_FAST_RR=y
++CONFIG_IRDA_DEBUG=y
++
++#
++# Infrared-port device drivers
++#
++
++#
++# SIR device drivers
++#
++# CONFIG_IRTTY_SIR is not set
++# CONFIG_IRPORT_SIR is not set
++
++#
++# Dongle support
++#
++# CONFIG_DONGLE is not set
++
++#
++# FIR device drivers
++#
++# CONFIG_USB_IRDA is not set
++CONFIG_NSC_FIR=y
++# CONFIG_WINBOND_FIR is not set
++# CONFIG_TOSHIBA_OLD is not set
++# CONFIG_TOSHIBA_FIR is not set
++# CONFIG_SMC_IRCC_FIR is not set
++# CONFIG_ALI_FIR is not set
++# CONFIG_VLSI_FIR is not set
++# CONFIG_VIA_IRCC_FIR is not set
++
++#
++# ATA/ATAPI/MFM/RLL support
++#
++CONFIG_IDE=y
++
++#
++# IDE, ATA and ATAPI Block devices
++#
++CONFIG_BLK_DEV_IDE=y
++
++#
++# Please see Documentation/ide.txt for help/info on IDE drives
++#
++# CONFIG_BLK_DEV_HD_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++CONFIG_BLK_DEV_IDEDISK=y
++# CONFIG_IDEDISK_MULTI_MODE is not set
++# CONFIG_IDEDISK_STROKE is not set
++# CONFIG_BLK_DEV_IDECS is not set
++CONFIG_BLK_DEV_IDECD=y
++# CONFIG_BLK_DEV_IDETAPE is not set
++# CONFIG_BLK_DEV_IDEFLOPPY is not set
++# CONFIG_BLK_DEV_IDESCSI is not set
++# CONFIG_IDE_TASK_IOCTL is not set
++
++#
++# IDE chipset support/bugfixes
++#
++# CONFIG_BLK_DEV_CMD640 is not set
++# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
++# CONFIG_BLK_DEV_ISAPNP is not set
++CONFIG_BLK_DEV_IDE_BAST=y
++# CONFIG_IDE_CHIPSETS is not set
++# CONFIG_IDEDMA_AUTO is not set
++# CONFIG_DMA_NONPCI is not set
++# CONFIG_BLK_DEV_ATARAID is not set
++# CONFIG_BLK_DEV_ATARAID_PDC is not set
++# CONFIG_BLK_DEV_ATARAID_HPT is not set
++# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set
++# CONFIG_BLK_DEV_ATARAID_SII 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=y
++CONFIG_INPUT_KEYBDEV=y
++CONFIG_INPUT_MOUSEDEV=y
++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
++CONFIG_INPUT_JOYDEV=y
++CONFIG_INPUT_EVDEV=y
++# CONFIG_INPUT_UINPUT is not set
++# CONFIG_INPUT_MX1TS is not set
++
++#
++# Character devices
++#
++CONFIG_VT=y
++CONFIG_VT_CONSOLE=y
++# CONFIG_SERIAL is not set
++# CONFIG_SERIAL_EXTENDED is not set
++# CONFIG_SERIAL_NONSTANDARD is not set
++CONFIG_DUMMY_KEYB=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 is not set
++# CONFIG_SERIAL_AT91_CONSOLE is not set
++CONFIG_SERIAL_S3C2410X=y
++CONFIG_SERIAL_S3C2410X_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_BAST_16550 is not set
++CONFIG_SERIAL_CORE=y
++CONFIG_SERIAL_CORE_CONSOLE=y
++CONFIG_UNIX98_PTYS=y
++CONFIG_UNIX98_PTY_COUNT=256
++CONFIG_PRINTER=y
++# CONFIG_LP_CONSOLE is not set
++CONFIG_PPDEV=y
++# CONFIG_TIPAR is not set
++
++#
++# I2C support
++#
++CONFIG_I2C=y
++CONFIG_I2C_ALGOBIT=y
++CONFIG_I2C_PHILIPSPAR=y
++# CONFIG_I2C_ELV is not set
++# CONFIG_I2C_VELLEMAN is not set
++# CONFIG_SCx200_I2C is not set
++# CONFIG_I2C_GUIDE is not set
++CONFIG_I2C_S3C2410BIT=y
++# CONFIG_SCx200_ACB is not set
++# CONFIG_I2C_ALGOPCF is not set
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_PROC=y
++# CONFIG_I2C_DS1307 is not set
++# CONFIG_I2C_M41ST87 is not set
++
++#
++# L3 serial bus support
++#
++# CONFIG_L3 is not set
++# CONFIG_L3_ALGOBIT is not set
++# CONFIG_L3_BIT_SA1100_GPIO is not set
++
++#
++# Other L3 adapters
++#
++# CONFIG_L3_SA1111 is not set
++# CONFIG_BIT_SA1100_GPIO is not set
++
++#
++# Mice
++#
++# CONFIG_BUSMOUSE is not set
++CONFIG_MOUSE=y
++CONFIG_PSMOUSE=y
++# CONFIG_82C710_MOUSE is not set
++# CONFIG_PC110_PAD is not set
++# CONFIG_MK712_MOUSE is not set
++
++#
++# Joysticks
++#
++# CONFIG_INPUT_GAMEPORT is not set
++# CONFIG_INPUT_NS558 is not set
++# CONFIG_INPUT_LIGHTNING is not set
++# CONFIG_INPUT_PCIGAME is not set
++# CONFIG_INPUT_CS461X is not set
++# CONFIG_INPUT_EMU10K1 is not set
++# CONFIG_INPUT_SERIO is not set
++# CONFIG_INPUT_SERPORT is not set
++
++#
++# Joysticks
++#
++# CONFIG_INPUT_ANALOG is not set
++# CONFIG_INPUT_A3D is not set
++# CONFIG_INPUT_ADI is not set
++# CONFIG_INPUT_COBRA is not set
++# CONFIG_INPUT_GF2K is not set
++# CONFIG_INPUT_GRIP is not set
++# CONFIG_INPUT_INTERACT is not set
++# CONFIG_INPUT_TMDC is not set
++# CONFIG_INPUT_SIDEWINDER is not set
++# CONFIG_INPUT_IFORCE_USB is not set
++# CONFIG_INPUT_IFORCE_232 is not set
++# CONFIG_INPUT_WARRIOR is not set
++# CONFIG_INPUT_MAGELLAN is not set
++# CONFIG_INPUT_SPACEORB is not set
++# CONFIG_INPUT_SPACEBALL is not set
++# CONFIG_INPUT_STINGER is not set
++# CONFIG_INPUT_DB9 is not set
++# CONFIG_INPUT_GAMECON is not set
++# CONFIG_INPUT_TURBOGRAFX 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 is not set
++# 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_S3C2410_WATCHDOG=y
++# CONFIG_AT91_WATCHDOG is not set
++# 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_BAST_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=y
++# 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=y
++CONFIG_JBD=y
++# CONFIG_JBD_DEBUG is not set
++CONFIG_FAT_FS=y
++CONFIG_MSDOS_FS=y
++# CONFIG_UMSDOS_FS is not set
++CONFIG_VFAT_FS=y
++# CONFIG_EFS_FS is not set
++CONFIG_JFFS_FS=y
++CONFIG_JFFS_FS_VERBOSE=0
++CONFIG_JFFS_PROC_FS=y
++CONFIG_JFFS2_FS=y
++CONFIG_JFFS2_FS_DEBUG=0
++CONFIG_CRAMFS=y
++# CONFIG_TMPFS is not set
++CONFIG_RAMFS=y
++CONFIG_ISO9660_FS=y
++CONFIG_JOLIET=y
++CONFIG_ZISOFS=y
++# 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 is not set
++# CONFIG_DEVFS_MOUNT is not set
++# CONFIG_DEVFS_DEBUG is not set
++CONFIG_DEVPTS_FS=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++CONFIG_ROMFS_FS=y
++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=y
++
++#
++# Partition Types
++#
++CONFIG_PARTITION_ADVANCED=y
++# CONFIG_ACORN_PARTITION is not set
++# CONFIG_OSF_PARTITION is not set
++# CONFIG_AMIGA_PARTITION is not set
++# CONFIG_ATARI_PARTITION is not set
++# CONFIG_MAC_PARTITION is not set
++CONFIG_MSDOS_PARTITION=y
++CONFIG_BSD_DISKLABEL=y
++CONFIG_MINIX_SUBPARTITION=y
++CONFIG_SOLARIS_X86_PARTITION=y
++CONFIG_UNIXWARE_DISKLABEL=y
++# CONFIG_LDM_PARTITION is not set
++# CONFIG_SGI_PARTITION is not set
++# CONFIG_ULTRIX_PARTITION is not set
++# CONFIG_SUN_PARTITION is not set
++# CONFIG_EFI_PARTITION is not set
++# CONFIG_SMB_NLS is not set
++CONFIG_NLS=y
++
++#
++# Native Language Support
++#
++CONFIG_NLS_DEFAULT="iso8859-1"
++CONFIG_NLS_CODEPAGE_437=y
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++CONFIG_NLS_CODEPAGE_850=y
++CONFIG_NLS_CODEPAGE_852=y
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++# CONFIG_NLS_ISO8859_1 is not set
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# Console drivers
++#
++# CONFIG_VGA_CONSOLE is not set
++
++#
++# Frame-buffer support
++#
++CONFIG_FB=y
++CONFIG_DUMMY_CONSOLE=y
++# CONFIG_FB_ACORN is not set
++# CONFIG_FB_ANAKIN is not set
++# CONFIG_FB_CLPS711X is not set
++# CONFIG_FB_SA1100 is not set
++CONFIG_FB_S3C2410=y
++# CONFIG_FB_DBMX1 is not set
++# CONFIG_FB_CYBER2000 is not set
++# CONFIG_FB_VIRTUAL is not set
++CONFIG_FBCON_ADVANCED=y
++CONFIG_FBCON_MFB=y
++# CONFIG_FBCON_CFB2 is not set
++CONFIG_FBCON_CFB4=y
++CONFIG_FBCON_CFB8=y
++# CONFIG_FBCON_CFB16 is not set
++# CONFIG_FBCON_CFB24 is not set
++# CONFIG_FBCON_CFB32 is not set
++# CONFIG_FBCON_AFB is not set
++# CONFIG_FBCON_ILBM is not set
++# CONFIG_FBCON_IPLAN2P2 is not set
++# CONFIG_FBCON_IPLAN2P4 is not set
++# CONFIG_FBCON_IPLAN2P8 is not set
++# CONFIG_FBCON_MAC is not set
++# CONFIG_FBCON_VGA_PLANES is not set
++# CONFIG_FBCON_VGA is not set
++# CONFIG_FBCON_HGA is not set
++# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
++CONFIG_FBCON_FONTS=y
++CONFIG_FONT_8x8=y
++CONFIG_FONT_8x16=y
++# CONFIG_FONT_SUN8x16 is not set
++# CONFIG_FONT_SUN12x22 is not set
++# CONFIG_FONT_6x11 is not set
++# CONFIG_FONT_PEARL_8x8 is not set
++CONFIG_FONT_ACORN_8x8=y
++
++#
++# Sound
++#
++CONFIG_SOUND=y
++# CONFIG_SOUND_ALI5455 is not set
++# CONFIG_SOUND_BT878 is not set
++# CONFIG_SOUND_CMPCI is not set
++# CONFIG_SOUND_EMU10K1 is not set
++# CONFIG_MIDI_EMU10K1 is not set
++# CONFIG_SOUND_FUSION is not set
++# CONFIG_SOUND_CS4281 is not set
++# CONFIG_SOUND_ES1370 is not set
++# CONFIG_SOUND_ES1371 is not set
++# CONFIG_SOUND_ESSSOLO1 is not set
++# CONFIG_SOUND_MAESTRO is not set
++# CONFIG_SOUND_MAESTRO3 is not set
++# CONFIG_SOUND_FORTE is not set
++# CONFIG_SOUND_ICH is not set
++# CONFIG_SOUND_RME96XX is not set
++# CONFIG_SOUND_SONICVIBES is not set
++# CONFIG_SOUND_TRIDENT is not set
++# CONFIG_SOUND_MSNDCLAS is not set
++# CONFIG_SOUND_MSNDPIN is not set
++# CONFIG_SOUND_VIA82CXXX is not set
++# CONFIG_MIDI_VIA82CXXX is not set
++CONFIG_SOUND_S3C2410=y
++CONFIG_SOUND_S3C2410_TLV320AIC23=y
++# CONFIG_SOUND_OSS is not set
++# CONFIG_SOUND_VIDC is not set
++# CONFIG_SOUND_WAVEARTIST is not set
++# CONFIG_SOUND_TVMIXER is not set
++# CONFIG_SOUND_AD1980 is not set
++# CONFIG_SOUND_WM97XX 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=y
++CONFIG_USB_DEBUG=y
++
++#
++# Miscellaneous USB options
++#
++CONFIG_USB_DEVICEFS=y
++# CONFIG_USB_BANDWIDTH is not set
++
++#
++# USB Host Controller Drivers
++#
++# CONFIG_USB_EHCI_HCD is not set
++# CONFIG_USB_UHCI is not set
++# CONFIG_USB_UHCI_ALT is not set
++CONFIG_USB_OHCI=y
++# CONFIG_USB_OHCI_SA1111 is not set
++CONFIG_USB_OHCI_S3C2410=y
++# CONFIG_USB_SL811HS_ALT is not set
++# CONFIG_USB_SL811HS is not set
++
++#
++# USB Device Class drivers
++#
++# CONFIG_USB_AUDIO is not set
++# CONFIG_USB_EMI26 is not set
++# CONFIG_USB_BLUETOOTH is not set
++# CONFIG_USB_MIDI is not set
++
++#
++# SCSI support is needed for USB Storage
++#
++# CONFIG_USB_STORAGE is not set
++# CONFIG_USB_STORAGE_DEBUG is not set
++# CONFIG_USB_STORAGE_DATAFAB is not set
++# CONFIG_USB_STORAGE_FREECOM is not set
++# CONFIG_USB_STORAGE_ISD200 is not set
++# 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=y
++
++#
++# USB Human Interface Devices (HID)
++#
++CONFIG_USB_HID=y
++CONFIG_USB_HIDINPUT=y
++CONFIG_USB_HIDDEV=y
++# CONFIG_USB_AIPTEK is not set
++# CONFIG_USB_WACOM is not set
++# CONFIG_USB_KBTAB is not set
++# CONFIG_USB_POWERMATE is not set
++
++#
++# USB Imaging devices
++#
++# CONFIG_USB_DC2XX is not set
++# CONFIG_USB_MDC800 is not set
++# CONFIG_USB_SCANNER is not set
++# CONFIG_USB_MICROTEK is not set
++# CONFIG_USB_HPUSBSCSI is not set
++
++#
++# USB Multimedia devices
++#
++
++#
++# Video4Linux support is needed for USB Multimedia device support
++#
++
++#
++# USB Network adaptors
++#
++# CONFIG_USB_PEGASUS is not set
++# CONFIG_USB_RTL8150 is not set
++# CONFIG_USB_KAWETH is not set
++# CONFIG_USB_CATC is not set
++# CONFIG_USB_CDCETHER is not set
++CONFIG_USB_USBNET=y
++
++#
++# USB port drivers
++#
++# CONFIG_USB_USS720 is not set
++
++#
++# USB Serial Converter support
++#
++CONFIG_USB_SERIAL=y
++# CONFIG_USB_SERIAL_DEBUG 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=y
++# 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_EDGEPORT_TI is not set
++# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
++# CONFIG_USB_SERIAL_KEYSPAN is not set
++# CONFIG_USB_SERIAL_MCT_U232 is not set
++# CONFIG_USB_SERIAL_KLSI is not set
++# CONFIG_USB_SERIAL_KOBIL_SCT is not set
++CONFIG_USB_SERIAL_PL2303=y
++# CONFIG_USB_SERIAL_CYBERJACK is not set
++# CONFIG_USB_SERIAL_XIRCOM is not set
++# CONFIG_USB_SERIAL_OMNINET is not set
++
++#
++# USB Miscellaneous drivers
++#
++# 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
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++CONFIG_DEBUG_INFO=y
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++CONFIG_DEBUG_SLAB=y
++CONFIG_MAGIC_SYSRQ=y
++CONFIG_DEBUG_SPINLOCK=y
++CONFIG_DEBUG_WAITQ=y
++CONFIG_DEBUG_BUGVERBOSE=y
++CONFIG_DEBUG_ERRORS=y
++CONFIG_DEBUG_LL=y
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
++CONFIG_DEBUG_S3C2410X_UART0=y
++# CONFIG_DEBUG_VR1000_SER3 is not set
++
++#
++# Library routines
++#
++# CONFIG_CRC32 is not set
++CONFIG_ZLIB_INFLATE=y
++CONFIG_ZLIB_DEFLATE=y
+diff -urN kernel-source-2.4.27-8/arch/arm/def-configs/csb337 kernel-source-2.4.27-8-arm-1/arch/arm/def-configs/csb337
+--- kernel-source-2.4.27-8/arch/arm/def-configs/csb337 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/def-configs/csb337 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/def-configs/vr1000 kernel-source-2.4.27-8-arm-1/arch/arm/def-configs/vr1000
+--- kernel-source-2.4.27-8/arch/arm/def-configs/vr1000 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/def-configs/vr1000 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,1108 @@
++#
++# 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=y
++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_BAST=y
++# CONFIG_ARCH_AT91RM9200 is not set
++
++#
++# Archimedes/A5000 Implementations
++#
++# CONFIG_ARCH_ARC is not set
++# CONFIG_ARCH_A5K is not set
++
++#
++# BAST Implementations
++#
++CONFIG_MACH_VR1000=y
++
++#
++# 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 is not set
++
++#
++# 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 is not set
++CONFIG_CPU_ARM920T=y
++CONFIG_CPU_S3C2410X=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=y
++# 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=y
++CONFIG_BINFMT_ELF=y
++CONFIG_BINFMT_MISC=y
++# CONFIG_PM is not set
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="root=/dev/hdc2 ro console=ttySAC0,115200 init=/bin/bash"
++CONFIG_ALIGNMENT_TRAP=y
++
++#
++# Parallel port support
++#
++# CONFIG_PARPORT is not set
++
++#
++# Memory Technology Devices (MTD)
++#
++CONFIG_MTD=y
++CONFIG_MTD_DEBUG=y
++CONFIG_MTD_DEBUG_VERBOSE=1
++CONFIG_MTD_PARTITIONS=y
++CONFIG_MTD_CONCAT=y
++CONFIG_MTD_REDBOOT_PARTS=y
++CONFIG_MTD_CMDLINE_PARTS=y
++CONFIG_MTD_AFS_PARTS=y
++CONFIG_MTD_CHAR=y
++CONFIG_MTD_BLOCK=y
++# CONFIG_FTL is not set
++CONFIG_NFTL=y
++CONFIG_NFTL_RW=y
++
++#
++# 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=y
++# CONFIG_MTD_CFI_STAA is not set
++CONFIG_MTD_RAM=y
++CONFIG_MTD_ROM=y
++CONFIG_MTD_ABSENT=y
++CONFIG_MTD_OBSOLETE_CHIPS=y
++CONFIG_MTD_AMDSTD=y
++# CONFIG_MTD_SHARP is not set
++CONFIG_MTD_JEDEC=y
++
++#
++# Mapping drivers for chip access
++#
++# CONFIG_MTD_PHYSMAP is not set
++# 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_BAST=y
++CONFIG_MTD_VR1000RAM=y
++# 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_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=y
++CONFIG_MTD_NAND_S3C2410=y
++# CONFIG_MTD_NAND_BAST is not set
++CONFIG_MTD_NAND_IDS=y
++
++#
++# 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=y
++CONFIG_BLK_DEV_NBD=y
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=4096
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_BLK_STATS=y
++
++#
++# 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=m
++CONFIG_PACKET_MMAP=y
++CONFIG_NETLINK_DEV=y
++CONFIG_NETFILTER=y
++CONFIG_NETFILTER_DEBUG=y
++CONFIG_FILTER=y
++CONFIG_UNIX=y
++CONFIG_INET=y
++CONFIG_IP_MULTICAST=y
++# CONFIG_IP_ADVANCED_ROUTER is not set
++# CONFIG_IP_PNP is not set
++# CONFIG_NET_IPIP is not set
++CONFIG_NET_IPGRE=m
++# CONFIG_NET_IPGRE_BROADCAST is not set
++# CONFIG_IP_MROUTE is not set
++# CONFIG_ARPD is not set
++# CONFIG_INET_ECN is not set
++CONFIG_SYN_COOKIES=y
++
++#
++# IP: Netfilter Configuration
++#
++CONFIG_IP_NF_CONNTRACK=m
++CONFIG_IP_NF_FTP=m
++CONFIG_IP_NF_AMANDA=m
++CONFIG_IP_NF_TFTP=m
++CONFIG_IP_NF_IRC=m
++CONFIG_IP_NF_QUEUE=m
++CONFIG_IP_NF_IPTABLES=m
++CONFIG_IP_NF_MATCH_LIMIT=m
++CONFIG_IP_NF_MATCH_MAC=m
++CONFIG_IP_NF_MATCH_PKTTYPE=m
++CONFIG_IP_NF_MATCH_MARK=m
++CONFIG_IP_NF_MATCH_MULTIPORT=m
++CONFIG_IP_NF_MATCH_TOS=m
++CONFIG_IP_NF_MATCH_RECENT=m
++CONFIG_IP_NF_MATCH_ECN=m
++CONFIG_IP_NF_MATCH_DSCP=m
++CONFIG_IP_NF_MATCH_AH_ESP=m
++CONFIG_IP_NF_MATCH_LENGTH=m
++CONFIG_IP_NF_MATCH_TTL=m
++CONFIG_IP_NF_MATCH_TCPMSS=m
++CONFIG_IP_NF_MATCH_HELPER=m
++CONFIG_IP_NF_MATCH_STATE=m
++CONFIG_IP_NF_MATCH_CONNTRACK=m
++CONFIG_IP_NF_MATCH_UNCLEAN=m
++CONFIG_IP_NF_MATCH_OWNER=m
++CONFIG_IP_NF_FILTER=m
++CONFIG_IP_NF_TARGET_REJECT=m
++CONFIG_IP_NF_TARGET_MIRROR=m
++CONFIG_IP_NF_NAT=m
++CONFIG_IP_NF_NAT_NEEDED=y
++CONFIG_IP_NF_TARGET_MASQUERADE=m
++CONFIG_IP_NF_TARGET_REDIRECT=m
++CONFIG_IP_NF_NAT_AMANDA=m
++CONFIG_IP_NF_NAT_LOCAL=y
++CONFIG_IP_NF_NAT_SNMP_BASIC=m
++CONFIG_IP_NF_NAT_IRC=m
++CONFIG_IP_NF_NAT_FTP=m
++CONFIG_IP_NF_NAT_TFTP=m
++CONFIG_IP_NF_MANGLE=m
++CONFIG_IP_NF_TARGET_TOS=m
++CONFIG_IP_NF_TARGET_ECN=m
++CONFIG_IP_NF_TARGET_DSCP=m
++CONFIG_IP_NF_TARGET_MARK=m
++CONFIG_IP_NF_TARGET_LOG=m
++CONFIG_IP_NF_TARGET_ULOG=m
++CONFIG_IP_NF_TARGET_TCPMSS=m
++# CONFIG_IP_NF_ARPTABLES is not set
++# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
++# CONFIG_IP_NF_COMPAT_IPFWADM is not set
++
++#
++# IP: Virtual Server Configuration
++#
++# CONFIG_IP_VS is not set
++# CONFIG_IPV6 is not set
++# CONFIG_KHTTPD is not set
++
++#
++# SCTP Configuration (EXPERIMENTAL)
++#
++# 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_NE2K_BAST is not set
++CONFIG_DM9000_BAST=y
++# 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_AT1700 is not set
++# CONFIG_DEPCA is not set
++# CONFIG_HP100 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=m
++CONFIG_PPP_MULTILINK=y
++CONFIG_PPP_FILTER=y
++CONFIG_PPP_ASYNC=m
++# CONFIG_PPP_SYNC_TTY is not set
++CONFIG_PPP_DEFLATE=m
++CONFIG_PPP_BSDCOMP=m
++# CONFIG_PPPOE 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=y
++CONFIG_IRLAN=y
++# CONFIG_IRNET is not set
++CONFIG_IRCOMM=y
++CONFIG_IRDA_ULTRA=y
++CONFIG_IRDA_CACHE_LAST_LSAP=y
++CONFIG_IRDA_FAST_RR=y
++CONFIG_IRDA_DEBUG=y
++
++#
++# Infrared-port device drivers
++#
++# CONFIG_IRTTY_SIR is not set
++# CONFIG_IRPORT_SIR is not set
++# CONFIG_DONGLE is not set
++# CONFIG_USB_IRDA is not set
++CONFIG_NSC_FIR=y
++# CONFIG_WINBOND_FIR is not set
++# CONFIG_TOSHIBA_OLD is not set
++# CONFIG_TOSHIBA_FIR is not set
++# CONFIG_SMC_IRCC_FIR is not set
++# CONFIG_ALI_FIR is not set
++# CONFIG_VLSI_FIR is not set
++# CONFIG_VIA_IRCC_FIR is not set
++
++#
++# ATA/ATAPI/MFM/RLL support
++#
++CONFIG_IDE=y
++
++#
++# IDE, ATA and ATAPI Block devices
++#
++CONFIG_BLK_DEV_IDE=y
++# CONFIG_BLK_DEV_HD_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++CONFIG_BLK_DEV_IDEDISK=y
++# CONFIG_IDEDISK_MULTI_MODE is not set
++# CONFIG_IDEDISK_STROKE is not set
++# CONFIG_BLK_DEV_IDECS is not set
++CONFIG_BLK_DEV_IDECD=y
++# CONFIG_BLK_DEV_IDETAPE is not set
++# CONFIG_BLK_DEV_IDEFLOPPY is not set
++# CONFIG_BLK_DEV_IDESCSI is not set
++# CONFIG_IDE_TASK_IOCTL is not set
++# CONFIG_BLK_DEV_CMD640 is not set
++# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
++# CONFIG_BLK_DEV_ISAPNP is not set
++CONFIG_BLK_DEV_IDE_BAST=y
++# CONFIG_IDE_CHIPSETS is not set
++# CONFIG_IDEDMA_AUTO is not set
++# CONFIG_DMA_NONPCI is not set
++# CONFIG_BLK_DEV_ATARAID is not set
++# CONFIG_BLK_DEV_ATARAID_PDC is not set
++# CONFIG_BLK_DEV_ATARAID_HPT is not set
++# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set
++# CONFIG_BLK_DEV_ATARAID_SII 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_DUMMY_KEYB is not set
++
++#
++# 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 is not set
++# CONFIG_SERIAL_AT91_CONSOLE is not set
++CONFIG_SERIAL_S3C2410X=y
++CONFIG_SERIAL_S3C2410X_CONSOLE=y
++CONFIG_SERIAL_8250=y
++# CONFIG_SERIAL_8250_CONSOLE is not set
++CONFIG_SERIAL_8250_EXTENDED=y
++CONFIG_SERIAL_8250_MANY_PORTS=y
++# 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_BAST_16550=y
++CONFIG_SERIAL_CORE=y
++CONFIG_SERIAL_CORE_CONSOLE=y
++CONFIG_UNIX98_PTYS=y
++CONFIG_UNIX98_PTY_COUNT=256
++
++#
++# I2C support
++#
++CONFIG_I2C=y
++CONFIG_I2C_ALGOBIT=y
++# CONFIG_I2C_PHILIPSPAR is not set
++# CONFIG_I2C_ELV is not set
++# CONFIG_I2C_VELLEMAN is not set
++# CONFIG_SCx200_I2C is not set
++# CONFIG_I2C_GUIDE is not set
++CONFIG_I2C_S3C2410BIT=y
++# CONFIG_SCx200_ACB is not set
++# CONFIG_I2C_ALGOPCF is not set
++# CONFIG_I2C_MAINBOARD is not set
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_PROC=y
++
++#
++# Hardware sensors support
++#
++CONFIG_SENSORS=y
++# CONFIG_SENSORS_ADM1021 is not set
++# CONFIG_SENSORS_ADM1024 is not set
++# CONFIG_SENSORS_ADM1025 is not set
++# CONFIG_SENSORS_ADM1026 is not set
++# CONFIG_SENSORS_ADM9240 is not set
++# CONFIG_SENSORS_ASB100 is not set
++# CONFIG_SENSORS_DS1621 is not set
++# CONFIG_SENSORS_FSCPOS is not set
++# CONFIG_SENSORS_FSCSCY is not set
++# CONFIG_SENSORS_GL518SM is not set
++# CONFIG_SENSORS_GL520SM is not set
++# CONFIG_SENSORS_MAXILIFE is not set
++# CONFIG_SENSORS_IT87 is not set
++# CONFIG_SENSORS_MAX6650 is not set
++# CONFIG_SENSORS_MTP008 is not set
++# CONFIG_SENSORS_LM75 is not set
++# CONFIG_SENSORS_LM78 is not set
++# CONFIG_SENSORS_LM80 is not set
++# CONFIG_SENSORS_LM83 is not set
++# CONFIG_SENSORS_LM85 is not set
++# CONFIG_SENSORS_LM87 is not set
++# CONFIG_SENSORS_LM90 is not set
++# CONFIG_SENSORS_LM92 is not set
++# CONFIG_SENSORS_SIS5595 is not set
++# CONFIG_SENSORS_SMSC47M1 is not set
++# CONFIG_SENSORS_THMC50 is not set
++# CONFIG_SENSORS_VIA686A is not set
++# CONFIG_SENSORS_VT1211 is not set
++# CONFIG_SENSORS_VT8231 is not set
++# CONFIG_SENSORS_W83781D is not set
++# CONFIG_SENSORS_W83627HF is not set
++# CONFIG_SENSORS_W83L785TS is not set
++CONFIG_SENSORS_TMP101=y
++# CONFIG_SENSORS_OTHER is not set
++# CONFIG_I2C_DS1307 is not set
++CONFIG_I2C_M41ST87=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 is not set
++# 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_S3C2410_WATCHDOG=y
++# CONFIG_AT91_WATCHDOG is not set
++# 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_BAST_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=m
++CONFIG_JBD=m
++# CONFIG_JBD_DEBUG is not set
++CONFIG_FAT_FS=m
++CONFIG_MSDOS_FS=m
++# CONFIG_UMSDOS_FS is not set
++CONFIG_VFAT_FS=m
++# CONFIG_EFS_FS is not set
++CONFIG_JFFS_FS=m
++CONFIG_JFFS_FS_VERBOSE=0
++CONFIG_JFFS_PROC_FS=y
++CONFIG_JFFS2_FS=m
++CONFIG_JFFS2_FS_DEBUG=0
++CONFIG_CRAMFS=y
++# CONFIG_TMPFS is not set
++CONFIG_RAMFS=y
++CONFIG_ISO9660_FS=m
++CONFIG_JOLIET=y
++CONFIG_ZISOFS=y
++CONFIG_JFS_FS=m
++# 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 is not set
++# CONFIG_DEVFS_MOUNT is not set
++# CONFIG_DEVFS_DEBUG is not set
++CONFIG_DEVPTS_FS=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++CONFIG_ROMFS_FS=y
++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 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
++# 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=m
++
++#
++# Partition Types
++#
++CONFIG_PARTITION_ADVANCED=y
++# CONFIG_ACORN_PARTITION is not set
++# CONFIG_OSF_PARTITION is not set
++# CONFIG_AMIGA_PARTITION is not set
++# CONFIG_ATARI_PARTITION is not set
++# CONFIG_MAC_PARTITION is not set
++CONFIG_MSDOS_PARTITION=y
++CONFIG_BSD_DISKLABEL=y
++CONFIG_MINIX_SUBPARTITION=y
++CONFIG_SOLARIS_X86_PARTITION=y
++CONFIG_UNIXWARE_DISKLABEL=y
++# CONFIG_LDM_PARTITION is not set
++# CONFIG_SGI_PARTITION is not set
++# CONFIG_ULTRIX_PARTITION is not set
++# CONFIG_SUN_PARTITION is not set
++# CONFIG_EFI_PARTITION is not set
++# CONFIG_SMB_NLS is not set
++CONFIG_NLS=y
++
++#
++# Native Language Support
++#
++CONFIG_NLS_DEFAULT="iso8859-1"
++CONFIG_NLS_CODEPAGE_437=y
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++CONFIG_NLS_CODEPAGE_850=y
++CONFIG_NLS_CODEPAGE_852=y
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++# CONFIG_NLS_ISO8859_1 is not set
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# Sound
++#
++CONFIG_SOUND=y
++# CONFIG_SOUND_ALI5455 is not set
++# CONFIG_SOUND_BT878 is not set
++# CONFIG_SOUND_CMPCI is not set
++# CONFIG_SOUND_EMU10K1 is not set
++# CONFIG_MIDI_EMU10K1 is not set
++# CONFIG_SOUND_FUSION is not set
++# CONFIG_SOUND_CS4281 is not set
++# CONFIG_SOUND_ES1370 is not set
++# CONFIG_SOUND_ES1371 is not set
++# CONFIG_SOUND_ESSSOLO1 is not set
++# CONFIG_SOUND_MAESTRO is not set
++# CONFIG_SOUND_MAESTRO3 is not set
++# CONFIG_SOUND_FORTE is not set
++# CONFIG_SOUND_ICH is not set
++# CONFIG_SOUND_RME96XX is not set
++# CONFIG_SOUND_SONICVIBES is not set
++# CONFIG_SOUND_TRIDENT is not set
++# CONFIG_SOUND_MSNDCLAS is not set
++# CONFIG_SOUND_MSNDPIN is not set
++# CONFIG_SOUND_VIA82CXXX is not set
++# CONFIG_MIDI_VIA82CXXX is not set
++CONFIG_SOUND_S3C2410=y
++CONFIG_SOUND_S3C2410_TLV320AIC23=y
++# CONFIG_SOUND_OSS is not set
++# CONFIG_SOUND_VIDC is not set
++# CONFIG_SOUND_WAVEARTIST is not set
++# CONFIG_SOUND_TVMIXER is not set
++# CONFIG_SOUND_AD1980 is not set
++# CONFIG_SOUND_WM97XX 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=y
++CONFIG_USB_DEBUG=y
++CONFIG_USB_DEVICEFS=y
++# CONFIG_USB_BANDWIDTH 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=y
++# CONFIG_USB_OHCI_SA1111 is not set
++CONFIG_USB_OHCI_S3C2410=y
++# CONFIG_USB_SL811HS_ALT is not set
++# CONFIG_USB_SL811HS is not set
++# 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
++# CONFIG_USB_STORAGE_FREECOM is not set
++# CONFIG_USB_STORAGE_ISD200 is not set
++# 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=m
++CONFIG_USB_HID=m
++# CONFIG_USB_HIDINPUT is not set
++# 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
++# CONFIG_USB_MICROTEK is not set
++# CONFIG_USB_HPUSBSCSI is not set
++# CONFIG_USB_PEGASUS is not set
++# CONFIG_USB_RTL8150 is not set
++# CONFIG_USB_KAWETH is not set
++# CONFIG_USB_CATC is not set
++# CONFIG_USB_CDCETHER is not set
++CONFIG_USB_USBNET=m
++# CONFIG_USB_USS720 is not set
++
++#
++# USB Serial Converter support
++#
++CONFIG_USB_SERIAL=y
++# CONFIG_USB_SERIAL_DEBUG 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=y
++# 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_EDGEPORT_TI is not set
++# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
++# CONFIG_USB_SERIAL_KEYSPAN is not set
++# CONFIG_USB_SERIAL_MCT_U232 is not set
++# CONFIG_USB_SERIAL_KLSI is not set
++# CONFIG_USB_SERIAL_KOBIL_SCT is not set
++CONFIG_USB_SERIAL_PL2303=y
++# 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
++#
++# 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
++CONFIG_DEBUG_S3C2410X_UART0=y
++# CONFIG_DEBUG_VR1000_SER3 is not set
++
++#
++# Library routines
++#
++CONFIG_CRC32=y
++CONFIG_ZLIB_INFLATE=y
++CONFIG_ZLIB_DEFLATE=m
+diff -urN kernel-source-2.4.27-8/arch/arm/fastfpe/CPDO.S kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/CPDO.S
+--- kernel-source-2.4.27-8/arch/arm/fastfpe/CPDO.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/CPDO.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,1786 @@
++/*
++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 mantissa is 0, otherwise it is a NaN.
++
++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, r11. They return
++to r14, which contains the address of a rounding function. The rounding
++function expects r0=address, r1-r4=sign, mantissa high, mantissa low,
++exponent, r5=additional lower mantissa bits.
++
++CPDO_rnf_core expects the return address in r14.
++*/
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_adf
++CPDO_adf:
++ ldmia r2,{r6,r7,r8,r11}
++ ldmia r1,{r1,r2,r3,r4}
++
++ cmp r11,#0x7fffffff
++ cmpne r11,#0x80000000
++ cmpne r4,#0x7fffffff
++ cmpne r4,#0x80000000
++ beq CPDO_adf_extra
++
++ cmp r1,r6
++ bne CPDO_suf_s
++
++CPDO_adf_s:
++ subs r6,r4,r11
++ bmi CPDO_adf_normalize1st
++
++CPDO_adf_normalize2nd:
++ cmp r6,#32
++ ble CPDO_adf_normalize2nd_1
++ cmp r6,#64
++ bgt CPDO_adf_normalize2nd_3
++
++CPDO_adf_normalize2nd_2:
++ sub r6,r6,#32
++ rsb r11,r6,#32
++ mov r5,r8,lsr r6
++ add r5,r5,r7,lsl r11
++ movs r11,r8,lsl r11
++ orrne r5,r5,#1
++ mov r8,r7,lsr r6
++ mov r7,#0
++ b CPDO_adf_add
++
++CPDO_adf_normalize2nd_1:
++ rsb r11,r6,#32
++ mov r5,r8,lsl r11
++ mov r8,r8,lsr r6
++ add r8,r8,r7,lsl r11
++ mov r7,r7,lsr r6
++ b CPDO_adf_add
++
++CPDO_adf_normalize2nd_3:
++ mov r5,#0x40000000
++ mov pc,r14
++
++CPDO_adf_normalize1st:
++ mov r4,r11
++ rsb r6,r6,#0
++ cmp r6,#32
++ ble CPDO_adf_normalize1st_1
++ cmp r6,#64
++ bgt CPDO_adf_normalize1st_3
++
++CPDO_adf_normalize1st_2:
++ sub r6,r6,#32
++ rsb r11,r6,#32
++ mov r5,r3,lsr r6
++ add r5,r5,r2,lsl r11
++ movs r11,r3,lsl r11
++ orrne r5,r5,#1
++ mov r3,r2,lsr r6
++ mov r2,#0
++ b CPDO_adf_add
++
++CPDO_adf_normalize1st_1:
++ rsb r11,r6,#32
++ mov r5,r3,lsl r11
++ mov r3,r3,lsr r6
++ add r3,r3,r2,lsl r11
++ mov r2,r2,lsr r6
++ b CPDO_adf_add
++
++CPDO_adf_normalize1st_3:
++ mov r5,#0x40000000
++ mov r2,r7
++ mov r3,r8
++ mov pc,r14
++
++CPDO_adf_add:
++ adds r3,r3,r8
++ adcs r2,r2,r7
++ bcc CPDO_adf_add_no_overflow
++
++ movs r2,r2,rrx
++ movs r3,r3,rrx
++ movs r5,r5,rrx
++ orrcs r5,r5,#1
++ add r4,r4,#1
++
++CPDO_adf_add_no_overflow:
++ mov pc,r14
++
++CPDO_adf_extra:
++ cmp r4,#0x7fffffff
++ beq CPDO_adf_1st_infnan
++ cmp r11,#0x7fffffff
++ beq CPDO_adf_2nd_infnan
++ cmp r11,#0x80000000
++ beq CPDO_adf_2nd_0
++
++CPDO_adf_1st_0:
++ mov r1,r6
++ mov r2,r7
++ mov r3,r8
++ mov r4,r11
++ mov r5,#0
++ mov pc,r14
++
++CPDO_adf_2nd_0:
++ cmp r4,#0x80000000
++ beq CPDO_adf_both_0
++ mov r5,#0
++ mov pc,r14
++
++CPDO_adf_both_0:
++ cmp r1,r6
++ beq CPDO_adf_both_0_equal_sign
++ and r5,r5,#0x00000060
++ cmp r5,#0x00000040 // rounding mode M?
++ moveq r1,#0x80000000
++ movne r1,#0
++CPDO_adf_both_0_equal_sign:
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_adf_1st_infnan:
++ cmp r11,#0x7fffffff
++ beq CPDO_adf_both_infnan
++CPDO_adf_1st_infnan_entry:
++ orrs r5,r3,r2,lsl#1 // ignore MSB
++ moveq pc,r14 // Inf
++ tst r2,#0x40000000
++ movne pc,r14 // QNaN
++CPDO_adf_generate_qnan:
++ mov r1,#0x80000000
++ mov r2,#0x7fffffff
++ mov r3,#0xffffffff
++ mov r4,#0x7fffffff
++ ldr r5,[r10,#128]
++ orr r5,r5,#1 // set invalid operation flag
++ str r5,[r10,#128]
++ mov pc,r14
++
++CPDO_adf_2nd_infnan:
++ mov r1,r6
++ mov r2,r7
++ mov r3,r8
++ mov r4,r11
++ b CPDO_adf_1st_infnan_entry
++
++CPDO_adf_both_infnan:
++ orrs r5,r3,r2,lsl#1 // ignore MSB
++ beq CPDO_adf_1st_inf
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ beq CPDO_adf_2nd_inf
++ tst r2,#0x40000000
++ tstne r7,#0x40000000
++ beq CPDO_adf_generate_qnan // at least one is SNaN
++ orrs r5,r3,r2,lsl#1 // ignore MSB, FIXME! what is going on here?
++ moveq r1,r6 // if first is not NaN
++ moveq r2,r7 // give second as result
++ moveq r3,r8
++ mov pc,r14
++
++CPDO_adf_1st_inf:
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ beq CPDO_adf_both_inf
++ tst r7,#0x40000000
++ beq CPDO_adf_generate_qnan
++ mov r1,r6 //if 2nd no SNaN return 2nd
++ mov r2,r7
++ mov r3,r8
++ mov pc,r14
++
++CPDO_adf_2nd_inf:
++ tst r2,#0x40000000
++ beq CPDO_adf_generate_qnan
++ mov pc,r14 // if 1st no SNaN just return it
++
++CPDO_adf_both_inf:
++ cmp r1,r6
++ bne CPDO_adf_generate_qnan // signs of both inf are different
++ mov pc,r14
++
++/*--------------------------------------------------------------------------*/
++
++ .globl CPDO_suf
++CPDO_suf:
++ ldmia r2,{r6,r7,r8,r11}
++ ldmia r1,{r1,r2,r3,r4}
++
++CPDO_suf_l:
++ cmp r11,#0x7fffffff
++ cmpne r11,#0x80000000
++ cmpne r4,#0x7fffffff
++ cmpne r4,#0x80000000
++ beq CPDO_suf_extra
++
++ cmp r1,r6
++ bne CPDO_adf_s
++
++CPDO_suf_s:
++ subs r6,r4,r11
++ blt CPDO_suf_normalize1st
++ bgt CPDO_suf_normalize2nd
++ cmp r2,r7
++ cmpeq r3,r8
++ beq CPDO_suf_zero
++ mov r5,#0
++ bcs CPDO_suf_sub_1stbigger
++ eor r1,r1,#0x80000000
++ b CPDO_suf_sub_2ndbigger
++
++CPDO_suf_normalize2nd:
++ cmp r6,#32
++ ble CPDO_suf_normalize2nd_1
++ cmp r6,#64
++ bgt CPDO_suf_normalize2nd_3
++
++CPDO_suf_normalize2nd_2:
++ sub r6,r6,#32
++ rsb r11,r6,#32
++ mov r5,r8,lsr r6
++ add r5,r5,r7,lsl r11
++ movs r11,r8,lsl r11
++ orrne r5,r5,#1
++ mov r8,r7,lsr r6
++ mov r7,#0
++ b CPDO_suf_sub_1stbigger
++
++CPDO_suf_normalize2nd_1:
++ rsb r11,r6,#32
++ mov r5,r8,lsl r11
++ mov r8,r8,lsr r6
++ add r8,r8,r7,lsl r11
++ mov r7,r7,lsr r6
++ b CPDO_suf_sub_1stbigger
++
++CPDO_suf_normalize2nd_3:
++ sub r6,r6,#64
++ cmp r6,#32
++ bge CPDO_suf_normalize2nd_4
++ rsb r11,r6,#32
++ mov r5,r7,lsr r6
++ orrs r11,r8,r7,lsl r11
++ orrne r5,r5,#1
++ mov r7,#0
++ mov r8,#0
++ b CPDO_suf_sub_1stbigger
++
++CPDO_suf_normalize2nd_4:
++ mov r5,#1
++ mov r7,#0
++ mov r8,#0
++ b CPDO_suf_sub_1stbigger
++
++CPDO_suf_normalize1st:
++ eor r1,r1,#0x80000000
++ mov r4,r11
++ rsb r6,r6,#0
++ cmp r6,#32
++ ble CPDO_suf_normalize1st_1
++ cmp r6,#64
++ bgt CPDO_suf_normalize1st_3
++
++CPDO_suf_normalize1st_2:
++ sub r6,r6,#32
++ rsb r11,r6,#32
++ mov r5,r3,lsr r6
++ add r5,r5,r2,lsl r11
++ movs r11,r3,lsl r11
++ orrne r5,r5,#1
++ mov r3,r2,lsr r6
++ mov r2,#0
++ b CPDO_suf_sub_2ndbigger
++
++CPDO_suf_normalize1st_1:
++ rsb r11,r6,#32
++ mov r5,r3,lsl r11
++ mov r3,r3,lsr r6
++ add r3,r3,r2,lsl r11
++ mov r2,r2,lsr r6
++ b CPDO_suf_sub_2ndbigger
++
++CPDO_suf_normalize1st_3:
++ sub r6,r6,#64
++ cmp r6,#32
++ bge CPDO_suf_normalize1st_4
++ rsb r11,r6,#32
++ mov r5,r2,lsr r6
++ orrs r11,r3,r2,lsl r11
++ orrne r5,r5,#1
++ mov r2,#0
++ mov r3,#0
++ b CPDO_suf_sub_2ndbigger
++
++CPDO_suf_normalize1st_4:
++ mov r5,#1
++ mov r2,#0
++ mov r3,#0
++ b CPDO_suf_sub_2ndbigger
++
++CPDO_suf_sub_1stbigger:
++ rsbs r5,r5,#0
++ sbcs r3,r3,r8
++ sbcs r2,r2,r7
++ movmi pc,r14
++ b CPDO_suf_norm
++
++CPDO_suf_sub_2ndbigger:
++ rsbs r5,r5,#0
++ sbcs r3,r8,r3
++ sbcs r2,r7,r2
++ movmi pc,r14
++
++CPDO_suf_norm:
++ teq r2,#0 // normalize 32 bit
++ bne CPDO_suf_norm16
++ teq r3,#0 // normalize 64 bit
++ bne CPDO_suf_norm32
++ mov r2,r5
++ mov r3,#0
++ mov r5,#0
++ sub r4,r4,#64
++ mov pc,r14
++CPDO_suf_norm32:
++ mov r2,r3
++ mov r3,r5
++ mov r5,#0
++ sub r4,r4,#32
++CPDO_suf_norm16:
++ cmp r2,#0x00010000 // normalize 16 bit
++ bcs CPDO_suf_norm8
++ mov r2,r2,lsl#16
++ orr r2,r2,r3,lsr#16
++ mov r3,r3,lsl#16
++ orr r3,r3,r5,lsr#16
++ mov r5,r5,lsl#16
++ sub r4,r4,#16
++CPDO_suf_norm8:
++ cmp r2,#0x01000000 // normalize 8 bit
++ bcs CPDO_suf_norm4
++ mov r2,r2,lsl#8
++ orr r2,r2,r3,lsr#24
++ mov r3,r3,lsl#8
++ orr r3,r3,r5,lsr#24
++ mov r5,r5,lsl#8
++ sub r4,r4,#8
++CPDO_suf_norm4:
++ cmp r2,#0x10000000 // normalize 4 bit
++ bcs CPDO_suf_norm2
++ mov r2,r2,lsl#4
++ orr r2,r2,r3,lsr#28
++ mov r3,r3,lsl#4
++ orr r3,r3,r5,lsr#28
++ mov r5,r5,lsl#4
++ sub r4,r4,#4
++CPDO_suf_norm2:
++ cmp r2,#0x40000000 // normalize 2 bit
++ bcs CPDO_suf_norm1
++ mov r2,r2,lsl#2
++ orr r2,r2,r3,lsr#30
++ mov r3,r3,lsl#2
++ orr r3,r3,r5,lsr#30
++ mov r5,r5,lsl#2
++ sub r4,r4,#2
++CPDO_suf_norm1:
++ cmp r2,#0x80000000 // normalize 1 bit
++ bcs CPDO_suf_norme
++ mov r2,r2,lsl#1
++ orr r2,r2,r3,lsr#31
++ mov r3,r3,lsl#1
++ orr r3,r3,r5,lsr#31
++ mov r5,r5,lsl#1
++ sub r4,r4,#1
++CPDO_suf_norme:
++ mov pc,r14
++
++CPDO_suf_zero:
++ and r5,r5,#0x00000060
++ cmp r5,#0x00000040 // rounding mode M?
++ moveq r1,#0x80000000
++ movne r1,#0
++ mov r2,#0
++ mov r3,#0
++ mov r4,#0x80000000
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_suf_extra: // nearly the same as with adf
++ cmp r11,#0x7fffffff // the only thing we need to do is
++ bne CPDO_suf_extra_sign // to invert the second sign if
++ orrnes r5,r8,r7,lsl#1 // it is not a NaN, ignore MSB
++ bne CPDO_adf_extra
++CPDO_suf_extra_sign:
++ eor r6,r6,#0x80000000
++ b CPDO_adf_extra
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_rsf
++CPDO_rsf:
++ ldmia r1,{r6,r7,r8,r11}
++ ldmia r2,{r1,r2,r3,r4}
++ b CPDO_suf_l
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_muf
++CPDO_muf:
++ ldmia r2,{r6,r7,r8,r11}
++ ldmia r1,{r1,r2,r3,r4}
++
++ cmp r11,#0x7fffffff
++ cmpne r4,#0x7fffffff
++ beq CPDO_muf_extra
++ eor r1,r1,r6 // sign
++ cmp r11,#0x80000000
++ cmpne r4,#0x80000000
++ beq CPDO_muf_zero
++
++ add r4,r4,r11 // exponent
++#if 0
++
++#define x4 r0
++#define x3 r1
++#define x2 r4
++#define x1 r6
++#define y4 r9
++#define y3 r7
++#define y2 r10
++#define y1 r8
++#define z4 r2
++#define z3 r3
++#define z2 r5
++#define z1 r11
++#define tmp r12
++
++ stmdb r13!, {r0, r1, r4, r9, r10, r12}
++ mov x4,r2,lsr#16
++ bic x3,r2,x4,lsl#16
++ mov x2,r3,lsr#16
++ bic x1,r3,x2,lsl#16
++ mov y4,r7,lsr#16
++ bic y3,r7,y4,lsl#16
++ mov y2,r8,lsr#16
++ bic y1,r8,y2,lsl#16
++ mul z1,x1,y1
++ mul tmp,x1,y2
++ mov z2,#0
++ adds z1,z1,tmp,lsl#16
++ adc z2,z2,tmp,lsr#16
++ mul tmp,x2,y1
++ adds z1,z1,tmp,lsl#16
++ adcs z2,z2,tmp,lsr#16
++ mul tmp,x1,y4
++ mov z3,#0
++ adds z2,z2,tmp,lsl#16
++ adc z3,z3,tmp,lsr#16
++ mul tmp,x2,y3
++ adds z2,z2,tmp,lsl#16
++ adc z3,z3,tmp,lsr#16
++ mul tmp,x3,y2
++ adds z2,z2,tmp,lsl#16
++ adc z3,z3,tmp,lsr#16
++ mul tmp,x4,y1
++ adds z2,z2,tmp,lsl#16
++ adc z3,z3,tmp,lsr#16
++ mul tmp,x3,y4
++ mul z4,x4,y4
++ adds z3,z3,tmp,lsl#16
++ adc z4,z4,tmp,lsr#16
++ mul tmp,x4,y3
++ adds z3,z3,tmp,lsl#16
++ adc z4,z4,tmp,lsr#16
++ mul tmp,x1,y3
++ adds z2,z2,tmp
++ mul tmp,x2,y4
++ adcs z3,z3,tmp
++ mul tmp,x2,y2
++ adc z4,z4,#0
++ adds z2,z2,tmp
++ mul tmp,x3,y3
++ adcs z3,z3,tmp
++ mul tmp,x3,y1
++ adc z4,z4,#0
++ adds z2,z2,tmp
++ teq z1,#0
++ orrne z2,z2,#1 // z1 must not be lost for rounding
++ mul tmp,x4,y2
++ adcs z3,z3,tmp
++ adcs z4,z4,#0
++ ldmia r13!, {r0, r1, r4, r9, r10, r12}
++#else
++
++#define x32 r2
++#define x10 r3
++#define y32 r7
++#define y10 r8
++#define z3 r0
++#define z2 r1
++#define z1 r4
++#define z0 r6
++#define v1 r9
++#define v0 r11
++#define tmp r5
++
++ stmdb r13!,{r0,r1,r4,r9}
++
++ mov z3,x32,lsr#16
++ bic z2,x32,z3,lsl#16
++ movs v1,y32,lsr#16
++ bic v0,y32,v1,lsl#16
++
++ mul tmp,z3,v0
++ mul z3,v1,z3
++ mulne v1,z2,v1
++ mul z2,v0,z2
++ adds z2,z2,tmp,lsl#16
++ adc z3,z3,tmp,lsr#16
++ adds z2,z2,v1,lsl#16
++ adc z3,z3,v1,lsr#16
++
++ mov z1,x10,lsr#16
++ bic z0,x10,z1,lsl#16
++ movs v1,y10,lsr#16
++ bic v0,y10,v1,lsl#16
++
++ mul tmp,z1,v0
++ mul z1,v1,z1
++ mulne v1,z0,v1
++ mul z0,v0,z0
++ adds z0,z0,tmp,lsl#16
++ adc z1,z1,tmp,lsr#16
++ adds z0,z0,v1,lsl#16
++ adc z1,z1,v1,lsr#16
++
++ adds z2,z2,z1 // z3 is max. 0xfffffffe
++ adc z3,z3,#0 // so this trick is possible
++ adds z1,z2,z0 // to save one addition
++ adcs z2,z2,z3
++ adc z3,z3,#0
++
++ subs x10,x32,x10
++ mov v0,#0
++ mov v1,v0,rrx
++
++ sublo v0,y32,y10
++ subnes y10,y10,y32
++
++ orreq v1,v1,#1<<31
++ eorcs v1,v1,#1<<31
++ subcc v0,v0,x10
++
++ movs x32,x10,lsr#16
++ bic x10,x10,x32,lsl#16
++ mov y32,y10,lsr#16
++ bic y10,y10,y32,lsl#16
++
++ mul tmp,x10,y10
++ mla v0,x32,y32,v0
++ mulne x32,y10,x32
++ adds tmp,tmp,x32,lsl#16
++ adc v0,v0,x32,lsr#16
++ mul y32,x10,y32
++ adds tmp,tmp,y32,lsl#16
++ adc v0,v0,y32,lsr#16
++ adds r5,z1,tmp
++ adcs r3,z2,v0
++ adc r2,z3,v1,asr#31
++
++ teq z0,#0
++ orrne r5,r5,#1 // z0 must not be lost for rounding
++ cmp r2,#0
++
++ ldmia r13!,{r0,r1,r4,r9}
++#endif
++
++ bpl CPDO_muf_norm
++ add r4,r4,#1
++ mov pc,r14
++
++CPDO_muf_norm:
++ adds r5,r5,r5
++ adcs r3,r3,r3
++ adc r2,r2,r2
++ mov pc,r14
++
++CPDO_muf_extra:
++ cmp r4,#0x7fffffff
++ beq CPDO_muf_1st_infnan
++CPDO_muf_2nd_infnan:
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ bne CPDO_muf_2nd_nan
++ cmp r4,#0x80000000
++ beq CPDO_muf_generate_qnan
++ mov r2,r7 // copy MSB
++ mov r3,#0
++ mov r4,#0x7fffffff
++ eor r1,r1,r6
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_muf_1st_infnan:
++ cmp r11,#0x7fffffff
++ beq CPDO_muf_both_infnan
++ orrs r5,r3,r2,lsl#1 // ignore MSB
++ bne CPDO_muf_1st_nan
++ cmp r11,#0x80000000
++ beq CPDO_muf_generate_qnan
++// mov r4,#0x7fffffff
++ eor r1,r1,r6
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_muf_both_infnan:
++ orrs r5,r3,r2,lsl#1 // ignore MSB
++ beq CPDO_muf_both_infnan_1st_inf
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ beq CPDO_muf_both_infnan_2nd_inf
++ tst r2,#0x40000000
++ tstne r7,#0x40000000
++ beq CPDO_muf_generate_qnan
++ mov pc,r14
++
++CPDO_muf_both_infnan_1st_inf:
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ beq CPDO_muf_both_inf
++ b CPDO_muf_2nd_nan
++
++CPDO_muf_both_infnan_2nd_inf:
++ b CPDO_muf_1st_nan
++
++CPDO_muf_both_inf:
++ eor r1,r1,r6
++ orr r2,r2,r7 // copy both MSB
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_muf_zero:
++ mov r2,#0
++ mov r3,#0
++ mov r4,#0x80000000
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_muf_1st_nan:
++ tst r2,#0x40000000
++ beq CPDO_muf_generate_qnan
++ mov pc,r14
++
++CPDO_muf_2nd_nan:
++ tst r7,#0x40000000
++ beq CPDO_muf_generate_qnan
++ mov r1,r6
++ mov r2,r7
++ mov r3,r8
++ mov r4,r11
++ mov pc,r14
++
++CPDO_muf_generate_qnan:
++ mov r1,#0x80000000
++ mov r2,#0x7fffffff
++ mov r3,#0xffffffff
++ mov r4,#0x7fffffff
++ ldr r5,[r10,#128]
++ orr r5,r5,#1
++ str r5,[r10,#128]
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_muf_M
++CPDO_muf_M:
++ ldmia r2,{r6,r7,r8,r11}
++ ldmia r1,{r1,r2,r3,r4}
++
++ cmp r11,#0x7fffffff
++ cmpne r4,#0x7fffffff
++ beq CPDO_muf_extra
++ eor r1,r1,r6 // sign
++ cmp r11,#0x80000000
++ cmpne r4,#0x80000000
++ beq CPDO_muf_zero
++
++ add r4,r4,r11 // exponent
++#if 0
++ umull r11,r5,r3,r8 // r5|r11 = lo1*lo2
++ teq r11,#0
++ orrne r5,r5,#0x00000001 // r11 must not be lost for rounding
++ umull r11,r6,r8,r2 // r6|r11 = lo2*hi1
++ umull r8,r2,r7,r2 // r2|r8 = hi1*hi2
++ umull r12,r3,r7,r3 // r3|r12 = lo1*hi2
++ adds r5,r5,r12
++ adcs r3,r3,r6
++ adc r2,r2,#0
++ adds r5,r5,r11
++ adcs r3,r3,r8
++ adcs r2,r2,#0
++#else
++ umull r12,r11,r2,r7
++ umull r2,r6,r8,r2
++ umull r8,r5,r3,r8
++ adds r5,r5,r2
++ adcs r12,r12,r6
++ adc r11,r11,#0
++ umull r7,r6,r3,r7
++ adds r5,r5,r7
++ adcs r3,r12,r6
++ adc r2,r11,#0
++ teq r8,#0
++ orrne r5,r5,#1 // r8 must not be lost for rounding
++ cmp r2,#0
++#endif
++ bpl CPDO_muf_norm
++ add r4,r4,#1
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++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
++
++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
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_dvf
++CPDO_dvf:
++ ldmia r2,{r6,r7,r8,r11}
++ ldmia r1,{r1,r2,r3,r4}
++
++CPDO_dvf_l:
++ cmp r11,#0x7fffffff
++ cmpne r4,#0x7fffffff
++ beq CPDO_dvf_infnan
++ eor r1,r1,r6
++ cmp r11,#0x80000000
++ cmpne r4,#0x80000000
++ beq CPDO_dvf_zero
++
++ sub r4,r4,r11
++
++#define x4 r11
++#define x3 r7
++#define x2 r12
++#define x1 r8
++#define y2 r14
++#define y1 r9
++#define z3 r4
++#define z2 r5
++#define z1 r6
++#define tmp r10
++
++ cmp r2,r7
++ cmpeq r3,r8
++ bcs CPDO_dvf_no_normalize
++
++ sub r4,r4,#1
++ stmdb r13!,{r1,r4,r9,r10,r11,r14}
++ mov r4,r2,lsr#31
++ mov r5,r2,lsl#1
++ orr r5,r5,r3,lsr#31
++ mov r6,r3,lsl#1 // dividend
++ b CPDO_dvf_normalize_back
++
++CPDO_dvf_no_normalize:
++ stmdb r13!,{r1,r4,r9,r10,r11,r14}
++ mov r4,#0
++ mov r5,r2
++ mov r6,r3 // dividend
++
++CPDO_dvf_normalize_back:
++ mov r1,#0
++ sub r10,r1,r7,lsr#1
++ mov r11,#0x40000000
++
++ .macro inv_step
++ adds r11,r10,r11,lsl#1
++ subcc r11,r11,r10
++ adc r1,r1,r1
++ .endm
++
++ .rept 17
++ inv_step
++ .endr
++
++ mov r1,r1,lsl#15
++ adds r1,r1,#1<<15
++ movcs r1,#0xffffffff // inverse
++ mov r1,r1,lsr#16
++
++ mov r2,#0
++ mov r3,#0 // clear result space
++
++ mov x4,r7,lsr#16
++ bic x3,r7,x4,lsl#16
++ mov x2,r8,lsr#16
++ bic x1,r8,x2,lsl#16 // split divisor for 16x16=32bit mul
++
++CPDO_dvf_loop_entry:
++ mov r4,r4,lsl#16
++ orrs r4,r4,r5,lsr#16
++ mov r5,r5,lsl#16
++ orr r5,r5,r6,lsr#16
++ mov r6,r6,lsl#16 // shift dividend left by 16
++
++ bmi CPDO_dvf_loop_negative
++ mov r10,r4,lsr#16
++ mul r9,r10,r1
++ bic r10,r4,r10,lsl#16
++ mul r10,r1,r10
++ add r9,r9,r10,lsr#16 //estimate 16 bits of result in r9
++
++ mov r2,r2,lsl#16
++ orr r2,r2,r3,lsr#16
++ adds r3,r9,r3,lsl#16 // shift result left by 16 and
++ adc r2,r2,#0 // add in new result bits
++
++ mov r9,r9,lsl#1
++ mov y2,r9,lsr#16
++ bic y1,r9,y2,lsl#16
++ mul tmp,x1,y1
++ subs z1,z1,tmp
++ mul tmp,x3,y1
++ sbcs z2,z2,tmp
++ mul tmp,x4,y2
++ sbc z3,z3,tmp
++ mul tmp,x2,y2
++ subs z2,z2,tmp
++ sbc z3,z3,#0
++ mul tmp,x2,y1
++ subs z1,z1,tmp,lsl#16
++ sbcs z2,z2,tmp,lsr#16
++ sbc z3,z3,#0
++ mul tmp,x1,y2
++ subs z1,z1,tmp,lsl#16
++ sbcs z2,z2,tmp,lsr#16
++ sbc z3,z3,#0
++ mul tmp,x4,y1
++ subs z2,z2,tmp,lsl#16
++ sbc z3,z3,tmp,lsr#16
++ mul tmp,x3,y2
++ subs z2,z2,tmp,lsl#16
++ sbc z3,z3,tmp,lsr#16 // subtract divisor * estimated result
++
++ tst r2,#0xff000000
++ beq CPDO_dvf_loop_entry
++
++ b CPDO_dvf_end_entry
++
++CPDO_dvf_loop_negative:
++ rsb r14,r4,#0
++ mov r10,r14,lsr#16
++ mul r9,r10,r1
++ bic r10,r14,r10,lsl#16
++ mul r10,r1,r10
++ add r9,r9,r10,lsr#16 // estimate 16 bits of result in r9
++
++ mov r2,r2,lsl#16
++ orr r2,r2,r3,lsr#16
++ rsbs r3,r9,r3,lsl#16 // shift result left by 16 and
++ sbc r2,r2,#0 // add in new result bits
++
++ mov r9,r9,lsl#1
++ mov y2,r9,lsr#16
++ bic y1,r9,y2,lsl#16
++ mul tmp,x1,y1
++ adds z1,z1,tmp
++ mul tmp,x3,y1
++ adcs z2,z2,tmp
++ mul tmp,x4,y2
++ adc z3,z3,tmp
++ mul tmp,x2,y2
++ adds z2,z2,tmp
++ adc z3,z3,#0
++ mul tmp,x2,y1
++ adds z1,z1,tmp,lsl#16
++ adcs z2,z2,tmp,lsr#16
++ adc z3,z3,#0
++ mul tmp,x1,y2
++ adds z1,z1,tmp,lsl#16
++ adcs z2,z2,tmp,lsr#16
++ adc z3,z3,#0
++ mul tmp,x4,y1
++ adds z2,z2,tmp,lsl#16
++ adc z3,z3,tmp,lsr#16
++ mul tmp,x3,y2
++ adds z2,z2,tmp,lsl#16
++ adc z3,z3,tmp,lsr#16 // subtract divisor * estimated result
++
++ tst r2,#0xff000000
++ beq CPDO_dvf_loop_entry
++
++CPDO_dvf_end_entry:
++ movs r4,r4,asr#1
++ movs r5,r5,rrx // remainder was shifted left by 1
++ movs r6,r6,rrx // relative to divisor
++
++ orr r7,x3,x4,lsl#16
++ orr r8,x1,x2,lsl#16 // put the split divisor together again
++
++ cmp r4,#0
++ blt CPDO_dvf_end_negative
++ cmpeq r5,r7
++ cmpeq r6,r8
++ bcc CPDO_dvf_end
++
++CPDO_dvf_end_positive:
++ adds r3,r3,#1
++ adc r2,r2,#0
++
++ subs r6,r6,r8
++ sbcs r5,r5,r7
++ sbcs r4,r4,#0
++ bne CPDO_dvf_end_positive
++
++ cmp r5,r7
++ cmpeq r6,r8
++ bcs CPDO_dvf_end_positive
++ b CPDO_dvf_end
++
++CPDO_dvf_end_negative:
++ subs r3,r3,#1
++ sbc r2,r2,#0
++
++ adds r6,r6,r8
++ adcs r5,r5,r7
++ adcs r4,r4,#0
++ bmi CPDO_dvf_end_negative
++
++CPDO_dvf_end:
++ orrs r9,r5,r6
++ ldmia r13!,{r1,r4,r9,r10,r11,r14}
++ moveq pc,r14
++
++ adds r6,r6,r6
++ adcs r5,r5,r5
++ movcs r5,#0xc0000000
++ movcs pc,r14
++
++ cmp r5,r7
++ cmpeq r6,r8
++ movcc r5,#0x40000000
++ moveq r5,#0x80000000
++ movhi r5,#0xc0000000
++ mov pc,r14
++
++CPDO_dvf_zero:
++ cmp r11,#0x80000000
++ beq CPDO_dvf_by_zero
++
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next // 0 already there
++@ mov pc,r14
++
++CPDO_dvf_by_zero:
++ cmp r4,#0x80000000
++ beq CPDO_dvf_generate_qnan // first 0 too
++
++ mov r2,#0x80000000 // set MSB
++ mov r3,#0
++ mov r4,#0x7fffffff
++ ldr r5,[r10,#128]
++ orr r5,r5,#2 // division by zero
++ str r5,[r10,#128]
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_dvf_infnan:
++ cmp r4,#0x7fffffff
++ beq CPDO_dvf_1st_infnan
++
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ beq CPDO_dvf_2nd_inf
++ mov r1,r6
++ mov r2,r7
++ mov r3,r8
++ mov r4,r11
++ b CPDO_dvf_1st_or_2nd_nan
++
++CPDO_dvf_2nd_inf:
++ eor r1,r1,r6
++ mov r2,#0
++ mov r3,#0
++ mov r4,#0x80000000
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next // zero created
++@ mov pc,r14
++
++CPDO_dvf_1st_infnan:
++ cmp r11,#0x7fffffff
++ beq CPDO_dvf_both_infnan
++
++ orrs r5,r3,r2,lsl#1 // 1st inf? ignore MSB
++ bne CPDO_dvf_1st_or_2nd_nan
++
++ eor r1,r1,r6 // sign for inf
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next // inf already there
++@ mov pc,r14
++
++CPDO_dvf_1st_or_2nd_nan:
++ tst r2,#0x40000000
++ beq CPDO_dvf_generate_qnan
++ mov pc,r14 // qnan1/2 already/copied there
++
++CPDO_dvf_both_infnan:
++ orrs r5,r3,r2,lsl#1 // ignore MSB
++ beq CPDO_dvf_both_infnan_1st_inf
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ beq CPDO_dvf_both_infnan_2nd_inf
++ tst r2,#0x40000000
++ tstne r7,#0x40000000
++ beq CPDO_dvf_generate_qnan
++ mov pc,r14
++
++CPDO_dvf_both_infnan_1st_inf:
++ tst r7,#0x40000000 // 2nd inf or SNaN ?
++ beq CPDO_dvf_generate_qnan
++ mov r1,r6
++ mov r2,r7
++ mov r3,r8
++ mov r4,r11 // copy 2nd QNaN
++ mov pc,r14
++
++CPDO_dvf_both_infnan_2nd_inf:
++ tst r2,#0x40000000 // 1st SNaN ?
++ beq CPDO_dvf_generate_qnan
++ mov pc,r14
++
++CPDO_dvf_generate_qnan:
++ mov r1,#0x80000000
++ mov r2,#0x7fffffff
++ mov r3,#0xffffffff
++ mov r4,#0x7fffffff
++ ldr r5,[r10,#128]
++ orr r5,r5,#1
++ str r5,[r10,#128]
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_dvf_M
++CPDO_dvf_M:
++ ldmia r2,{r6,r7,r8,r11}
++ ldmia r1,{r1,r2,r3,r4}
++
++CPDO_dvf_M_l:
++ cmp r11,#0x7fffffff
++ cmpne r4,#0x7fffffff
++ beq CPDO_dvf_infnan
++ eor r1,r1,r6
++ cmp r11,#0x80000000
++ cmpne r4,#0x80000000
++ beq CPDO_dvf_zero
++
++ sub r4,r4,r11
++
++ cmp r2,r7
++ cmpeq r3,r8
++ bcs CPDO_dvf_M_no_normalize
++
++ sub r4,r4,#1
++ stmdb r13!,{r1,r4,r9,r10}
++ mov r4,r2,lsr#31
++ mov r5,r2,lsl#1
++ orr r5,r5,r3,lsr#31
++ mov r6,r3,lsl#1 // dividend
++ b CPDO_dvf_M_normalize_back
++
++CPDO_dvf_M_no_normalize:
++ stmdb r13!,{r1,r4,r9,r10}
++ mov r4,#0
++ mov r5,r2
++ mov r6,r3 // dividend
++
++CPDO_dvf_M_normalize_back:
++ mov r1,#0
++ sub r10,r1,r7,lsr#1
++ mov r11,#0x40000000
++
++ .macro inv_step
++ adds r11,r10,r11,lsl#1
++ subcc r11,r11,r10
++ adc r1,r1,r1
++ .endm
++
++ .rept 18
++ inv_step
++ .endr
++
++ mov r1,r1,lsl#14
++ adds r1,r1,#1<<15
++ movcs r1,#0xffffffff // inverse
++
++ mov r2,#0
++ mov r3,#0 // clear result space
++
++CPDO_dvf_M_loop_entry:
++ mov r4,r4,lsl#16
++ orrs r4,r4,r5,lsr#16
++ mov r5,r5,lsl#16
++ orr r5,r5,r6,lsr#16
++ mov r6,r6,lsl#16 // shift dividend left by 16
++
++ bmi CPDO_dvf_M_loop_negative
++ umull r10,r9,r4,r1 // estimate 16 bits of result in r9
++
++ mov r2,r2,lsl#16
++ orr r2,r2,r3,lsr#16
++ adds r3,r9,r3,lsl#16 // shift result left by 16 and
++ adc r2,r2,#0 // add in new result bits
++
++ mov r9,r9,lsl#1
++ umull r11,r10,r8,r9 // divisor lo * estimated result
++ subs r6,r6,r11
++ sbcs r5,r5,r10
++ sbc r4,r4,#0
++
++ umull r11,r10,r7,r9 // divisor hi * estimated result
++ subs r5,r5,r11
++ sbc r4,r4,r10
++
++ tst r2,#0xff000000
++ beq CPDO_dvf_M_loop_entry
++
++ b CPDO_dvf_M_end_entry
++
++CPDO_dvf_M_loop_negative:
++ rsb r11,r4,#0
++ umull r10,r9,r11,r1 // estimate 16 bits of result in r9
++
++ mov r2,r2,lsl#16
++ orr r2,r2,r3,lsr#16
++ rsbs r3,r9,r3,lsl#16 // shift result left by 16 and
++ sbc r2,r2,#0 // add in new result bits
++
++ mov r9,r9,lsl#1
++ umull r11,r10,r8,r9 // divisor lo * estimated result
++ adds r6,r6,r11
++ adcs r5,r5,r10
++ adc r4,r4,#0
++
++ umlal r5,r4,r7,r9 // divisor hi * estimated result
++
++ tst r2,#0xff000000
++ beq CPDO_dvf_M_loop_entry
++
++CPDO_dvf_M_end_entry:
++ movs r4,r4,asr#1
++ movs r5,r5,rrx // remainder was shifted left by 1
++ movs r6,r6,rrx // relative to divisor
++
++ cmp r4,#0
++ blt CPDO_dvf_M_end_negative
++ cmpeq r5,r7
++ cmpeq r6,r8
++ bcc CPDO_dvf_M_end
++
++CPDO_dvf_M_end_positive:
++ adds r3,r3,#1
++ adc r2,r2,#0
++
++ subs r6,r6,r8
++ sbcs r5,r5,r7
++ sbcs r4,r4,#0
++
++ cmp r5,r7
++ cmpeq r6,r8
++ bcs CPDO_dvf_M_end_positive
++ b CPDO_dvf_M_end
++
++CPDO_dvf_M_end_negative:
++ subs r3,r3,#1
++ sbc r2,r2,#0
++
++ adds r6,r6,r8
++ adcs r5,r5,r7
++ adcs r4,r4,#0
++ bmi CPDO_dvf_M_end_negative
++
++CPDO_dvf_M_end:
++ orrs r9,r5,r6
++ ldmia r13!,{r1,r4,r9,r10}
++ moveq pc,r14
++
++ adds r6,r6,r6
++ adcs r5,r5,r5
++ movcs r5,#0xc0000000
++ movcs pc,r14
++
++ cmp r5,r7
++ cmpeq r6,r8
++ movcc r5,#0x40000000
++ moveq r5,#0x80000000
++ movhi r5,#0xc0000000
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_rdf
++CPDO_rdf:
++ ldmia r1,{r6,r7,r8,r11}
++ ldmia r2,{r1,r2,r3,r4}
++ b CPDO_dvf_l
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_rdf_M
++CPDO_rdf_M:
++ ldmia r1,{r6,r7,r8,r11}
++ ldmia r2,{r1,r2,r3,r4}
++ b CPDO_dvf_M_l
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_rmf
++CPDO_rmf:
++ ldmia r2,{r6,r7,r8,r11}
++ ldmia r1,{r1,r2,r3,r4}
++
++ cmp r11,#0x7fffffff
++ cmpne r4,#0x7fffffff
++ beq CPDO_rmf_infnan
++ cmp r11,#0x80000000
++ cmpne r4,#0x80000000
++ beq CPDO_rmf_zero
++
++ cmp r4,r11
++ bge CPDO_rmf_loop_entry
++ b CPDO_rmf_smaller
++
++CPDO_rmf_loop_0:
++ mov r5,#0
++CPDO_rmf_loop:
++ cmp r4,r11
++ ble CPDO_rmf_loop_end
++
++ sub r4,r4,#1
++
++ adds r3,r3,r3
++ adcs r2,r2,r2
++ bcs CPDO_rmf_loop_anyway
++
++CPDO_rmf_loop_entry:
++ cmp r2,r7
++ cmpeq r3,r8
++ bcc CPDO_rmf_loop_0
++
++CPDO_rmf_loop_anyway:
++ subs r3,r3,r8
++ sbc r2,r2,r7
++ mov r5,#1
++ b CPDO_rmf_loop
++
++CPDO_rmf_loop_end:
++ teq r2,#0
++ teqeq r3,#0
++ beq CPDO_rmf_created_zero
++
++ //eor r1,r1,r6 // only if result not zero
++
++ mov r6,r2,lsr#31
++ mov r11,r2,lsl#1
++ orr r11,r11,r3,lsr#31
++
++ cmp r6,#0
++ cmpeq r11,r7
++ rsbeqs r6,r8,r3,lsl#1
++ cmpeq r5,#1 // for nearest-even
++ bcc CPDO_rmf_norm
++
++ eor r1,r1,#0x80000000
++ subs r3,r8,r3
++ sbc r2,r7,r2
++
++CPDO_rmf_norm:
++ teq r2,#0 // normalize 32 bit
++ moveq r2,r3
++ moveq r3,#0
++ subeq r4,r4,#32
++
++ cmp r2,#0x00010000 // normalize 16 bit
++ movcc r2,r2,lsl#16
++ orrcc r2,r2,r3,lsr#16
++ movcc r3,r3,lsl#16
++ subcc r4,r4,#16
++
++ cmp r2,#0x01000000 // normalize 8 bit
++ movcc r2,r2,lsl#8
++ orrcc r2,r2,r3,lsr#24
++ movcc r3,r3,lsl#8
++ subcc r4,r4,#8
++
++ cmp r2,#0x10000000 // normalize 4 bit
++ movcc r2,r2,lsl#4
++ orrcc r2,r2,r3,lsr#28
++ movcc r3,r3,lsl#4
++ subcc r4,r4,#4
++
++ cmp r2,#0x40000000 // normalize 2 bit
++ movcc r2,r2,lsl#2
++ orrcc r2,r2,r3,lsr#30
++ movcc r3,r3,lsl#2
++ subcc r4,r4,#2
++
++ cmp r2,#0x80000000 // normalize 1 bit
++ movcc r2,r2,lsl#1
++ orrcc r2,r2,r3,lsr#31
++ movcc r3,r3,lsl#1
++ subcc r4,r4,#1
++
++ mov r5,#0
++ mov pc,r14
++
++CPDO_rmf_created_zero:
++ mov r4,#0x80000000
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_rmf_smaller:
++ add r5,r4,#1
++ cmp r5,r11
++ blt CPDO_rmf_norm
++ cmp r2,r7
++ cmpeq r3,r8
++ bls CPDO_rmf_norm
++
++ eor r1,r1,#0x80000000
++ adds r8,r8,r8
++ adc r7,r7,r7
++ subs r3,r8,r3
++ sbc r2,r7,r2
++ b CPDO_rmf_norm
++
++CPDO_rmf_zero:
++ cmp r11,#0x80000000
++ beq CPDO_rmf_generate_qnan
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++@ mov pc,r14
++
++CPDO_rmf_infnan:
++ cmp r4,#0x7fffffff
++ beq CPDO_rmf_1st_infnan
++
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ beq CPDO_rmf_2nd_inf
++ mov r1,r6
++ mov r2,r7
++ mov r3,r8
++ mov r4,r11
++ b CPDO_rmf_1st_or_2nd_nan
++
++CPDO_rmf_2nd_inf:
++ mov pc,r14 // result = 1st operand
++
++CPDO_rmf_1st_infnan:
++ cmp r11,#0x7fffffff
++ beq CPDO_rmf_both_infnan
++
++ orrs r5,r3,r2,lsl#1 // 1st inf?
++ bne CPDO_rmf_1st_or_2nd_nan
++
++ b CPDO_rmf_generate_qnan
++
++CPDO_rmf_1st_or_2nd_nan:
++ tst r2,#0x40000000
++ beq CPDO_rmf_generate_qnan
++ mov pc,r14 // qnan1/2 already/copied there
++
++CPDO_rmf_both_infnan:
++ orrs r5,r3,r2,lsl#1 // ignore MSB
++ beq CPDO_rmf_both_infnan_1st_inf
++ orrs r5,r8,r7,lsl#1 // ignore MSB
++ beq CPDO_rmf_both_infnan_2nd_inf
++ tst r2,#0x40000000
++ tstne r7,#0x40000000
++ beq CPDO_rmf_generate_qnan
++ mov pc,r14
++
++CPDO_rmf_both_infnan_1st_inf:
++ tst r7,#0x40000000 // 2nd inf or SNaN ?
++ beq CPDO_rmf_generate_qnan
++ mov r1,r6
++ mov r2,r7
++ mov r3,r8
++ mov r4,r11 // copy 2nd QNaN
++ mov pc,r14
++
++CPDO_rmf_both_infnan_2nd_inf:
++ tst r2,#0x40000000 // 1st SNaN ?
++ beq CPDO_rmf_generate_qnan
++ mov pc,r14
++
++CPDO_rmf_generate_qnan:
++ mov r1,#0x80000000
++ mov r2,#0x7fffffff
++ mov r3,#0xffffffff
++ mov r4,#0x7fffffff
++ ldr r5,[r10,#128]
++ orr r5,r5,#1
++ str r5,[r10,#128]
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_mvf
++CPDO_mvf:
++ ldmia r2,{r1,r2,r3,r4}
++ mov r5,#0
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_mnf
++CPDO_mnf:
++ ldmia r2,{r1,r2,r3,r4}
++ eor r1,r1,#0x80000000
++ mov r5,#0
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++ .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
++ adc r2,r2,r2
++ cmp r2,#0x20000000 @set carry for 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
++ ldr r6,[r10,#128]
++ stmia r0,{r1,r2,r3,r5}
++ orr r6,r6,r4
++ str r6,[r10,#128]
++ b fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDO_rnd_core
++CPDO_rnd_core:
++ and r6,r4,#0x00000060
++ mov r4,#0 // for return of exception flags
++ cmp r5,#63
++ bge CPDO_rnd_big
++ add pc,pc,r6,lsr#3
++ mov r0,r0
++ b CPDO_rnd_NE
++ b CPDO_rnd_P
++ b CPDO_rnd_M
++ b CPDO_rnd_Z
++
++CPDO_rnd_NE:
++ cmp r5,#0
++ blt CPDO_rnd_NE_01
++
++ subs r6,r5,#31
++ bpl CPDO_rnd_NE_2
++ mov r7,#0x40000000
++ mov r8,#0x7fffffff
++ mov r7,r7,lsr r5
++ mov r8,r8,lsr r5
++ teq r3,#0
++ tsteq r2,r8
++ orrne r4,r4,#16 // set inexact flag
++ adds r2,r2,r7
++ bcs CPDO_rnd_overflow
++ teq r3,#0
++ tsteq r2,r8
++ beq CPDO_rnd_NE_equal
++ mov r3,#0
++ bic r2,r2,r8
++ mov pc,r14
++
++CPDO_rnd_NE_2:
++ mov r7,#0x80000000
++ mov r8,#0xffffffff
++ mov r7,r7,lsr r6
++ mov r8,r8,lsr r6
++ tst r3,r8
++ orrne r4,r4,#16 // set inexact flag
++ adds r3,r3,r7
++ adcs r2,r2,#0
++ bcs CPDO_rnd_overflow
++ tst r3,r8
++ beq CPDO_rnd_NE_equal
++ bic r3,r3,r8
++ mov pc,r14
++
++CPDO_rnd_NE_equal:
++ mov r7,#0x80000000
++ subs r6,r5,#32
++ bicpl r3,r3,r7,lsr r6
++ bicmi r2,r2,r7,lsr r5
++ mov pc,r14
++
++CPDO_rnd_NE_01:
++ cmp r5,#-1
++ bne CPDO_rnd_0
++ cmp r2,#0x80000000
++ cmpeq r3,#0
++ beq CPDO_rnd_0
++
++ mov r2,#0x80000000
++ mov r3,#0
++ mov r5,#0
++ orr r4,r4,#16 // set inexact flag
++ mov pc,r14
++
++CPDO_rnd_P:
++ teq r1,#0
++ beq CPDO_rnd_NZ
++ b CPDO_rnd_Z
++
++CPDO_rnd_M:
++ teq r1,#0
++ beq CPDO_rnd_Z
++ b CPDO_rnd_NZ
++
++CPDO_rnd_Z:
++ cmp r5,#0 // smaller than 1 will be 0
++ blt CPDO_rnd_0
++
++ rsbs r6,r5,#31
++ bmi CPDO_rnd_Z_2
++ cmp r3,#0
++ mov r3,#0
++ mov r7,r2,lsr r6
++ teqeq r2,r7,lsl r6
++ mov r2,r7,lsl r6
++ orrne r4,r4,#16 // set inexact flag
++ mov pc,r14
++
++CPDO_rnd_Z_2:
++ rsb r6,r5,#63
++ mov r7,r3,lsr r6
++ teq r3,r7,lsl r6
++ mov r3,r7,lsl r6
++ orrne r4,r4,#16 // set inexact flag
++ mov pc,r14
++
++CPDO_rnd_0:
++ cmp r5,#0x80000000
++ moveq pc,r14 // already 0 -> ok
++
++ mov r2,#0
++ mov r3,#0
++ mov r5,#0x80000000
++ orr r4,r4,#16 // set inexact flag
++ mov pc,r14
++
++CPDO_rnd_NZ:
++ cmp r5,#0 // smaller than 1 will be stay 0 or become 1
++ blt CPDO_rnd_NZ_01
++
++ mov r7,#0x7fffffff
++ subs r6,r5,#32
++ bpl CPDO_rnd_NZ_2
++ mov r7,r7,lsr r5
++ teq r3,#0
++ tsteq r2,r7
++ orrne r4,r4,#16 // set inexact flag
++ adds r3,r3,#0xffffffff
++ adcs r2,r2,r7
++ bcs CPDO_rnd_overflow
++ mov r3,#0
++ bic r2,r2,r7
++ mov pc,r14
++
++CPDO_rnd_NZ_2:
++ mov r7,r7,lsr r6
++ tst r3,r7
++ orrne r4,r4,#16 // set inexact flag
++ adds r3,r3,r7
++ adcs r2,r2,#0
++ bcs CPDO_rnd_overflow
++ bic r3,r3,r7
++ mov pc,r14
++
++CPDO_rnd_NZ_01:
++ cmp r5,#0x80000000
++ moveq pc,r14 // already 0 -> ok
++
++ mov r2,#0x80000000
++ mov r3,#0
++ mov r5,#0
++ orr r4,r4,#16 // set inexact flag
++ mov pc,r14
++
++CPDO_rnd_overflow:
++ mov r2,#0x80000000
++ mov r3,#0
++ add r5,r5,#1
++ mov pc,r14
++
++CPDO_rnd_big:
++ cmp r5,#0x7fffffff
++ movne pc,r14 // just big
++ orrs r6,r3,r2,lsl#1 // ignore MSB
++ moveq pc,r14 // infinity
++ tst r2,#0x40000000 // signalling NaN ?
++ orreq r4,r4,#1 // set invalid operation flag
++ orreq r2,r2,#0x40000000 // make quiet NaN
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
+diff -urN kernel-source-2.4.27-8/arch/arm/fastfpe/CPDT.S kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/CPDT.S
+--- kernel-source-2.4.27-8/arch/arm/fastfpe/CPDT.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/CPDT.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,396 @@
++/*
++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 mantissa is 0, otherwise it is a NaN.
++
++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#8
++ bics r3,r3,#0x80000000
++ orreq r3,r3,#0x80000000 // set MSB for inf
++ 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
++ bne CPDT_ld_e0_norm_end
++ sub r5,r5,#1
++ movs r4,r4,lsl#1
++ adc r3,r3,r3
++ b CPDT_ld_e0_norm
++CPDT_ld_e0_norm_end:
++ stmia r0,{r2-r5}
++ b fastfpe_next
++
++CPDT_ld_e2047:
++ mov r3,r1,lsl#11
++ orr r3,r3,r6,lsr #21
++ bic r3,r3,#0x80000000
++ mov r4,r6,lsl#11 @ r3,r4 = mantissa
++ orrs r5,r3,r4
++ orreq r3,r3,#0x80000000 // set MSB fo inf
++ 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,#0x8000
++ mov r2,r2,lsl#16
++ mov r5,r1,lsl#17
++ movs r5,r5,lsr#17
++ beq CPDT_le_e0
++ add r1,r5,#1
++ teq r1,#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 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
++
++ 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_e0:
++ cmp r4,#-150
++ ble CPDT_ss_zero
++
++ add r4,r4,#126
++ rsb r4,r4,#0
++ mov r2,r2,lsr r4
++
++ orr r1,r1,r2,lsr#8
++
++CPDT_ss_zero:
++ str r1,[r6]
++ b fastfpe_next
++
++CPDT_ss_e255:
++ orr r1,r1,#0x7f000000
++ orr r1,r1,#0x00800000
++ cmp r4,#0x7fffffff
++ movne r2,#0
++ movne r3,#0
++ bic r2,r2,#0x80000000
++ orrs r4,r3,r2,lsl#24 // only bits not stored in single
++ bne CPDT_ss_nan_special // NaN must not become Inf
++CPDT_ss_nan_back:
++ orr r1,r1,r2,lsr#8
++ str r1,[r6]
++ b fastfpe_next
++
++CPDT_ss_nan_special:
++ cmp r2,#1<<8
++ movlt r2,#1<<8
++ b CPDT_ss_nan_back
++
++/*---------------------------------------------------------------------------*/
++
++ .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
++
++ 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_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:
++ orr r1,r1,#0x7f000000
++ orr r1,r1,#0x00f00000
++ cmp r4,#0x7fffffff
++ movne r2,#0
++ movne r3,#0
++ movs r5,r3,lsl#21 // only bits not stored in double !
++ bne CPDT_sd_nan_special
++CPDT_sd_nan_back:
++ 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_nan_special:
++ bics r2,r2,#0x80000000
++ bne CPDT_sd_nan_back
++ cmp r3,#1<<11
++ movlt r3,#1<<11
++ b CPDT_sd_nan_back
++
++/*---------------------------------------------------------------------------*/
++
++ .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,r4,r1,lsr#16
++
++ 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
++
++ mov r1,r1,lsr#16
++ stmia r6,{r1-r3}
++ b fastfpe_next
++
++CPDT_se_zero:
++ mov r1,r1,lsr#16
++ mov r2,#0
++ mov r3,#0
++ stmia r6,{r1-r3}
++ b fastfpe_next
++
++CPDT_se_e32767:
++ cmp r4,#0x7fffffff
++ movne r2,#0
++ movne r3,#0
++ mov r1,r1,lsr#16
++ 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_loop:
++ add r0,r0,#1<<12
++ and r0,r0,#7<<12
++CPDT_sfm:
++ add r7,r10,r0,lsr#8
++ ldmia r7,{r2-r5}
++ bic r3,r3,#0x80000000
++ orr r3,r3,r2
++ stmia r6!,{r3-r5}
++
++ subs r1,r1,#1
++ bne CPDT_sfm_loop
++ b fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPDT_lfm
++CPDT_lfm_loop:
++ add r0,r0,#1<<12
++ and r0,r0,#7<<12
++CPDT_lfm:
++ add r7,r10,r0,lsr#8
++ ldmia r6!,{r3-r5}
++ and r2,r3,#0x80000000
++ cmp r5,#0x80000000 // check if the number was 0
++ cmpne r5,#0x7fffffff // or inf/NaN
++ biceq r3,r3,#0x80000000 // yes -> clear mantissa MSB
++ orrne r3,r3,#0x80000000 // no -> set mantissa MSB
++ stmia r7,{r2-r5}
++
++ subs r1,r1,#1
++ bne CPDT_lfm_loop
++ b fastfpe_next
++
++/*---------------------------------------------------------------------------*/
+diff -urN kernel-source-2.4.27-8/arch/arm/fastfpe/CPRT.S kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/CPRT.S
+--- kernel-source-2.4.27-8/arch/arm/fastfpe/CPRT.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/CPRT.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,220 @@
++/*
++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 mantissa is 0, otherwise it is a NaN.
++
++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 r0,r1
++ mov r3,#0
++ cmp r2,#0
++ beq CPRT_flt_zero
++
++ ldr r6,=round_table
++ and r5,r4,#0x000000e0
++ and r4,r4,#0x00080000
++ orr r5,r5,r4,lsr#11
++ ldr r6,[r6,r5,lsr#3] // address of rounding function
++
++ ands r1,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
++
++ mov r5,#0
++ ldr r14,=fastfpe_next
++ mov pc,r6
++
++CPRT_flt_zero:
++ mov r1,#0
++ mov r4,#0x80000000
++ stmia r0,{r1,r2,r3,r4}
++ b fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPRT_fix
++CPRT_fix:
++ ldmia r2,{r1,r2,r3,r5}
++ bl CPDO_rnd_core
++
++ add r0,r13,r0,lsr#10
++ cmp r5,#0
++ blt CPRT_fix_zero
++ cmp r5,#30
++ bgt CPRT_fix_overflow
++
++CPRT_fix_no_overflow:
++ rsb r5,r5,#31
++ mov r2,r2,lsr r5
++ tst r1,#0x80000000
++ rsbne r2,r2,#0
++CPRT_fix_zero_back:
++ str r2,[r0]
++ ldr r1,[r10,#128]
++ orr r1,r1,r4 // set flags possibly caused by rounding
++ str r1,[r10,#128]
++ b fastfpe_next
++
++CPRT_fix_zero:
++ mov r2,#0
++ b CPRT_fix_zero_back
++
++CPRT_fix_overflow:
++ cmp r1,#0x80000000 // -2^31 is not exactly an overflow ...
++ cmpeq r2,#0x80000000
++ cmpeq r5,#31
++ beq CPRT_fix_no_overflow
++
++ mov r2,#0x80000000
++ tst r1,#0x80000000
++ subeq r2,r2,#1
++ str r2,[r0]
++
++ ldr r1,[r10,#128]
++ orr r1,r1,#1 // set invalid operation flag
++ str r1,[r10,#128]
++ b fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPRT_wfs
++CPRT_wfs:
++ ldr r0,[r13,r0,lsr#10]
++ str r0,[r10,#128]
++ b fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++ .globl CPRT_rfs
++CPRT_rfs:
++ ldr r1,[r10,#128]
++ bic r1,r1,#0xff000000
++ orr r1,r1,#0x02000000 @ Software Emulation, not Acorn FPE
++ str r1,[r13,r0,lsr#10]
++ 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]
++ bic r0,r0,#0xf0000000
++
++ cmp r7,#0x7fffffff
++ beq CPRT_cmf_nan1
++CPRT_cmf_nixnan1:
++ cmp r8,#0x7fffffff
++ beq CPRT_cmf_nan2
++CPRT_cmf_nixnan2:
++
++ cmp r1,r2
++ beq CPRT_cmf_equalsign
++ b CPRT_cmf_signx
++
++CPRT_cmf_nan1:
++ orrs r11,r5,r3,lsl#1 // ignore MSB
++ beq CPRT_cmf_nixnan1
++ b CPRT_cmf_unordered
++
++CPRT_cmf_nan2:
++ orrs r11,r6,r4,lsl#1 // ignore MSB
++ beq CPRT_cmf_nixnan2
++ b CPRT_cmf_unordered
++
++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_signx:
++ teq r7,#0x80000000
++ teqeq r8,#0x80000000
++ beq CPRT_cmf_equal
++CPRT_cmf_sign:
++ tst r1,#0x80000000
++ orreq r0,r0,#0x20000000 // PSR carry
++ orrne r0,r0,#0x80000000 // PSR negative
++ str r0,[r13,#16*4]
++ b fastfpe_next
++
++CPRT_cmf_signb:
++ tst r1,#0x80000000
++ orrne r0,r0,#0x20000000 // PSR carry
++ orreq r0,r0,#0x80000000 // PSR negative
++ str r0,[r13,#16*4]
++ b fastfpe_next
++
++CPRT_cmf_equal:
++ orr r0,r0,#0x60000000 // PSR carry, zero
++ str r0,[r13,#16*4]
++ b fastfpe_next
++
++CPRT_cmf_unordered:
++ ldr r1,[r10,#128]
++ orr r1,r1,#1 // set invalid operation flag
++ str r1,[r10,#128]
++
++ tst r0,#1<<12 // FPSR AC bit set ?
++ orrne r0,r0,#0x20000000 // PSR carry
++ orr r0,r0,#0x10000000 // PSR overflow
++ 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 kernel-source-2.4.27-8/arch/arm/fastfpe/Makefile kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/Makefile
+--- kernel-source-2.4.27-8/arch/arm/fastfpe/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/Makefile 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,27 @@
++#
++# linux/arch/arm/fastfpe/Makefile
++#
++# Copyright (C) Peter Teichmann
++#
++
++O_TARGET := fast-math-emu.o
++
++AFLAGS_CPDO.o := -march=armv3m
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj- :=
++
++fastfpe-objs := module.o round.o CPDT.o CPRT.o CPDO.o entry.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 kernel-source-2.4.27-8/arch/arm/fastfpe/entry.S kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/entry.S
+--- kernel-source-2.4.27-8/arch/arm/fastfpe/entry.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/entry.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,339 @@
++/*
++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)
++
++*/
++
++#include <asm/procinfo.h>
++
++/*---------------------------------------------------------------------------*/
++
++ .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_false:
++__x1:
++ ldrt r4,[r5],#4
++
++ ldr r0,=fp_cond @ check condition of next instruction
++ mov r2,r4,lsr#28
++ cmp r2,#0xe @ "always" condition code
++ bne next_check_cond
++
++next_check_copro:
++ 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
++ cmp r1,#1<<8 @ which copro ?
++ beq copro_1
++ b copro_2
++
++next_check_cond:
++ ldr r1,[r13,#64] @ psr containing flags
++ ldr r0,[r0,r2,lsl#2]
++ mov r1,r1,lsr#28
++ mov r0,r0,lsr r1
++ tst r0,#1
++ bne next_check_copro
++ b next_after_cond_false @ must not necessarily have been an
++ @ FP instruction !
++
++/*---------------------------------------------------------------------------*/
++
++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
++
++ and r1,r4,#0x00008000 @ T0
++ tst r4,#0x00400000
++ orrne r1,r1,#0x00010000 @ T1
++ tst r4,#0x00100000
++ orrne r1,r1,#0x00020000 @ L/S
++
++ ldr pc,[pc,r1,lsr#13]
++ .word 0
++ .word CPDT_store_single @ these functions get
++ .word CPDT_store_double @ r0=address of fp register
++ .word CPDT_store_extended @ r6=address of data
++ .word undefined @ CPDT_store_decimal
++ .word CPDT_load_single
++ .word CPDT_load_double
++ .word CPDT_load_extended
++ .word 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:
++ ldr r3,=round_table
++ and r5,r4,#0x000000e0
++ and r6,r4,#0x00080000
++ orr r5,r5,r6,lsr#11 @ r5=containing rounding mode/precision
++ ldr r14,[r3,r5,lsr#3] @ r14=address of rounding function
++ and r3,r4,#0x00f00000
++ tst r4,#0x00008000
++ orrne r3,r3,#0x01000000 @ r3=operation code
++
++ ldr pc,[pc,r3,lsr#18]
++ .word 0
++CPDO_table:
++ .word CPDO_adf
++ .word CPDO_muf
++ .word CPDO_suf
++ .word CPDO_rsf
++ .word CPDO_dvf
++ .word CPDO_rdf
++ .word undefined
++ .word undefined
++ .word CPDO_rmf
++ .word CPDO_muf
++ .word CPDO_dvf
++ .word CPDO_rdf
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word CPDO_mvf
++ .word CPDO_mnf
++ .word CPDO_abs
++ .word CPDO_rnd
++ .word CPDO_sqt
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word CPDO_rnd
++ .word 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
++
++ ldr pc,[pc,r3,lsr#18]
++ .word 0
++ .word CPRT_flt
++ .word CPRT_fix
++ .word CPRT_wfs
++ .word CPRT_rfs
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word undefined
++ .word CPRT_cmf
++ .word undefined
++ .word CPRT_cnf
++ .word undefined
++ .word CPRT_cmf
++ .word undefined
++ .word CPRT_cnf
++
++CPRT_const:
++ ldr r2,=fp_const
++ and r3,r4,#0x00000007
++ add r2,r2,r3,lsl#4
++ b CPRT_constback
++
++/*---------------------------------------------------------------------------*/
++
++ @ Test if long multiply instructions are available
++
++ .globl fastfpe_test
++fastfpe_test:
++ .globl elf_hwcap
++ ldr r0,=elf_hwcap
++ ldr r0,[r0]
++ tst r0,#HWCAP_FAST_MULT
++ bne fastfpe_has_long_multiply
++ mov r0,#0
++ mov pc,r14
++
++fastfpe_has_long_multiply:
++ adr r0,CPDO_table
++ ldr r1,=CPDO_muf_M
++ str r1,[r0,#1*4] @ muf
++ str r1,[r0,#9*4] @ fml
++ ldr r1,=CPDO_dvf_M
++ str r1,[r0,#4*4] @ dvf
++ str r1,[r0,#10*4] @ fdv
++ ldr r1,=CPDO_rdf_M
++ str r1,[r0,#5*4] @ rdf
++ str r1,[r0,#11*4] @ frd
++ mov r0,#1
++ mov pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++ @ 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 kernel-source-2.4.27-8/arch/arm/fastfpe/module.c kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/module.c
+--- kernel-source-2.4.27-8/arch/arm/fastfpe/module.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/module.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,64 @@
++/*
++ Fast Floating Point Emulator
++ (c) Peter Teichmann <mail at peter-teichmann.de>
++
++ 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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++
++#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 */
++extern int fastfpe_test(void); /* long multiply available ? */
++
++static int __init fpe_init(void)
++{
++ if (fpe_type[0] && strcmp(fpe_type, "fastfpe"))
++ return 0;
++
++ printk("Fast Floating Point Emulator V0.94");
++ if (fastfpe_test() == 1) printk("M");
++ printk(" by 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 <mail at peter-teichmann.de>");
++MODULE_DESCRIPTION("Fast floating point emulator with full precision");
+diff -urN kernel-source-2.4.27-8/arch/arm/fastfpe/round.S kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/round.S
+--- kernel-source-2.4.27-8/arch/arm/fastfpe/round.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/fastfpe/round.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,912 @@
++
++/*
++Rounds fp register r1-r4, additional mantissa bits in r5 and stores result
++at address r0. Returns to fastfpe_next.
++*/
++
++/*------------------------------------------------------------------------*/
++
++ .data
++ .globl round_table
++round_table:
++ .word round_single_ne
++ .word round_single_p
++ .word round_single_m
++ .word round_single_z
++ .word round_double_ne
++ .word round_double_p
++ .word round_double_m
++ .word round_double_z
++ .word round_extended_ne
++ .word round_extended_p
++ .word round_extended_m
++ .word round_extended_z
++ .word round_undef
++ .word round_undef
++ .word round_undef
++ .word round_undef
++
++/*------------------------------------------------------------------------*/
++
++ .text
++round_single_ne:
++ cmp r4,#127
++ bgt round_single_nz_ne_overflow
++ cmp r4,#-126-23-1
++ blt round_single_z_ne_underflow
++ cmp r4,#-126
++ blt round_single_ne_denormalized
++
++ adds r6,r2,#0x80 // add 0x80.00000000.00000000 to
++ bcs round_single_add_ov // mantissa and additional bits
++
++ teq r5,#0
++ teqeq r3,#0
++ tsteq r2,#0xff // test for inexact
++
++ ldrne r7,[r10,#128]
++ orrne r7,r7,#16 // set inexact flag
++ strne r7,[r10,#128]
++
++ teq r5,#0
++ teqeq r3,#0
++ tsteq r6,#0xff
++ biceq r6,r6,#0x100 // the even thingy
++
++ mov r3,#0 // remove bits not existing in single
++ bic r2,r6,#0xff // remove bits not existing in single
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_ne_denormalized:
++ add r7,r4,#150
++ mov r6,#0xffffffff
++ mov r6,r6,lsr r7
++
++ teq r5,#0
++ teqeq r3,#0
++ tsteq r2,r6
++ ldrne r8,[r10,#128]
++ orrne r8,r8,#16+8 // set inexact, underflow flag
++ strne r8,[r10,#128]
++
++ mov r8,#0x80000000
++ mov r8,r8,lsr r7
++ adds r2,r2,r8
++ bcs round_single_ne_denormalized_ov
++
++ teq r5,#0
++ teqeq r3,#0
++ tsteq r2,r6
++ biceq r2,r2,r8,lsl #1 // the even thingy
++
++ mov r3,#0
++ bic r2,r2,r6 // removing bits not existing in single
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_ne_denormalized_ov:
++ cmp r4,#-150
++ cmpeq r3,#0
++ cmpeq r2,#0
++ beq round_single_z_ne_underflow // 1.0*2^-150 to zero!
++ add r4,r4,#1
++ cmp r4,#-126 // left denormalized range ?
++ cmpge r2,#0x80 // yes -> overflow also without denormalisation ?
++ ldrge r5,[r10,#128]
++ bicge r5,r5,#8 // yes -> clear underflow flag
++ strge r5,[r10,#128]
++ mov r3,#0
++ mov r2,#0x80000000
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_single_p:
++ teq r1,#0
++ beq round_single_nz
++ b round_single_z
++
++/*------------------------------------------------------------------------*/
++
++round_single_m:
++ teq r1,#0
++ beq round_single_z
++ b round_single_nz
++
++/*------------------------------------------------------------------------*/
++
++round_single_z:
++ cmp r4,#127
++ bgt round_single_z_overflow
++ cmp r4,#-126-23
++ blt round_single_z_ne_underflow
++ cmp r4,#-126
++ blt round_single_z_denormalized
++
++ teq r5,#0
++ teqeq r3,#0
++ tsteq r2,#0xff // testing for inexact
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16 // set inexact flag
++ strne r5,[r10,#128]
++
++ mov r3,#0
++ bic r2,r2,#0xff // removing bits not existing in single
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_z_overflow:
++ cmp r4,#0x7fffffff
++ beq round_single_infnan
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+4 // set inexact,overflow flag
++ strne r5,[r10,#128]
++ mov r2,#0xffffff00
++ mov r3,#0
++ mov r4,#127 // biggest non-infinity single
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_infnan:
++ orrs r5,r3,r2,lsl#1 // is it Inf? ignore MSB
++ beq round_single_infnan_store
++ tst r2,#0x40000000 // is it a SNaN?
++ beq round_single_infnan_create_qnan
++ mov r3,#0 // these bits can not be stored
++ bic r2,r2,#0xff // in single precision
++round_single_infnan_store:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_infnan_create_qnan:
++ mov r1,#0x80000000
++ mov r2,#0xffffff00
++ bic r2,r2,#0x80000000 // r2 = 0x7fffff00
++ mov r3,#0
++ ldr r5,[r10,#128]
++ orr r5,r5,#1 // set invalid operation flag
++ str r5,[r10,#128]
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_z_ne_underflow:
++ cmp r4,#0x80000000
++ beq round_single_z_zero
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+8 // set inexact, underflow flag
++ strne r5,[r10,#128]
++ mov r2,#0
++ mov r3,#0
++ mov r4,#0x80000000 // was by ERROR -127
++round_single_z_zero:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_z_denormalized:
++ mov r6,#0xffffffff
++ add r7,r4,#150
++
++ teq r5,#0
++ teqeq r3,#0
++ tsteq r2,r6,lsr r7 // testing for tinyness
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+8 // set inexact, undeflow flag
++ strne r5,[r10,#128]
++
++ mov r3,#0
++ bic r2,r2,r6,lsr r7 // removing bits not existing in single
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_single_nz:
++ cmp r4,#127
++ bgt round_single_nz_ne_overflow
++ cmp r4,#-126-23
++ blt round_single_nz_underflow
++ cmp r4,#-126
++ blt round_single_nz_denormalized
++
++ adds r5,r5,#0xffffffff
++ adcs r3,r3,#0xffffffff // add 0xff.ffffffff.ffffffff to
++ adcs r2,r2,#0xff // mantissa and additional bits
++ bcs round_single_add_ov
++
++ cmp r5,#0xffffffff
++ cmpeq r3,#0xffffffff
++ andeq r5,r2,#0xff
++ cmpeq r5,#0xff // test for inexact
++
++ bic r2,r2,#0xff // remove bits not existing in single
++
++round_single_add_ov_back:
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16 // set inexact flag
++ strne r5,[r10,#128]
++
++ mov r3,#0 // remove bits not existing in single
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_add_ov:
++ add r4,r4,#1
++ cmp r4,#127
++ bgt round_single_nz_ne_overflow
++ movs r2,#0x80000000 // so that inexact flag gets set !!!
++ b round_single_add_ov_back
++
++round_single_nz_ne_overflow:
++ cmp r4,#0x7fffffff
++ beq round_single_infnan
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+4 // set inexact,overflow flag
++ strne r5,[r10,#128]
++ mov r2,#0x80000000 // set MSB
++ mov r3,#0
++ mov r4,#0x7fffffff
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_nz_underflow:
++ cmp r4,#0x80000000
++ beq round_single_nz_zero
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+8 // set inexact, underflow flag
++ strne r5,[r10,#128]
++ mov r2,#0x80000000
++ mov r3,#0
++ mov r4,#-149 // smallest non-zero single
++round_single_nz_zero:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_nz_denormalized:
++ mov r6,#0xffffffff
++ add r7,r4,#150
++ mov r6,r6,lsr r7
++
++ teq r5,#0
++ teqeq r3,#0
++ tsteq r2,r6
++ ldrne r8,[r10,#128]
++ orrne r8,r8,#16+8 // set inexact, underflow flag
++ strne r8,[r10,#128]
++
++ adds r5,r5,#0xffffffff
++ adcs r3,r3,#0xffffffff
++ adcs r2,r2,r6
++ bcs round_single_nz_denormalized_ov
++
++ mov r3,#0
++ bic r2,r2,r6 // removing bits not existing in single
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_single_nz_denormalized_ov:
++ add r4,r4,#1
++ cmp r4,#-126 // left denormalized range ?
++ cmpge r2,#0x100 // yes -> overflow also without denormalisation ?
++ ldrge r5,[r10,#128]
++ bicge r5,r5,#8 // yes -> clear underflow flag
++ strge r5,[r10,#128]
++ mov r3,#0
++ mov r2,#0x80000000
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_double_ne:
++ mov r7,#0xffffffff // to generate e.g. 0x7ff
++
++ cmp r4,#1024
++ bge round_double_nz_ne_overflow
++ add r6,r4,#1024
++ cmp r6,#-1022+1024
++ blt round_double_ne_denormalized
++
++ teq r5,#0
++ tsteq r3,r7,lsr#32-11 // testing for inexact
++ ldrne r6,[r10,#128]
++ orrne r6,r6,#16 // set inexact flag
++ strne r6,[r10,#128]
++
++ adds r3,r3,#0x400 // add 0x0.00000400.00000000 to
++ adcs r2,r2,#0 // mantissa and additional bits
++ bcs round_double_add_ov
++
++ teq r5,#0
++ tsteq r3,r7,lsr#32-11
++ biceq r3,r3,#0x800 // the even thingy
++
++ bic r3,r3,r7,lsr#32-11 // remove bits not existing in double
++
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_ne_denormalized:
++ cmp r6,#-1022-52-1+1024
++ blt round_double_z_ne_underflow
++
++ adds r6,r6,#1022+53-32-1024
++
++ addmi r6,r6,#32
++ movmi r6,r7,lsr r6
++
++ movpl r7,r7,lsr r6
++ movpl r6,#0
++
++ teq r5,#0
++ tsteq r3,r7
++ tsteq r2,r6 // testing for tinyness
++ ldrne r8,[r10,#128]
++ orrne r8,r8,#16+8 // set inexact, undeflow flag
++ strne r8,[r10,#128]
++
++ bics r8,r6,r6,lsr#1 // generate ...0001000...
++ movne r11,#0 // from ...0001111...
++ biceq r11,r7,r7,lsr#1 // 64bit
++
++ adds r3,r3,r11
++ adcs r2,r2,r8
++ bcs round_double_ne_denormalized_ov
++
++ teq r5,#0
++ tsteq r3,r7
++ tsteq r2,r6
++ bne round_double_ne_denormalized_noeventhingy
++ adds r11,r11,r11
++ adc r8,r8,r8
++ bic r3,r3,r11
++ bic r2,r2,r8 // the even thingy
++
++round_double_ne_denormalized_noeventhingy:
++ bic r3,r3,r7 // removing bits not existing in
++ bic r2,r2,r6 // denormalized double
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_ne_denormalized_ov:
++ add r6,r4,#1024
++ cmp r6,#-1023-52+1024
++ cmpeq r3,#0
++ cmpeq r2,#0
++ beq round_single_z_ne_underflow // 1.0*2^(-1023-52) to zero!
++ add r4,r4,#1
++ cmp r6,#-1022-1+1024 // left denormalized range ?
++ cmpge r3,#0x400 // yes -> overflow also without denormalisation ?
++ ldrge r5,[r10,#128]
++ bicge r5,r5,#8 // yes -> clear underflow flag
++ strge r5,[r10,#128]
++ mov r3,#0
++ mov r2,#0x80000000
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_double_p:
++ teq r1,#0
++ beq round_double_nz
++ b round_double_z
++
++/*------------------------------------------------------------------------*/
++
++round_double_m:
++ teq r1,#0
++ beq round_double_z
++ b round_double_nz
++
++/*------------------------------------------------------------------------*/
++
++round_double_z:
++ mov r7,#0xffffffff
++
++ cmp r4,#1024
++ bge round_double_z_overflow
++ add r6,r4,#1024
++ cmp r6,#-1022+1024
++ blt round_double_z_denormalized
++
++ teq r5,#0
++ tsteq r3,r7,lsr#32-11 // testing for inexact
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16 // set inexact flag
++ strne r5,[r10,#128]
++
++ bic r3,r3,r7,lsr#32-11 // removing bits not existing in double
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_z_overflow:
++ cmp r4,#0x7fffffff
++ beq round_double_infnan
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+4 // set inexact,overflow flag
++ strne r5,[r10,#128]
++ mov r2,#0xffffffff
++ mov r3,r2,lsl#11 // 0xfffff800
++ mov r4,#1024
++ sub r4,r4,#1 // 1023; biggest non-infinity double
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_infnan:
++ orrs r5,r3,r2,lsl#1 // is it Inf? ignore MSB
++ beq round_double_infnan_store
++ tst r2,#0x40000000 // is it a SNaN?
++ beq round_double_infnan_create_qnan
++ bic r3,r3,r7,lsr#32-11 // clear bits not in double
++round_double_infnan_store:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_infnan_create_qnan:
++ mov r1,#0x80000000
++ mov r2,#0x7fffffff
++ mov r3,r2,lsl#11 // 0xfffff800
++ ldr r5,[r10,#128]
++ orr r5,r5,#1 // set invalid operation flag
++ str r5,[r10,#128]
++ b round_double_infnan_store
++
++round_double_z_ne_underflow:
++ cmp r4,#0x80000000
++ beq round_double_z_zero
++ ldr r5,[r10,#128]
++ orr r5,r5,#16+8 // set inexact, underflow flag
++ str r5,[r10,#128]
++ mov r2,#0
++ mov r3,#0
++ mov r4,#0x80000000
++round_double_z_zero:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_z_denormalized:
++ cmp r6,#-1022-52+1024
++ blt round_double_z_ne_underflow
++
++ adds r6,r6,#1022+53-32-1024
++
++ addmi r6,r6,#32
++ movmi r6,r7,lsr r6
++
++ movpl r7,r7,lsr r6
++ movpl r6,#0
++
++ teq r5,#0
++ tsteq r3,r7
++ tsteq r2,r6 // testing for tinyness
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+8 // set inexact, undeflow flag
++ strne r5,[r10,#128]
++
++ bic r3,r3,r7 // rmoving bits not existing in
++ bic r2,r2,r6 // denormalized double
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_double_nz:
++ mov r7,#0xffffffff // to generate e.g. 0x7ff
++
++ cmp r4,#1024
++ bge round_double_nz_ne_overflow
++ add r6,r4,#1024
++ cmp r6,#-1022+1024
++ blt round_double_nz_denormalized
++
++ teq r5,#0
++ tsteq r3,r7,lsr#32-11 // testing for inexact
++ ldrne r6,[r10,#128]
++ orrne r6,r6,#16 // set inexact flag
++ strne r6,[r10,#128]
++
++ adds r5,r5,#0xffffffff
++ adcs r3,r3,r7,lsr#32-11 // add 0x0.000007ff.ffffffff to
++ adcs r2,r2,#0 // mantissa and additional bits
++ bcs round_double_add_ov
++
++ bic r3,r3,r7,lsr#32-11 // remove bits not existing in double
++
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_add_ov:
++ add r4,r4,#1
++ cmp r4,#1024
++ bge round_double_nz_ne_overflow
++
++// ldrne r6,[r10,#128]
++// orrne r6,r6,#16 // set inexact flag
++// strne r6,[r10,#128]
++ mov r2,#0x80000000
++ mov r3,#0
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_nz_ne_overflow:
++ cmp r4,#0x7fffffff
++ beq round_double_infnan
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+4 // set inexact,overflow flag
++ strne r5,[r10,#128]
++ mov r2,#0x80000000 // set MSB
++ mov r3,#0
++ mov r4,#0x7fffffff
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_nz_underflow:
++ cmp r4,#0x80000000
++ beq round_double_nz_zero
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+8 // set inexact, underflow flag
++ strne r5,[r10,#128]
++ mov r2,#0x80000000
++ mov r3,#0
++ mov r4,#-1074+1024
++ sub r4,r4,#1024 // smallest non-zero double
++round_double_nz_zero:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_nz_denormalized:
++ cmp r6,#-1022-52+1024
++ blt round_double_nz_underflow
++
++ adds r6,r6,#1022+53-32-1024
++
++ addmi r6,r6,#32
++ movmi r6,r7,lsr r6
++
++ movpl r7,r7,lsr r6
++ movpl r6,#0
++
++ teq r5,#0
++ tsteq r3,r7
++ tsteq r2,r6 // testing for tinyness
++ ldrne r8,[r10,#128]
++ orrne r8,r8,#16+8 // set inexact, undeflow flag
++ strne r8,[r10,#128]
++
++ adds r5,r5,#0xffffffff
++ adcs r3,r3,r7
++ adcs r2,r2,r6
++ bcs round_double_nz_denormalized_ov
++
++ bic r3,r3,r7 // rmoving bits not existing in
++ bic r2,r2,r6 // denormalized double
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_double_nz_denormalized_ov:
++ add r4,r4,#1
++ add r6,r4,#1024
++ cmp r6,#-1022+1024 // left denormalized range ?
++ cmpge r3,#0x800 // yes -> overflow also without denormalisation ?
++ ldrge r5,[r10,#128]
++ bicge r5,r5,#8 // yes -> clear underflow flag
++ strge r5,[r10,#128]
++ mov r3,#0
++ mov r2,#0x80000000
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_extended_ne:
++ mov r7,#0xffffffff // to generate e.g. 0x7ff
++
++ cmp r4,#16384
++ bge round_extended_nz_ne_overflow
++ add r6,r4,#16384
++ cmp r6,#-16382+16384
++ blt round_extended_ne_denormalized
++
++ teq r5,#0 // testing for inexact
++ ldrne r6,[r10,#128]
++ orrne r6,r6,#16 // set inexact flag
++ strne r6,[r10,#128]
++
++ adds r5,r5,#0x80000000 // add 0x0.00000400.00000000 to
++ adcs r3,r3,#0 // mantissa and additional bits
++ adcs r2,r2,#0
++ bcs round_extended_add_ov
++
++ teq r5,#0
++ biceq r3,r3,#1 // the even thingy
++
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_ne_denormalized:
++ cmp r6,#-16382-63-1+16384
++ blt round_extended_z_ne_underflow
++
++ adds r6,r6,#16382+64-32-16384
++
++ addmi r6,r6,#32
++ movmi r6,r7,lsr r6
++
++ movpl r7,r7,lsr r6
++ movpl r6,#0
++
++ teq r5,#0
++ tsteq r3,r7
++ tsteq r2,r6 // testing for tinyness
++ ldrne r8,[r10,#128]
++ orrne r8,r8,#16+8 // set inexact, undeflow flag
++ strne r8,[r10,#128]
++
++ bics r8,r6,r6,lsr#1 // generate ...0001000...
++ movne r11,#0 // from ...0001111...
++ biceq r11,r7,r7,lsr#1 // 64bit
++
++ adds r3,r3,r11
++ adcs r2,r2,r8
++ bcs round_extended_ne_denormalized_ov
++
++ teq r5,#0
++ tsteq r3,r7
++ tsteq r2,r6
++ bne round_extended_ne_denormalized_noeventhingy
++ adds r11,r11,r11
++ adc r8,r8,r8
++ bic r3,r3,r11
++ bic r2,r2,r8 // the even thingy
++
++round_extended_ne_denormalized_noeventhingy:
++ bic r3,r3,r7 // removing bits not existing in
++ bic r2,r2,r6 // denormalized extended
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_ne_denormalized_ov:
++ add r6,r4,#16384
++ cmp r6,#-16383-63+16384
++ cmpeq r5,#0
++ cmpeq r3,#0
++ cmpeq r2,#0
++ beq round_single_z_ne_underflow // 1.0*2^(-16383-63) to zero!
++ add r4,r4,#1
++ cmp r6,#-16382-1+16384 // left denormalized range ?
++ blt round_extended_ne_still_denormalized
++ cmp r5,#0x80000000 // FIXME yes -> overflow also without denormalisation ?
++ ldrcs r5,[r10,#128]
++ biccs r5,r5,#8 // yes -> clear underflow flag
++ strcs r5,[r10,#128]
++round_extended_ne_still_denormalized:
++ mov r3,#0
++ mov r2,#0x80000000
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_extended_p:
++ teq r1,#0
++ beq round_extended_nz
++ b round_extended_z
++
++/*------------------------------------------------------------------------*/
++
++round_extended_m:
++ teq r1,#0
++ beq round_extended_z
++ b round_extended_nz
++
++/*------------------------------------------------------------------------*/
++
++round_extended_z:
++ mov r7,#0xffffffff
++
++ cmp r4,#16384
++ bge round_extended_z_overflow
++ add r6,r4,#16384
++ cmp r6,#-16382+16384
++ blt round_extended_z_denormalized
++
++ teq r5,#0 // testing for inexact
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16 // set inexact flag
++ strne r5,[r10,#128]
++
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_z_overflow:
++ cmp r4,#0x7fffffff
++ beq round_extended_infnan
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+4 // set inexact,overflow flag
++ strne r5,[r10,#128]
++ mov r2,#0xffffffff
++ mov r3,#0xffffffff
++ mov r4,#16384
++ sub r4,r4,#1 // 16383; biggest non-infinity extended
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_infnan:
++ orrs r5,r3,r2,lsl#1 // is it Inf? ignore MSB
++ beq round_extended_infnan_store
++ tst r2,#0x40000000 // is it a SNaN?
++ beq round_extended_infnan_create_qnan
++ bic r3,r3,r7,lsr#32-11 // clear bits not in extended
++round_extended_infnan_store:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_infnan_create_qnan:
++ mov r1,#0x80000000
++ mov r2,#0x7fffffff
++ mov r3,#0xffffffff
++ ldr r5,[r10,#128]
++ orr r5,r5,#1 // set invalid operation flag
++ str r5,[r10,#128]
++ b round_extended_infnan_store
++
++round_extended_z_ne_underflow:
++ cmp r4,#0x80000000
++ beq round_extended_z_zero
++ ldr r5,[r10,#128]
++ orr r5,r5,#16+8 // set inexact, underflow flag
++ str r5,[r10,#128]
++ mov r2,#0
++ mov r3,#0
++ mov r4,#0x80000000
++round_extended_z_zero:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_z_denormalized:
++ cmp r6,#-16382-63+16384
++ blt round_extended_z_ne_underflow
++
++ adds r6,r6,#16382+64-32-16384
++
++ addmi r6,r6,#32
++ movmi r6,r7,lsr r6
++
++ movpl r7,r7,lsr r6
++ movpl r6,#0
++
++ teq r5,#0
++ tsteq r3,r7
++ tsteq r2,r6 // testing for tinyness
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+8 // set inexact, undeflow flag
++ strne r5,[r10,#128]
++
++ bic r3,r3,r7 // removing bits not existing in
++ bic r2,r2,r6 // denormalized extended
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_extended_nz:
++ mov r7,#0xffffffff // to generate e.g. 0x7ff
++
++ cmp r4,#16384
++ bge round_extended_nz_ne_overflow
++ add r6,r4,#16384
++ cmp r6,#-16382+16384
++ blt round_extended_nz_denormalized
++
++ teq r5,#0 // testing for inexact
++ ldrne r6,[r10,#128]
++ orrne r6,r6,#16 // set inexact flag
++ strne r6,[r10,#128]
++
++ adds r5,r5,#0xffffffff
++ adcs r3,r3,#0 // add 0x0.0.ffffffff to
++ adcs r2,r2,#0 // mantissa and additional bits
++ bcs round_extended_add_ov
++
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_add_ov:
++ add r4,r4,#1
++ cmp r4,#16384
++ bge round_extended_nz_ne_overflow
++
++// ldrne r6,[r10,#128]
++// orrne r6,r6,#16 // set inexact flag
++// strne r6,[r10,#128]
++ mov r2,#0x80000000
++ mov r3,#0
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_nz_ne_overflow:
++ cmp r4,#0x7fffffff
++ beq round_extended_infnan
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+4 // set inexact,overflow flag
++ strne r5,[r10,#128]
++ mov r2,#0x80000000 // set MSB
++ mov r3,#0
++ mov r4,#0x7fffffff
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_nz_underflow:
++ cmp r4,#0x80000000
++ beq round_extended_nz_zero
++
++ ldrne r5,[r10,#128]
++ orrne r5,r5,#16+8 // set inexact, underflow flag
++ strne r5,[r10,#128]
++ mov r2,#0x80000000
++ mov r3,#0
++ mov r4,#-16445+16384
++ sub r4,r4,#16384 // smallest non-zero extended
++round_extended_nz_zero:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_nz_denormalized:
++ cmp r6,#-16382-63+16384
++ blt round_extended_nz_underflow
++
++ adds r6,r6,#16382+64-32-16384
++
++ addmi r6,r6,#32
++ movmi r6,r7,lsr r6
++
++ movpl r7,r7,lsr r6
++ movpl r6,#0
++
++ teq r5,#0
++ tsteq r3,r7
++ tsteq r2,r6 // testing for tinyness
++ ldrne r8,[r10,#128]
++ orrne r8,r8,#16+8 // set inexact, undeflow flag
++ strne r8,[r10,#128]
++
++ adds r5,r5,#0xffffffff
++ adcs r3,r3,r7
++ adcs r2,r2,r6
++ bcs round_extended_nz_denormalized_ov
++
++ bic r3,r3,r7 // removing bits not existing in
++ bic r2,r2,r6 // denormalized extended
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++round_extended_nz_denormalized_ov:
++ add r4,r4,#1
++ add r6,r4,#16384
++ cmp r6,#-16382+16384 // left denormalized range ?
++ cmpge r3,#1 // yes -> overflow also without denormalisation ?
++ ldrge r5,[r10,#128]
++ bicge r5,r5,#8 // yes -> clear underflow flag
++ strge r5,[r10,#128]
++ mov r3,#0
++ mov r2,#0x80000000
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_undef:
++ stmia r0,{r1-r4}
++ b fastfpe_next
++
++/*------------------------------------------------------------------------*/
+diff -urN kernel-source-2.4.27-8/arch/arm/kernel/Makefile kernel-source-2.4.27-8-arm-1/arch/arm/kernel/Makefile
+--- kernel-source-2.4.27-8/arch/arm/kernel/Makefile 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/Makefile 2005-02-18 17:48:34.000000000 +0000
+@@ -54,7 +54,7 @@
+ obj-$(CONFIG_ARCH_ACORN) += ecard.o fiq.o time-acorn.o
+ obj-$(CONFIG_ARCH_CLPS7500) += time-acorn.o
+ obj-$(CONFIG_ARCH_RISCSTATION) += time-acorn.o
+-obj-$(CONFIG_DEBUG_LL) += debug-$(PROCESSOR).o
++obj-$(CONFIG_DEBUG_LL) += debug-$(PROCESSOR).o debug.o
+ obj-$(CONFIG_MODULES) += armksyms.o
+ obj-$(CONFIG_ARTHUR) += arthur.o
+ obj-$(CONFIG_ISA_DMA) += dma-isa.o
+diff -urN kernel-source-2.4.27-8/arch/arm/kernel/calls.S kernel-source-2.4.27-8-arm-1/arch/arm/kernel/calls.S
+--- kernel-source-2.4.27-8/arch/arm/kernel/calls.S 2005-01-19 09:57:51.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/calls.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/kernel/debug-armv.S kernel-source-2.4.27-8-arm-1/arch/arm/kernel/debug-armv.S
+--- kernel-source-2.4.27-8/arch/arm/kernel/debug-armv.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/debug-armv.S 2005-02-18 17:48:34.000000000 +0000
+@@ -30,10 +30,9 @@
+ *
+ * (bjd)
+ */
+- mrc p15, 0, \rx, c1, c0
+- tst \rx, #1 @ MMU enabled?
+- movne \rx, #0xe0000000
+- moveq \rx, #0x03000000
++ cmp pc, #0xc0000000
++ movhs \rx, #0xe0000000
++ movlo \rx, #0x03000000
+ orr \rx, \rx, #0x00010000
+ orr \rx, \rx, #0x00000fe0
+ .endm
+@@ -434,6 +433,60 @@
+ ands \rd, \rd, #UART_TSR_TX_LEVEL_MSK
+ bne 1001b
+ .endm
++#elif (CONFIG_ARCH_BAST)
++#include <asm/arch/map.h>
++
++ .macro addruart, rx
++ mrc p15, 0, \rx, c1, c0
++ tst \rx, #1
++ ldreq \rx, = S3C2410_PA_UART
++ ldrne \rx, = S3C2410_VA_UART
++ .endm
++
++ .macro senduart,rd,rx
++ str \rd, [\rx, #0x20]
++ .endm
++
++ .macro busyuart, rd, rx
++ ldr \rd, [ \rx, #0x08 ]
++ tst \rd, #1 @ fifo enabled?
++ beq 1001f @
++ @ FIFO enabled...
++1003:
++ ldr \rd, [ \rx, #0x18 ]
++ tst \rd, #1<<9
++ bne 1003b
++ b 1002f
++
++1001:
++ @ busy waiting for non fifo
++ ldr \rd, [ \rx, #0x10 ]
++ tst \rd, #1<<1
++ beq 1001b
++
++1002: @ exit busyuart
++ .endm
++
++ .macro waituart,rd,rx
++
++ ldr \rd, [ \rx, #0x08 ]
++ tst \rd, #1 @ fifo enabled?
++ beq 1001f @
++ @ FIFO enabled...
++1003:
++ ldr \rd, [ \rx, #0x18 ]
++ ands \rd, \rd, #15<<4
++ bne 1003b
++ b 1002f
++
++1001:
++ @ idle waiting for non fifo
++ ldr \rd, [ \rx, #0x10 ]
++ tst \rd, #1<<1
++ beq 1001b
++
++1002: @ exit busyuart
++ .endm
+
+ #elif defined(CONFIG_ARCH_MX1ADS)
+
+diff -urN kernel-source-2.4.27-8/arch/arm/kernel/debug.c kernel-source-2.4.27-8-arm-1/arch/arm/kernel/debug.c
+--- kernel-source-2.4.27-8/arch/arm/kernel/debug.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/debug.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,43 @@
++/* arch/arm/kernel/debug.c
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * Debug code to access printascii() and other functions
++ *
++ * 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:
++ * 26-06-2003 BJD Created file
++*/
++
++#include <linux/config.h>
++#include <linux/tty.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/init.h>
++
++#include <asm/elf.h>
++#include <asm/setup.h>
++#include <asm/mach-types.h>
++
++#include <asm/mach/arch.h>
++#include <asm/debug.h>
++
++
++
++extern void printascii(const char *);
++
++void llprintk(const char *msg, ...)
++{
++ char llpk_buf[512];
++ va_list va;
++
++ va_start(va, msg);
++ vsprintf(llpk_buf, msg, va);
++ va_end(va);
++
++ printascii(llpk_buf);
++}
+diff -urN kernel-source-2.4.27-8/arch/arm/kernel/dma-rpc.c kernel-source-2.4.27-8-arm-1/arch/arm/kernel/dma-rpc.c
+--- kernel-source-2.4.27-8/arch/arm/kernel/dma-rpc.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/dma-rpc.c 2005-02-18 17:48:34.000000000 +0000
+@@ -26,19 +26,6 @@
+ #include <asm/mach/dma.h>
+ #include <asm/hardware/iomd.h>
+
+-#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,9 +86,7 @@
+ if (!(status & DMA_ST_INT))
+ return;
+
+- if (status & DMA_ST_OFL && !dma->sg)
+- break;
+-
++ if ((dma->state ^ status) & DMA_ST_AB)
+ iomd_get_next_sg(&dma->cur_sg, dma);
+
+ switch (status & (DMA_ST_OFL | DMA_ST_AB)) {
+@@ -113,17 +94,23 @@
+ 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 kernel-source-2.4.27-8/arch/arm/kernel/entry-armv.S kernel-source-2.4.27-8-arm-1/arch/arm/kernel/entry-armv.S
+--- kernel-source-2.4.27-8/arch/arm/kernel/entry-armv.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/entry-armv.S 2005-02-18 17:48:34.000000000 +0000
+@@ -615,6 +615,206 @@
+ .text
+ .endm
+
++#elif CONFIG_CPU_S3C2410X
++#include <asm/arch/map.h>
++ @ irq priority table
++
++ .macro irq_prio_table
++ .endm
++
++ .macro disable_fiq
++ .endm
++
++
++ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
++ mov \tmp, #S3C2410_VA_IRQ
++ ldr \irqnr, [ \tmp, #0x14 ] @ get irq no
++30000:
++#if 0
++ stmfd r13!, { r0 - r12, r14 }
++ mov r1, \irqnr
++ adr r0, 10001f
++ bl printk
++ b 10002f
++10001:
++ .ascii "irq: irqno=%d\n"
++ .byte 0
++ .align 4
++10002:
++ ldmfd r13!, { r0 - r12, r14 }
++#endif
++
++ teq \irqnr, #4
++ teqne \irqnr, #5
++ beq 1002f @ external irq reg
++ teq \irqnr, #16
++ beq 1003f @ lcd controller
++
++ @ debug check to see if interrupt reported is the same
++ @ as the offset....
++
++#if 1
++ teq \irqnr, #0
++ beq 20002f
++ ldr \irqstat, [ \tmp, #0x10 ] @ INTPND
++ mov \irqstat, \irqstat, lsr \irqnr
++ tst \irqstat, #1
++ bne 20002f
++
++#if 1
++ stmfd r13!, { r0 - r4 , r14 }
++ ldr r1, [ \tmp, #0x14 ] @ intoffset
++ ldr r2, [ \tmp, #0x10 ] @ INTPND
++ ldr r3, [ \tmp, #0x00 ] @ SRCPND
++ adr r0, 20003f
++ bl printk
++ b 20004f
++#endif
++20003:
++ .ascii "<7>irq: err - bad offset %d, intpnd=%08x, srcpnd=%08x\n"
++ .byte 0
++ .align 4
++20004:
++ mov r1, #1
++ mov \tmp, #S3C2410_VA_IRQ
++#if 0
++ ldr \irqnr, [ \tmp, # 0x14 ]
++ mov r1, r1, lsl \irqnr
++
++
++ @ldr r2, [ \tmp, # 0x08 ] @ IRQMASK
++ @bic r2, r2, r1
++ @str r2, [ \tmp, # 0x08 ]
++
++ str r1, [ \tmp, # 0x00 ]
++ str r1, [ \tmp, # 0x10 ]
++
++ @ldr r2, [ \tmp, # 0x08 ]
++ @orr r2, r2, r1
++ @str r2, [ \tmp, # 0x08 ]
++#endif
++ ldmfd r13!, { r0 - r4 , r14 }
++
++ @ try working out interript number for ourselves
++ mov \irqnr, #0
++ ldr \irqstat, [ \tmp, #0x10 ] @ INTPND
++10021:
++ movs \irqstat, \irqstat, lsr#1
++ bcs 30000b @ try and re-start the proccess
++ add \irqnr, \irqnr, #1
++ cmp \irqnr, #32
++ ble 10021b
++
++ @ found no interrupt, set Z flag and leave
++ movs \irqnr, #0
++ b 1001f
++
++20005:
++#if 0
++ stmfd r13!, { r0 - r4, r14 }
++ mov r1, \irqnr
++ adr r0, 20006f
++ bl printk
++ b 20007f
++20006:
++ .ascii "irq: fixup=%d\n"
++ .byte 0
++ .align 4
++20007:
++ ldmfd r13!, { r0 - r4, r14 }
++
++#endif
++
++20002: @ exit
++#endif
++
++ @ we base the s3c2410x interrupts at 16 and above to allow
++ @ isa peripherals to have their standard interrupts, also
++ @ ensure that Z flag is un-set on exit
++
++ @ note, we cannot be sure if we get IRQ_EINT0 (0) that
++ @ there is simply no interrupt pending, so in all other
++ @ cases we jump to say we have found something, otherwise
++ @ we check to see if the interrupt really is assrted
++ adds \irqnr, \irqnr, #IRQ_EINT0
++ teq \irqnr, #IRQ_EINT0
++ bne 1001f @ exit
++ ldr \irqstat, [ \tmp, #0x10 ] @ INTPND
++ teq \irqstat, #0
++ moveq \irqnr, #0
++ b 1001f
++
++ @ we get here from no main or external interrupts pending
++1002:
++ add \tmp, \tmp, #S3C2410_VA_GPIO - S3C2410_VA_IRQ
++ ldr \irqstat, [ \tmp, # 0xa8 ] @ EXTINTPEND
++ ldr \irqnr, [ \tmp, # 0xa4 ] @ EXTINTMASK
++
++#if 0
++ stmfd r13!, { r0 - r12, r14 }
++ mov r1, \irqnr
++ mov r2, \irqstat
++ cmp r1, #IRQ_EINT0
++ bcc 10002f
++ adr r0, 10001f
++ bl printk
++ b 10002f
++10001:
++ .ascii "irq: irqmask=%08x, irqnr=%08x\n"
++ .byte 0
++ .align 4
++10002:
++ ldmfd r13!, { r0 - r12, r14 }
++#endif
++ bic \irqstat, \irqstat, \irqnr @ clear masked irqs
++
++ mov \irqnr, #IRQ_EINT4 @ start extint nos
++ mov \irqstat, \irqstat, lsr#4 @ ignore bottom 4 bits
++10021:
++ movs \irqstat, \irqstat, lsr#1
++ bcs 1004f
++ add \irqnr, \irqnr, #1
++ cmp \irqnr, #IRQ_EINT23
++ ble 10021b
++
++ @ found no interrupt, set Z flag and leave
++ movs \irqnr, #0
++ b 1001f
++
++1003:
++ @ lcd interrupt has been asserted...
++ add \tmp, \tmp, #S3C2410_VA_LCD - S3C2410_VA_IRQ
++ ldr \irqstat, [ \tmp, # 0x54 ] @ lcd int pending
++
++ tst \irqstat, #2
++ movne \irqnr, #IRQ_LCD_FRAME
++ tst \irqstat, #1
++ movne \irqnr, #IRQ_LCD_FIFO
++
++ @ fall through to exit with flags updated
++
++1004: @ ensure Z flag clear in case our MOVS shifted out the last bit
++
++#if 0
++ stmfd r13!, { r0 - r12, r14 }
++ mov r1, \irqnr
++ cmp r1, #IRQ_EINT0
++ bcc 10002f
++ adr r0, 10001f
++ bl printk
++ b 10002f
++10001:
++ .ascii "irq: irqno=%d\n"
++ .byte 0
++ .align 4
++10002:
++ ldmfd r13!, { r0 - r12, r14 }
++#endif
++ teq \irqnr, #0
++1001:
++ @ exit irq routine
++ .endm
++
+ #else
+ #error Unknown architecture
+ #endif
+@@ -677,12 +877,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 +987,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 +1038,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 kernel-source-2.4.27-8/arch/arm/kernel/fiq.c kernel-source-2.4.27-8-arm-1/arch/arm/kernel/fiq.c
+--- kernel-source-2.4.27-8/arch/arm/kernel/fiq.c 2003-06-13 15:51:29.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/fiq.c 2005-02-18 17:48:34.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/arch/arm/kernel/head-armv.S kernel-source-2.4.27-8-arm-1/arch/arm/kernel/head-armv.S
+--- kernel-source-2.4.27-8/arch/arm/kernel/head-armv.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/head-armv.S 2005-02-18 17:48:34.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
+@@ -72,7 +72,26 @@
+ .section ".text.init",#alloc,#execinstr
+ .type stext, #function
+ ENTRY(stext)
++
++#if defined(CONFIG_ARCH_NETWINDER)
++/*
++ * Debian specific - we add zImage header to non zImage kernel because
++ * some bootloaders (nettrom) cannot cope otherwise
++ */
++ .type start,#function
++ .rept 8
++ mov r0, r0
++ .endr
++
++ b 1f
++ .word 0x016f2818 @ Magic numbers to help the loader
++ .word stext @ absolute load/run Image address
++ .word _edata @ zImage end address
++1:
++#endif
++
+ mov r12, r0
++
+ /*
+ * NOTE! Any code which is placed here should be done for one of
+ * the following reasons:
+@@ -163,10 +182,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 +233,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 kernel-source-2.4.27-8/arch/arm/kernel/irq.c kernel-source-2.4.27-8-arm-1/arch/arm/kernel/irq.c
+--- kernel-source-2.4.27-8/arch/arm/kernel/irq.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/irq.c 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/kernel/ptrace.c kernel-source-2.4.27-8-arm-1/arch/arm/kernel/ptrace.c
+--- kernel-source-2.4.27-8/arch/arm/kernel/ptrace.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/ptrace.c 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/kernel/semaphore.c kernel-source-2.4.27-8-arm-1/arch/arm/kernel/semaphore.c
+--- kernel-source-2.4.27-8/arch/arm/kernel/semaphore.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/semaphore.c 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/kernel/signal.c kernel-source-2.4.27-8-arm-1/arch/arm/kernel/signal.c
+--- kernel-source-2.4.27-8/arch/arm/kernel/signal.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/kernel/signal.c 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/lib/Makefile kernel-source-2.4.27-8-arm-1/arch/arm/lib/Makefile
+--- kernel-source-2.4.27-8/arch/arm/lib/Makefile 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/lib/Makefile 2005-02-18 17:48:34.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 :=
+
+@@ -28,6 +28,7 @@
+ obj-shark := io-shark.o
+ obj-edb7211 := io-acorn.o
+ obj-riscstation := io-acorn.o floppydma.o
++obj-bast := io-m8-readsl.o io-m8-writesl.o io-m4-readsl.o io-m4-writesl.o
+
+ obj-y += $(obj-$(MACHINE))
+
+diff -urN kernel-source-2.4.27-8/arch/arm/lib/div64.S kernel-source-2.4.27-8-arm-1/arch/arm/lib/div64.S
+--- kernel-source-2.4.27-8/arch/arm/lib/div64.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/lib/div64.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,59 @@
++#include <linux/linkage.h>
++
++#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 kernel-source-2.4.27-8/arch/arm/lib/io-m4-readsl.S kernel-source-2.4.27-8-arm-1/arch/arm/lib/io-m4-readsl.S
+--- kernel-source-2.4.27-8/arch/arm/lib/io-m4-readsl.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/lib/io-m4-readsl.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,98 @@
++/* linux/arch/arm/lib/io-m8sl.S
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ *
++ * IO access to multi-word mapped registers
++ *
++ * 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 <linux/linkage.h>
++#include <asm/assembler.h>
++#include <asm/hardware.h>
++
++ENTRY(__raw_m4_readsl)
++ @ r0 = pointer to register
++ @ r1 = pointer to data to read
++ @ r2 = length to transfer in words
++
++#ifdef CONFIG_NO_MULTIWORD_IO
++ b __raw_readsl
++#endif
++
++ teq r2, #0
++ moveq pc, r14
++ stmfd r13!, { r4 - r12 , r14 }
++ and ip, r1, #3
++ add pc, pc, ip, lsl#2
++ mov r0, r0 @ nop
++ b aligned
++ b unalign1
++ b unalign2
++ b unalign3
++
++unalign1:
++unalign2:
++unalign3:
++ ldmfd r13!, { r4 - r12, r14 }
++
++ stmfd r13!, { r0 - r4 , r14 }
++ mov r3, r2
++ mov r2, r1
++ mov r1, r0
++ adr r0, 1f
++ bl printk
++ b 2f
++1:
++ .ascii "__raw_m4_readsl: unaligned: io=%08x, src=%08x, len=%02x\n"
++ .byte 0
++ .align 4
++2:
++ ldmfd r13!, { r0 - r4 , r14 }
++ b __raw_readsl
++
++ /* transfer is properly aligned, we can start moving data... */
++aligned:
++
++#if 0
++ /* report */
++ stmfd r13!, { r0 - r4 , r14 }
++ mov r3, r2
++ mov r2, r1
++ mov r1, r0
++ adr r0, 1f
++ bl printk
++ b 2f
++1:
++ .ascii "__raw_m4_readsl: aligned: io=%08x, src=%08x, llen=%02x\n"
++ .byte 0
++ .align 4
++2:
++ ldmfd r13!, { r0 - r4 , r14 }
++#endif
++
++1:
++ subs r2, r2, #8 @ have we enough for 8words?
++ ldmgeia r0, { r3, r4, r5, r6 } @ load 4 words
++ stmgeia r1!, { r3 - r6 } @ store
++ ldmgeia r0, { r3, r4, r5, r6 } @ load 4 words
++ stmgeia r1!, { r3 - r6 } @ store
++ bgt 1b
++
++ @ if the input of the above loop was 8, then the output would be
++ @ 0, if the input was 7, then the output will be -1, with no data
++ @ moved. If the output is eq, then the exact number of words have
++ @ been moved, exit
++
++ ldmeqfd r13!, { r4 - r12, pc } @ if exact, then exit
++
++ add r2, r2, #8 @ fixup count
++1:
++ subs r2, r2, #1
++ ldrge r3, [ r0 ]
++ strge r3, [ r1 ], #4
++ bge 1b
++2:
++ ldmfd r13!, { r4 - r12, pc }
+diff -urN kernel-source-2.4.27-8/arch/arm/lib/io-m4-writesl.S kernel-source-2.4.27-8-arm-1/arch/arm/lib/io-m4-writesl.S
+--- kernel-source-2.4.27-8/arch/arm/lib/io-m4-writesl.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/lib/io-m4-writesl.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,98 @@
++/* linux/arh/arm/lib/io-m8sl.S
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ *
++ * IO access to multi-word mapped registers
++ *
++ * 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 <linux/linkage.h>
++#include <asm/assembler.h>
++#include <asm/hardware.h>
++
++#ifdef CONFIG_NO_MULTIWORD_IO
++ b __raw_writesl
++#endif
++
++ENTRY(__raw_m4_writesl)
++ @ r0 = pointer to register
++ @ r1 = pointer to data to read
++ @ r2 = length to transfer in words
++
++ teq r2, #0
++ moveq pc, r14
++ stmfd r13!, { r4 - r12 , r14 }
++ and ip, r1, #3
++ add pc, pc, ip, lsl#2
++ mov r0, r0 @ nop
++ b aligned
++ b unalign1
++ b unalign2
++ b unalign3
++
++unalign1:
++unalign2:
++unalign3:
++ ldmfd r13!, { r4 - r12, r14 }
++
++ stmfd r13!, { r0 - r4 , r14 }
++ mov r3, r2
++ mov r2, r1
++ mov r1, r0
++ adr r0, 1f
++ bl printk
++ b 2f
++1:
++ .ascii "__raw_m4_writesl: unaligned: io=%08x, src=%08x, len=%02x\n"
++ .byte 0
++ .align 4
++2:
++ ldmfd r13!, { r0 - r4 , r14 }
++ b __raw_writesl
++
++ /* transfer is properly aligned, we can start moving data... */
++aligned:
++
++#if 0
++ /* report */
++ stmfd r13!, { r0 - r4 , r14 }
++ mov r3, r2
++ mov r2, r1
++ mov r1, r0
++ adr r0, 1f
++ bl printk
++ b 2f
++1:
++ .ascii "__raw_m4_writesl: aligned: io=%08x, src=%08x, llen=%02x\n"
++ .byte 0
++ .align 4
++2:
++ ldmfd r13!, { r0 - r4 , r14 }
++#endif
++
++1:
++ subs r2, r2, #8 @ have we enough for 8words?
++ ldmgeia r1!, { r3, r4, r5, r6 } @ load 4 words
++ stmgeia r0, { r3 - r6 } @ store
++ ldmgeia r1!, { r3, r4, r5, r6 } @ load 4 words
++ stmgeia r0, { r3 - r6 } @ store
++ bgt 1b
++
++ @ if the input of the above loop was 8, then the output would be
++ @ 0, if the input was 7, then the output will be -1, with no data
++ @ moved. If the output is eq, then the exact number of words have
++ @ been moved, exit
++
++ ldmeqfd r13!, { r4 - r12, pc } @ if exact, then exit
++
++ add r2, r2, #8 @ fixup count
++1:
++ subs r2, r2, #1
++ ldrge r3, [ r1 ], #4
++ strge r3, [ r0 ]
++ bge 1b
++2:
++ ldmfd r13!, { r4 - r12, pc }
+diff -urN kernel-source-2.4.27-8/arch/arm/lib/io-m8-readsl.S kernel-source-2.4.27-8-arm-1/arch/arm/lib/io-m8-readsl.S
+--- kernel-source-2.4.27-8/arch/arm/lib/io-m8-readsl.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/lib/io-m8-readsl.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,96 @@
++/* linux/arch/arm/lib/io-m8sl.S
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ *
++ * IO access to multi-word mapped registers
++ *
++ * 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 <linux/linkage.h>
++#include <asm/assembler.h>
++#include <asm/hardware.h>
++
++ENTRY(__raw_m8_readsl)
++ @ r0 = pointer to register
++ @ r1 = pointer to data to read
++ @ r2 = length to transfer in words
++
++#ifdef CONFIG_NO_MULTIWORD_IO
++ b __raw_readsl
++#endif
++
++ teq r2, #0
++ moveq pc, r14
++ stmfd r13!, { r4 - r12 , r14 }
++ and ip, r1, #3
++ add pc, pc, ip, lsl#2
++ mov r0, r0 @ nop
++ b aligned
++ b unalign1
++ b unalign2
++ b unalign3
++
++unalign1:
++unalign2:
++unalign3:
++ ldmfd r13!, { r4 - r12, r14 }
++
++ stmfd r13!, { r0 - r4 , r14 }
++ mov r3, r2
++ mov r2, r1
++ mov r1, r0
++ adr r0, 1f
++ bl printk
++ b 2f
++1:
++ .ascii "__raw_m8_readsl: unaligned: io=%08x, src=%08x, len=%02x\n"
++ .byte 0
++ .align 4
++2:
++ ldmfd r13!, { r0 - r4 , r14 }
++ b __raw_readsl
++
++ /* transfer is properly aligned, we can start moving data... */
++aligned:
++
++#if 0
++ /* report */
++ stmfd r13!, { r0 - r4 , r14 }
++ mov r3, r2
++ mov r2, r1
++ mov r1, r0
++ adr r0, 1f
++ bl printk
++ b 2f
++1:
++ .ascii "__raw_m8_readsl: aligned: io=%08x, dst=%08x, llen=%02x\n"
++ .byte 0
++ .align 4
++2:
++ ldmfd r13!, { r0 - r4 , r14 }
++#endif
++
++1:
++ subs r2, r2, #8 @ have we enough for 8words?
++ ldmgeia r0, { r3 - r10 } @ load 8 wordds
++ stmgeia r1!, { r3 - r10 } @ store
++ bgt 1b
++
++ @ if the input of the above loop was 8, then the output would be
++ @ 0, if the input was 7, then the output will be -1, with no data
++ @ moved. If the output is eq, then the exact number of words have
++ @ been moved, exit
++
++ ldmeqfd r13!, { r4 - r12, pc } @ if exact, then exit
++
++ add r2, r2, #8
++1:
++ subs r2, r2, #1
++ ldrge r3, [ r0 ]
++ strge r3, [ r1 ], #4
++ bge 1b
++
++ ldmfd r13!, { r4 - r12, pc }
+diff -urN kernel-source-2.4.27-8/arch/arm/lib/io-m8-writesl.S kernel-source-2.4.27-8-arm-1/arch/arm/lib/io-m8-writesl.S
+--- kernel-source-2.4.27-8/arch/arm/lib/io-m8-writesl.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/lib/io-m8-writesl.S 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,96 @@
++/* linux/arch/arm/lib/io-m8sl.S
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ *
++ * IO access to multi-word mapped registers
++ *
++ * 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 <linux/linkage.h>
++#include <asm/assembler.h>
++#include <asm/hardware.h>
++
++ENTRY(__raw_m8_writesl)
++ @ r0 = pointer to register
++ @ r1 = pointer to data to read
++ @ r2 = length to transfer in words
++
++#ifdef CONFIG_NO_MULTIWORD_IO
++ b __raw_writesl
++#endif
++
++ teq r2, #0
++ moveq pc, r14
++ stmfd r13!, { r4 - r12 , r14 }
++ and ip, r1, #3
++ add pc, pc, ip, lsl#2
++ mov r0, r0 @ nop
++ b aligned
++ b unalign1
++ b unalign2
++ b unalign3
++
++unalign1:
++unalign2:
++unalign3:
++ ldmfd r13!, { r4 - r12, r14 }
++
++ stmfd r13!, { r0 - r4 , r14 }
++ mov r3, r2
++ mov r2, r1
++ mov r1, r0
++ adr r0, 1f
++ bl printk
++ b 2f
++1:
++ .ascii "__raw_m8_writesl: unaligned: io=%08x, src=%08x, len=%02x\n"
++ .byte 0
++ .align 4
++2:
++ ldmfd r13!, { r0 - r4 , r14 }
++ b __raw_writesl
++
++ /* transfer is properly aligned, we can start moving data... */
++aligned:
++
++#if 0
++ /* report */
++ stmfd r13!, { r0 - r4 , r14 }
++ mov r3, r2
++ mov r2, r1
++ mov r1, r0
++ adr r0, 1f
++ bl printk
++ b 2f
++1:
++ .ascii "__raw_m8_writesl: aligned: io=%08x, dst=%08x, llen=%02x\n"
++ .byte 0
++ .align 4
++2:
++ ldmfd r13!, { r0 - r4 , r14 }
++#endif
++
++1:
++ subs r2, r2, #8 @ have we enough for 8words?
++ ldmgeia r1!, { r3 - r10 } @ store
++ stmgeia r0, { r3 - r10 } @ load 8 wordds
++ bgt 1b
++
++ @ if the input of the above loop was 8, then the output would be
++ @ 0, if the input was 7, then the output will be -1, with no data
++ @ moved. If the output is eq, then the exact number of words have
++ @ been moved, exit
++
++ ldmeqfd r13!, { r4 - r12, pc } @ if exact, then exit
++
++ add r2, r2, #8
++1:
++ subs r2, r2, #1
++ ldrge r3, [ r1 ], #4
++ strge r3, [ r0 ]
++ bge 1b
++
++ ldmfd r13!, { r4 - r12, pc }
+diff -urN kernel-source-2.4.27-8/arch/arm/lib/putuser.S kernel-source-2.4.27-8-arm-1/arch/arm/lib/putuser.S
+--- kernel-source-2.4.27-8/arch/arm/lib/putuser.S 2001-10-11 17:04:57.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/lib/putuser.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mach-at91rm9200/Makefile kernel-source-2.4.27-8-arm-1/arch/arm/mach-at91rm9200/Makefile
+--- kernel-source-2.4.27-8/arch/arm/mach-at91rm9200/Makefile 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-at91rm9200/Makefile 2005-02-18 17:48:34.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/arch/arm/mach-at91rm9200/dk-leds.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-at91rm9200/dk-leds.c
+--- kernel-source-2.4.27-8/arch/arm/mach-at91rm9200/dk-leds.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-at91rm9200/dk-leds.c 2005-02-18 17:48:34.000000000 +0000
+@@ -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 <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <asm/mach-types.h>
++#include <asm/leds.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pio.h>
++
++
++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 kernel-source-2.4.27-8/arch/arm/mach-bast/Makefile kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/Makefile
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/Makefile 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,24 @@
++#
++# Makefile for the linux kernel.
++#
++# 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).
++
++USE_STANDARD_AS_RULE := true
++
++O_TARGET := bast.o
++
++# Object file lists.
++
++obj-y := arch.o irq.o mm.o dma.o
++obj-m :=
++obj-n :=
++obj- :=
++
++export-objs := s3c2410-pcibuf.o pcipool.o
++
++obj-$(CONFIG_USB_OHCI_S3C2410) += s3c2410-pcibuf.o pcipool.o
++obj-$(CONFIG_S3C2410_DMA_PROC) += dma-proc.o
++
++include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/arch.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/arch.c
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/arch.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/arch.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,89 @@
++/* linux/arch/arm/mach-bast/arch.c
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * http://www.simtec.co.uk/products/EB110ITX/
++ *
++ * 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.
++ *
++ * Modifications:
++ * 16-May-2003 BJD Created initial version
++ * 16-Aug-2003 BJD Fixed header files and copyright, added URL
++ */
++
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/pm.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/setup.h>
++#include <asm/mach-types.h>
++#include <asm/io.h>
++
++#include <asm/mach/arch.h>
++
++#include <asm/arch-s3c2410/S3C2410-gpio.h>
++
++/* get our memory definitions */
++#include <asm/arch/map.h>
++
++#include "arch.h"
++
++static void
++vr1000_power_off(void)
++{
++ unsigned int tmp;
++
++ printk("VR1000: powering down PSU\n");
++
++ tmp = __raw_readl(S3C2410_GPBDAT);
++ __raw_writel(tmp | (1<<9), S3C2410_GPBDAT);
++}
++
++static void __init
++bast_fixup(struct machine_desc *desc, struct param_struct *unused,
++ char **cmdline, struct meminfo *mi)
++{
++ /* nothing to be done here atm */
++
++ if (machine_is_vr1000()) {
++ pm_power_off = vr1000_power_off;
++ }
++}
++
++MACHINE_START(BAST, "Simtec-BAST")
++ MAINTAINER("Ben Dooks <ben at simtec.co.uk>")
++
++ /* ensure our system starts-up with the UARTs mapped for debug */
++ BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, S3C2410_VA_UART)
++ BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
++ FIXUP(bast_fixup)
++ MAPIO(bast_map_io)
++ INITIRQ(bast_init_irq)
++MACHINE_END
++
++MACHINE_START(VR1000, "Thorcom-VR1000")
++ MAINTAINER("Ben Dooks <ben at simtec.co.uk>")
++
++ /* ensure our system starts-up with the UARTs mapped for debug */
++#ifdef CONFIG_DEBUG_VR1000_SER3
++ BOOT_MEM(S3C2410_SDRAM_PA, VR1000_VA_DEBUG, VR1000_VA_DEBUG)
++#else
++ BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, S3C2410_VA_UART)
++#endif
++ BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
++ FIXUP(bast_fixup)
++ MAPIO(bast_map_io)
++ INITIRQ(bast_init_irq)
++MACHINE_END
++
++
++
++
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/arch.h kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/arch.h
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/arch.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/arch.h 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,7 @@
++/* linux/arch/arm/mach-bast/arch.h
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ */
++
++extern void bast_map_io(void);
++extern void bast_init_irq(void);
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/dma-proc.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/dma-proc.c
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/dma-proc.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/dma-proc.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,392 @@
++/* linux/arch/arm/mach-bast/dma-proc.c
++ *
++ * (c) 2004 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * S3C2410 DMA handler /proc interface
++ *
++ * http://www.simtec.co.uk/products/EB2410ITX/
++ *
++ * 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:
++ * 12-Jul-2004 BJD Added `all` output to the /proc interface
++ * 12-Jul-2004 BJD Updated output, removed bits of debug info
++ * 08-Jul-2004 BJD Created to contain interface to DMA system via /proc
++ *
++*/
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/proc_fs.h>
++
++#include <asm/system.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/mach/dma.h>
++
++struct proc_dir_entry *dma_root = NULL;
++static const char *proc_root_name = "driver/s3c-dma";
++
++static struct proc_dir_entry *all_entry;
++
++struct s3c2410_dma_proc_entry {
++ struct proc_dir_entry *entry;
++ s3c2410_dma_chan_t *channel;
++ char name[8];
++};
++
++struct s3c2410_dma_proc_entry dma_entries[S3C2410_DMA_CHANNELS];
++
++static int
++do_pf(char **ptr, const char *fmt, ...)
++{
++ int len;
++ va_list va;
++
++ va_start(va, fmt);
++ len = vsprintf(*ptr, fmt, va);
++ va_end(va);
++
++ //printk(KERN_DEBUG "dma-proc: %s", *ptr);
++
++ *ptr += len;
++
++ return len;
++}
++
++const char *
++s3c2410_dma_decode_state(s3c2410_dma_state_t state)
++{
++ switch (state) {
++ case S3C2410_DMA_IDLE:
++ return "Idle";
++ case S3C2410_DMA_RUNNING:
++ return "Running";
++ case S3C2410_DMA_PAUSED:
++ return "Paused";
++ default:
++ return "Invalid";
++ }
++
++ return NULL;
++}
++
++const char *
++s3c2410_dma_decode_loadstate(s3c2410_dma_loadst_t state)
++{
++ switch (state) {
++ case S3C2410_DMALOAD_NONE:
++ return "Nothing Loaded";
++ case S3C2410_DMALOAD_1LOADED:
++ return "1 Loaded";
++ case S3C2410_DMALOAD_1RUNNING:
++ return "1 Running";
++ case S3C2410_DMALOAD_1LOADED_1RUNNING:
++ return "1 Loaded, 1 Running";
++ default:
++ return "Invalid Load State";
++ }
++
++ return NULL;
++}
++
++const char *
++s3c2410_dma_decode_loadstate_short(s3c2410_dma_loadst_t state)
++{
++ switch (state) {
++ case S3C2410_DMALOAD_NONE:
++ return "No Load";
++ case S3C2410_DMALOAD_1LOADED:
++ return "1Load";
++ case S3C2410_DMALOAD_1RUNNING:
++ return "1Run";
++ case S3C2410_DMALOAD_1LOADED_1RUNNING:
++ return "1Load, 1Run";
++ default:
++ return "Invalid";
++ }
++
++ return NULL;
++}
++
++const char *
++s3c2410_dma_decode_source(s3c2410_dmasrc_t source)
++{
++ switch (source) {
++ case S3C2410_DMASRC_HW:
++ return "Hardware";
++ case S3C2410_DMASRC_MEM:
++ return "Memory";
++ default:
++ return "Unknown Source";
++ }
++
++ return NULL;
++}
++
++int s3c2410_proc_channel(char *buffer, char **start, off_t offset,
++ int count, int *eof, void *data )
++{
++ struct s3c2410_dma_proc_entry *entry;
++ s3c2410_dma_chan_t *chan;
++ unsigned long flags;
++ int len;
++
++ printk(KERN_DEBUG "s3c2410_proc_channel:"
++ "buffer=%p, start=%p, offset=%d, count=%d\n",
++ buffer, start, (int)offset, count);
++
++ entry = (struct s3c2410_dma_proc_entry *)data;
++ chan = entry->channel;
++
++ len = do_pf(&buffer, "DMA Channel %d\n", chan->number);
++ len += do_pf(&buffer, "-------------\n");
++ len += do_pf(&buffer, "\n");
++
++ len += do_pf(&buffer, "IRQ:\t\t\t\t%d\n", chan->irq);
++ len += do_pf(&buffer, "Register Base:\t\t\t0x%08x\n", chan->regs);
++ len += do_pf(&buffer, "Address Register:\t\t0x%08lx\n", chan->addr_reg);
++ len += do_pf(&buffer, "Hardware Register:\t\t0x%08lx\n", chan->dev_addr);
++ len += do_pf(&buffer, "Load Timeout:\t\t\t0x%08x\n", chan->load_timeout);
++ len += do_pf(&buffer, "Data Source:\t\t\t%s\n",
++ s3c2410_dma_decode_source(chan->source));
++
++ len += do_pf(&buffer, "Channel State:\t\t\t%s (%d)\n",
++ s3c2410_dma_decode_state(chan->state), chan->state);
++ len += do_pf(&buffer, "Load State:\t\t\t%s (%d)\n",
++ s3c2410_dma_decode_loadstate(chan->load_state),
++ chan->load_state);
++
++ if (chan->client != NULL) {
++ len += do_pf(&buffer, "Claimed by:\t\t\t%s\n",
++ chan->client->name);
++ } else {
++ len += do_pf(&buffer, "Claimed by:\t\t\tNo-one\n");
++ }
++
++ len += do_pf(&buffer, "\n");
++
++#ifdef CONFIG_S3CDMA_PROC_VERBOSE
++ len += do_pf(&buffer, "=> DISRC = \t\t\t%08lx\n",
++ readl(chan->regs + 0x00));
++ len += do_pf(&buffer, "=> DISRCC = \t\t\t%08lx\n",
++ readl(chan->regs + 0x04));
++ len += do_pf(&buffer, "=> DIDST = \t\t\t%08lx\n",
++ readl(chan->regs + 0x08));
++ len += do_pf(&buffer, "=> DIDSTC = \t\t\t%08lx\n",
++ readl(chan->regs + 0x0C));
++ len += do_pf(&buffer, "=> DCON = \t\t\t%08lx\n",
++ readl(chan->regs + 0x10));
++ len += do_pf(&buffer, "=> DSTAT = \t\t\t%08lx\n",
++ readl(chan->regs + 0x14));
++ len += do_pf(&buffer, "=> DCSRC = \t\t\t%08lx\n",
++ readl(chan->regs + 0x18));
++ len += do_pf(&buffer, "=> DCDST = \t\t\t%08lx\n",
++ readl(chan->regs + 0x1c));
++ len += do_pf(&buffer, "=> DMTRG = \t\t\t%08lx\n",
++ readl(chan->regs + 0x20));
++
++ len += do_pf(&buffer, "\n");
++
++#if 0
++ /* debug for show state of the IIS system wrt to the DMA
++ * as well */
++#include <asm/arch-s3c2410/S3C2410-iis.h>
++
++ len += do_pf(&buffer, "=> IISCON = \t\t\t%08lx\n",
++ __raw_readl(S3C2410_IISCON));
++ len += do_pf(&buffer, "=> IISMOD = \t\t\t%08lx\n",
++ __raw_readl(S3C2410_IISMOD));
++ len += do_pf(&buffer, "=> IISFCON = \t\t\t%08lx\n",
++ __raw_readl(S3C2410_IISFCON));
++
++ len += do_pf(&buffer, "\n");
++#endif
++
++#endif /* CONFIG_S3CDMA_PROC_VERBOSE */
++
++ if (chan->stats != NULL) {
++ s3c2410_dma_stats_t *stats = chan->stats;
++
++ len += do_pf(&buffer, "Stats:\n");
++ len += do_pf(&buffer, "\n");
++ len += do_pf(&buffer, "Buffer Loads:\t\t\t%ld\n", stats->loads);
++ len += do_pf(&buffer, "Load Timeout (shortest)\t\t%ld\n",
++ stats->timeout_shortest);
++ len += do_pf(&buffer, "Load Timeout (longest)\t\t%ld\n",
++ stats->timeout_longest);
++ len += do_pf(&buffer, "Load Timeout (average)\t\t%ld\n",
++ (stats->loads == 0) ? 0 :
++ stats->timeout_avg / stats->loads);
++
++ len += do_pf(&buffer, "Load Timeout Failures\t\t%ld\n",
++ stats->timeout_failed);
++
++ len += do_pf(&buffer, "\n");
++ }
++
++#define BUFF_FMT "data=%08x, size=%08x, id=%08x"
++#define buff_pfargs(bf) (bf)->data, (bf)->size, (bf)->id
++
++ local_irq_save(flags);
++
++ if (chan->curr != NULL) {
++ len += do_pf(&buffer, "Current Buffer:\t%08x" BUFF_FMT "\n",
++ chan->curr, buff_pfargs(chan->curr));
++ }
++
++ if (chan->next != NULL) {
++ s3c2410_dma_buf_t *ptr = chan->next;
++
++ len +=do_pf(&buffer, "Buffer Queue: %08x -> %08x\n",
++ chan->next, chan->end);
++
++ for (; ptr != NULL; ptr = ptr->next) {
++ len +=do_pf(&buffer, "\t%08x: " BUFF_FMT "\n",
++ ptr, buff_pfargs(ptr));
++ }
++ }
++
++ local_irq_restore(flags);
++
++ *eof = 1;
++ return len;
++}
++
++static int
++s3c2410_dma_count_queue(s3c2410_dma_chan_t *chan)
++{
++ int count = 0;
++
++ if (chan->next != NULL) {
++ s3c2410_dma_buf_t *ptr = chan->next;
++
++ for (; ptr != NULL; ptr = ptr->next) {
++ count++;
++ }
++ }
++
++ return count;
++}
++
++/* s3c2410_proc_all
++ *
++ * Show brief output for all the dma channels
++*/
++
++int s3c2410_proc_all(char *buffer, char **start, off_t offset,
++ int count, int *eof, void *data )
++{
++ s3c2410_dma_chan_t *chan;
++ int channel;
++ int len = 0;
++
++ len = do_pf(&buffer, "Overall State:\n\n");
++ len += do_pf(&buffer, "No State LoadState Queued Claimed By\n");
++ len += do_pf(&buffer, "-- --------- ----------- ------ ----------\n");
++
++ for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) {
++ chan = &s3c2410_chans[channel];
++
++ len += do_pf(&buffer, "%d ", channel);
++ len += do_pf(&buffer, "%9s ",
++ s3c2410_dma_decode_state(chan->state));
++ len += do_pf(&buffer, "%11s ",
++ s3c2410_dma_decode_loadstate_short(chan->load_state));
++ len += do_pf(&buffer, "%6d ",
++ s3c2410_dma_count_queue(chan));
++
++ if (chan->client == NULL) {
++ len += do_pf(&buffer, "Unclaimed\n");
++ } else {
++ len += do_pf(&buffer, "%s\n", chan->client->name);
++ }
++ }
++
++ /* next, let's show the register state for each of these channels */
++
++ len += do_pf(&buffer, "\nRegister State:\n\n");
++ len += do_pf(&buffer, "No DISRCx DISRCC DIDST DIDSTC DCON DSTAT DCSRC DCDST DMT\n");
++ len += do_pf(&buffer, "-- -------- -------- -------- -------- -------- -------- -------- -------- ---\n");
++
++ for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) {
++ chan = &s3c2410_chans[channel];
++
++ len += do_pf(&buffer, "%d ", channel);
++
++ len += do_pf(&buffer, "%08lx ",
++ readl(chan->regs + 0x00));
++ len += do_pf(&buffer, "%08lx ",
++ readl(chan->regs + 0x04));
++ len += do_pf(&buffer, "%08lx ",
++ readl(chan->regs + 0x08));
++ len += do_pf(&buffer, "%08lx ",
++ readl(chan->regs + 0x0C));
++ len += do_pf(&buffer, "%08lx ",
++ readl(chan->regs + 0x10));
++ len += do_pf(&buffer, "%08lx ",
++ readl(chan->regs + 0x14));
++ len += do_pf(&buffer, "%08lx ",
++ readl(chan->regs + 0x18));
++ len += do_pf(&buffer, "%08lx ",
++ readl(chan->regs + 0x1c));
++ len += do_pf(&buffer, "%02lx",
++ readl(chan->regs + 0x20));
++
++ len += do_pf(&buffer, "\n");
++ }
++
++ len += do_pf(&buffer, "\n");
++
++ *eof = 1;
++ return len;
++}
++
++
++static int __init s3c2410_init_dmaproc(void)
++{
++ char name[32];
++ int i;
++
++ printk("S3C2410 DMA Driver /proc info, (c) 2004 Simtec Electronics\n");
++
++ dma_root = proc_mkdir( proc_root_name, NULL );
++
++ if (dma_root == NULL) {
++ printk(KERN_ERR "Failed to register '%s'\n", proc_root_name);
++ return -ENOMEM;
++ }
++
++ all_entry = create_proc_read_entry("all", 0444, dma_root,
++ s3c2410_proc_all, NULL);
++
++ for (i = 0; i < S3C2410_DMA_CHANNELS; i++) {
++ struct s3c2410_dma_proc_entry *ptr = &dma_entries[i];
++ sprintf(ptr->name, "ch%d", i);
++
++ ptr->channel = &s3c2410_chans[i];
++ ptr->entry = create_proc_read_entry(ptr->name,
++ 0444,
++ dma_root,
++ s3c2410_proc_channel,
++ ptr);
++
++ if (ptr->entry == NULL) {
++ printk(KERN_ERR "Failed to register '%s' in '%s'\n",
++ ptr->name, proc_root_name);
++ }
++ }
++
++ return 0;
++}
++
++__initcall(s3c2410_init_dmaproc);
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/dma.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/dma.c
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/dma.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/dma.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,1080 @@
++/* linux/arch/arm/mach-bast/dma.c
++ *
++ * (c) 2003,2004 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * S3C2410 DMA core
++ *
++ * http://www.simtec.co.uk/products/EB2410ITX/
++ *
++ * 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:
++ * 12-Jul-2004 BJD Finished re-write and change of API
++ * 06-Jul-2004 BJD Rewrote dma code to try and cope with various problems
++ * 23-May-2003 BJD Created file
++ * 19-Aug-2003 BJD Cleanup, header fix, added URL
++ *
++ * This file is based on the Sangwook Lee/Samsung patches, re-written due
++ * to various ommisions from the code (such as flexible dma configuration)
++ * for use with the BAST system board.
++ *
++ * The re-write is pretty much complete, and should be good enough for any
++ * possible DMA function
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/delay.h>
++
++#include <asm/system.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++
++#include <asm/mach/dma.h>
++#include <asm/arch/map.h>
++
++/* dma channel state information */
++s3c2410_dma_chan_t s3c2410_chans[S3C2410_DMA_CHANNELS];
++
++#define PFX "dma<%d>"
++
++/* debugging functions */
++
++#define BUF_MAGIC (0xcafebabe)
++
++#define dmawarn(str...) printk(KERN_DEBUG ## str)
++
++#ifdef CONFIG_S3C2410_DMA_DEBUG
++#define dmadbg(str, arg...) printk(KERN_DEBUG "dma<%d>: " ## str, chan->number, arg)
++#else
++#define dmadbg(str, arg...)
++#endif
++
++#define dma_regaddr(chan, reg) ((chan)->regs + (reg))
++
++#if 1
++#define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg))
++#else
++static inline void
++dma_wrreg(s3c2410_dma_chan_t *chan, int reg, unsigned long val)
++{
++ dmadbg("writing %08x to register %08x\n",(unsigned int)val,reg);
++ writel(val, dma_regaddr(chan, reg));
++}
++
++#endif
++
++#define dma_rdreg(chan, reg) readl((chan)->regs + (reg))
++
++/* captured register state for debug */
++
++struct s3c2410_dma_regstate {
++ unsigned long dcsrc;
++ unsigned long disrc;
++ unsigned long dstat;
++ unsigned long dcon;
++ unsigned long dmsktrig;
++};
++
++#ifdef CONFIG_S3C2410_DMA_DEBUG
++
++/* dmadbg_showregs
++ *
++ * simple debug routine to print the current state of the dma registers
++*/
++
++static void
++dmadbg_capture(s3c2410_dma_chan_t *chan, struct s3c2410_dma_regstate *regs)
++{
++ regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC);
++ regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC);
++ regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT);
++ regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON);
++ regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
++}
++
++static void
++dmadbg_showregs(const char *fname, int line, s3c2410_dma_chan_t *chan,
++ struct s3c2410_dma_regstate *regs)
++{
++ printk(KERN_DEBUG "dma<%d>: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n",
++ chan->number, fname, line,
++ regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig,
++ regs->dcon);
++}
++
++static void
++dmadbg_showchan(const char *fname, int line, s3c2410_dma_chan_t *chan)
++{
++ struct s3c2410_dma_regstate state;
++
++ dmadbg_capture(chan, &state);
++
++ printk(KERN_DEBUG "dma<%d>: %s:%d: ls=%d, cur=%p, %p %p\n",
++ chan->number, fname, line, chan->load_state,
++ chan->curr, chan->next, chan->end);
++
++ dmadbg_showregs(fname, line, chan, &state);
++}
++
++#define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan))
++#define dbg_showchan(chan) dmadbg_showchan(__FUNCTION__, __LINE__, (chan))
++#else
++#define dbg_showregs(chan) do { } while(0)
++#define dbg_showchan(chan) do { } while(0)
++#endif /* CONFIG_S3C2410_DMA_DEBUG */
++
++#define check_channel(chan) \
++ do { if ((chan) >= S3C2410_DMA_CHANNELS) { \
++ printk(KERN_ERR __FUNCTION__ ": invalid channel %d\n", (chan)); \
++ return -EINVAL; \
++ } } while(0)
++
++
++
++
++/* s3c2410_dma_stats_timeout
++ *
++ * Update DMA stats from timeout info
++*/
++
++static void
++s3c2410_dma_stats_timeout(s3c2410_dma_stats_t *stats, int val)
++{
++ if (stats == NULL)
++ return;
++
++ if (val > stats->timeout_longest)
++ stats->timeout_longest = val;
++ if (val < stats->timeout_shortest)
++ stats->timeout_shortest = val;
++
++ stats->timeout_avg += val;
++}
++
++
++
++/* s3c2410_dma_waitforload
++ *
++ * wait for the DMA engine to load a buffer, and update the state accordingly
++*/
++
++static int
++s3c2410_dma_waitforload(s3c2410_dma_chan_t *chan, int line)
++{
++ int timeout = chan->load_timeout;
++ int took;
++
++ if (chan->load_state != S3C2410_DMALOAD_1LOADED) {
++ printk(KERN_ERR PFX ": s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line);
++ return 0;
++ }
++
++ if (chan->stats != NULL)
++ chan->stats->loads++;
++
++ while (--timeout > 0) {
++ if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) {
++ took = chan->load_timeout - timeout;
++
++ //printk(KERN_DEBUG PFX "s3c2410_dma_waitforload:%d: took %d cycles for ready-ness\n", chan->number, line, took);
++
++ s3c2410_dma_stats_timeout(chan->stats, took);
++
++ switch (chan->load_state) {
++ case S3C2410_DMALOAD_1LOADED:
++ chan->load_state = S3C2410_DMALOAD_1RUNNING;
++ break;
++
++ default:
++ printk(KERN_ERR PFX ": unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state);
++ }
++
++ return 1;
++ }
++ }
++
++ if (chan->stats != NULL) {
++ chan->stats->timeout_failed++;
++ }
++
++ return 0;
++}
++
++
++
++/* s3c2410_dma_loadbuffer
++ *
++ * load a buffer, and update the channel state
++*/
++
++static inline int
++s3c2410_dma_loadbuffer(s3c2410_dma_chan_t *chan,
++ s3c2410_dma_buf_t *buf)
++{
++ unsigned long reload;
++
++ dmadbg("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n",
++ buf, (unsigned long)buf->data, buf->size);
++
++ if (buf == NULL) {
++ dmawarn("buffer is NULL\n");
++ return -EINVAL;
++ }
++
++ /* check the state of the channel before we do anything */
++
++ if (chan->load_state == S3C2410_DMALOAD_1LOADED) {
++ dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n");
++ }
++
++ if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) {
++ dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n");
++ }
++
++ /* it would seem sensible if we are the last buffer to not bother
++ * with the auto-reload bit, so that the DMA engine will not try
++ * and load another transfer after this one has finished...
++ */
++ if (chan->load_state == S3C2410_DMALOAD_NONE) {
++ dmadbg("load_state is none, checking for noreload (next=%p)\n",
++ buf->next);
++ reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0;
++ } else {
++ dmadbg("load_state is %d => autoreload\n", chan->load_state);
++ reload = S3C2410_DCON_AUTORELOAD;
++ }
++
++ writel(buf->data, chan->addr_reg);
++
++ dma_wrreg(chan, S3C2410_DMA_DCON,
++ chan->dcon | reload | (buf->size/chan->xfer_unit));
++
++ chan->next = buf->next;
++
++ /* update the state of the channel */
++
++ switch (chan->load_state) {
++ case S3C2410_DMALOAD_NONE:
++ chan->load_state = S3C2410_DMALOAD_1LOADED;
++ break;
++
++ case S3C2410_DMALOAD_1RUNNING:
++ chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING;
++ break;
++
++ default:
++ dmawarn("dmaload: unknown state %d in loadbuffer\n",
++ chan->load_state);
++ break;
++ }
++
++ return 0;
++}
++
++/* s3c2410_dma_call_op
++ *
++ * small routine to call the op routine with the given op if it has been
++ * registered
++*/
++
++static void
++s3c2410_dma_call_op(s3c2410_dma_chan_t *chan, s3c2410_chan_op_t op)
++{
++ if (chan->op_fn != NULL) {
++ (chan->op_fn)(chan, op);
++ }
++}
++
++/* s3c2410_dma_buffdone
++ *
++ * small wrapper to check if callback routine needs to be called, and
++ * if so, call it
++*/
++
++static inline void
++s3c2410_dma_buffdone(s3c2410_dma_chan_t *chan, s3c2410_dma_buf_t *buf,
++ s3c2410_dma_buffresult_t result)
++{
++ dmadbg("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n",
++ chan->callback_fn, buf, buf->id, buf->size, result);
++
++ if (chan->callback_fn != NULL) {
++ (chan->callback_fn)(chan, buf->id, buf->size, result);
++ }
++}
++
++/* s3c2410_dma_start
++ *
++ * start a dma channel going
++*/
++
++static int s3c2410_dma_start(s3c2410_dma_chan_t *chan)
++{
++ unsigned long tmp;
++ int flags;
++
++ dmadbg("s3c2410_start_dma: channel=%d\n", chan->number);
++
++ local_irq_save(flags);
++
++ if (chan->state == S3C2410_DMA_RUNNING) {
++ dmadbg("s3c2410_start_dma: already running (%d)\n", chan->state);
++ local_irq_restore(flags);
++ return 0;
++ }
++
++ chan->state = S3C2410_DMA_RUNNING;
++
++ /* check wether there is anything to load, and if not, see
++ * if we can find anything to load
++ */
++
++ if (chan->load_state == S3C2410_DMALOAD_NONE) {
++ if (chan->next == NULL) {
++ printk(KERN_ERR PFX ": channel has nothing loaded\n",
++ chan->number);
++ chan->state = S3C2410_DMA_IDLE;
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++
++ s3c2410_dma_loadbuffer(chan, chan->next);
++ }
++
++ dbg_showchan(chan);
++
++ /* enable the channel */
++
++ if (!chan->irq_enabled) {
++ enable_irq(chan->irq);
++ chan->irq_enabled = 1;
++ }
++
++ /* start the channel going */
++
++ tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
++ tmp &= ~S3C2410_DMASKTRIG_STOP;
++ tmp |= S3C2410_DMASKTRIG_ON;
++ dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);
++
++ dmadbg("wrote %08lx to DMASKTRIG\n", tmp);
++
++#if 0
++ /* the dma buffer loads should take care of clearing the AUTO
++ * reloading feature */
++ tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
++ tmp &= ~S3C2410_DCON_NORELOAD;
++ dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
++#endif
++
++ s3c2410_dma_call_op(chan, S3C2410_DMAOP_START);
++
++ dbg_showchan(chan);
++
++ local_irq_restore(flags);
++ return 0;
++}
++
++/* s3c2410_dma_canload
++ *
++ * work out if we can queue another buffer into the DMA engine
++*/
++
++static int
++s3c2410_dma_canload(s3c2410_dma_chan_t *chan)
++{
++ if (chan->load_state == S3C2410_DMALOAD_NONE ||
++ chan->load_state == S3C2410_DMALOAD_1RUNNING)
++ return 1;
++
++ return 0;
++}
++
++
++/* s3c2410_dma_enqueue
++ *
++ * queue an given buffer for dma transfer.
++ *
++ * id the device driver's id information for this buffer
++ * data the physical address of the buffer data
++ * size the size of the buffer in bytes
++ *
++ * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART
++ * is checked, and if set, the channel is started. If this flag isn't set,
++ * then an error will be returned.
++ *
++ * It is possible to queue more than one DMA buffer onto a channel at
++ * once, and the code will deal with the re-loading of the next buffer
++ * when necessary.
++*/
++
++int s3c2410_dma_enqueue(unsigned int channel, void *id,
++ dma_addr_t data, int size)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++ s3c2410_dma_buf_t *buf;
++ int flags;
++
++ check_channel(channel);
++
++ dmadbg(__FUNCTION__ ": id=%p, data=%08x, size=%d\n",
++ id, (unsigned int)data, size);
++
++ buf = (s3c2410_dma_buf_t *)kmalloc(sizeof(*buf), GFP_ATOMIC);
++ if (buf == NULL) {
++ dmadbg(__FUNCTION__ ": out of memory (%d alloc)\n",
++ sizeof(*buf));
++ return -ENOMEM;
++ }
++
++ dmadbg(__FUNCTION__ ": new buffer %p\n", buf);
++
++ //dbg_showchan(chan);
++
++ buf->next = NULL;
++ buf->data = buf->ptr = data;
++ buf->size = size;
++ buf->id = id;
++ buf->magic = BUF_MAGIC;
++
++ local_irq_save(flags);
++
++ if (chan->curr == NULL) {
++ /* we've got nothing loaded... */
++ dmadbg(__FUNCTION__ ": buffer %p queued onto empty channel\n",
++ buf);
++
++ chan->curr = buf;
++ chan->end = buf;
++ chan->next = NULL;
++ } else {
++ dmadbg(__FUNCTION__
++ ": buffer %p queued onto non-empty channel\n", buf);
++
++ if (chan->end == NULL)
++ dmadbg(__FUNCTION__
++ ": %p not empty, and chan->end==NULL?\n", chan);
++
++ chan->end->next = buf;
++ chan->end = buf;
++ }
++
++ /* if necessary, update the next buffer field */
++ if (chan->next == NULL)
++ chan->next = buf;
++
++ /* check to see if we can load a buffer */
++ if (chan->state == S3C2410_DMA_RUNNING) {
++ if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) {
++ if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
++ printk(KERN_ERR PFX ": loadbuffer:"
++ "timeout loading buffer\n",
++ chan->number);
++ dbg_showchan(chan);
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++ }
++
++ while (s3c2410_dma_canload(chan) && chan->next != NULL) {
++ s3c2410_dma_loadbuffer(chan, chan->next);
++ }
++ } else if (chan->state == S3C2410_DMA_IDLE) {
++ if (chan->flags & S3C2410_DMAF_AUTOSTART) {
++ s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START);
++ } else {
++ printk(KERN_DEBUG PFX "cannot load onto stopped channel'n", chan->number);
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++ }
++
++ local_irq_restore(flags);
++ return 0;
++}
++
++static inline void
++s3c2410_dma_freebuf(s3c2410_dma_buf_t *buf)
++{
++ int magicok = (buf->magic == BUF_MAGIC);
++
++ buf->magic = -1;
++
++ if (magicok) {
++ kfree(buf);
++ } else {
++ printk("s3c2410_dma_freebuf: buff %p with bad magic\n", buf);
++ }
++}
++
++/* s3c2410_dma_lastxfer
++ *
++ * called when the system is out of buffers, to ensure that the channel
++ * is prepared for shutdown.
++*/
++
++static inline void
++s3c2410_dma_lastxfer(s3c2410_dma_chan_t *chan)
++{
++ dmadbg("s3c2410_dma_lastxfer: load_state %d\n", chan->load_state);
++
++ switch (chan->load_state) {
++ case S3C2410_DMALOAD_NONE:
++ break;
++
++ case S3C2410_DMALOAD_1LOADED:
++ if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
++ /* flag error? */
++ printk(KERN_ERR PFX ": timeout waiting for load\n",
++ chan->number);
++ return;
++ }
++ break;
++
++ default:
++ dmadbg("lastxfer: unhandled load_state %d with no next",
++ chan->load_state);
++ return;
++
++ }
++
++ /* hopefully this'll shut the damned thing up after the transfer... */
++ dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD);
++}
++
++
++#define dmadbg2(x...)
++
++static void
++s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs)
++{
++ s3c2410_dma_chan_t *chan = (s3c2410_dma_chan_t *)devpw;
++ s3c2410_dma_buf_t *buf;
++
++ dmadbg2(__FUNCTION__ ": irq=%d, pw=%p, regs=%p\n", irq, devpw, regs);
++
++ buf = chan->curr;
++
++ dbg_showchan(chan);
++
++ /* modify the channel state */
++
++ switch (chan->load_state) {
++ case S3C2410_DMALOAD_1RUNNING:
++ /* TODO - if we are running only one buffer, we probably
++ * want to reload here, and then worry about the buffer
++ * callback */
++
++ chan->load_state = S3C2410_DMALOAD_NONE;
++ break;
++
++ case S3C2410_DMALOAD_1LOADED:
++ /* iirc, we should go back to NONE loaded here, we
++ * had a buffer, and it was never verified as being
++ * loaded.
++ */
++
++ chan->load_state = S3C2410_DMALOAD_NONE;
++ break;
++
++ case S3C2410_DMALOAD_1LOADED_1RUNNING:
++ /* we'll worry about checking to see if another buffer is
++ * ready after we've called back the owner. This should
++ * ensure we do not wait around too long for the DMA
++ * engine to start the next transfer
++ */
++
++ chan->load_state = S3C2410_DMALOAD_1LOADED;
++ break;
++
++ case S3C2410_DMALOAD_NONE:
++ printk(KERN_ERR PFX ": IRQ with no loaded buffer?\n",
++ chan->number);
++ break;
++
++ default:
++ printk(KERN_ERR PFX ": IRQ in invalid load_state %d\n",
++ chan->number, chan->load_state);
++ break;
++ }
++
++ if (buf != NULL) {
++ /* update the chain to make sure that if we load any more
++ * buffers when we call the callback function, things should
++ * work properly */
++
++ chan->curr = buf->next;
++ buf->next = NULL;
++
++ if (buf->magic != BUF_MAGIC) {
++ printk(KERN_ERR PFX __FUNCTION__
++ ": buf %p has incorrect magic\n",
++ chan->number, buf);
++ return;
++ }
++
++ s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK);
++
++ /* free resouces */
++ s3c2410_dma_freebuf(buf);
++ } else {
++ }
++
++ if (chan->next != NULL) {
++ unsigned long flags;
++
++ switch (chan->load_state) {
++ case S3C2410_DMALOAD_1RUNNING:
++ /* don't need to do anything for this state */
++ break;
++
++ case S3C2410_DMALOAD_NONE:
++ /* can load buffer immediately */
++ break;
++
++ case S3C2410_DMALOAD_1LOADED:
++ if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
++ /* flag error? */
++ printk(KERN_ERR PFX "timeout waiting for load\n",
++ chan->number);
++ return;
++ }
++
++ break;
++
++ default:
++ printk(KERN_ERR PFX "unknown load_state in irq, %d\n",
++ chan->number, chan->load_state);
++ return;
++ }
++
++ local_irq_save(flags);
++ s3c2410_dma_loadbuffer(chan, chan->next);
++ local_irq_restore(flags);
++ } else {
++ s3c2410_dma_lastxfer(chan);
++
++ /* see if we can stop this channel.. */
++ if (chan->load_state == S3C2410_DMALOAD_NONE) {
++ dmadbg("end of transfer, stopping channel (%ld)\n",
++ jiffies);
++ s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP);
++ }
++ }
++}
++
++
++
++/* s3c2410_request_dma
++ *
++ * get control of an dma channel
++*/
++
++int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client,
++ void *dev)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++ int err;
++ int flags;
++
++ dmadbg("s3c2410_request_dma: client=%s, dev=%p\n", client->name, dev);
++
++ check_channel(channel);
++
++ local_irq_save(flags);
++
++ dbg_showchan(chan);
++
++ if (chan->in_use) {
++ if (client != chan->client) {
++ printk(KERN_ERR PFX "channel is already in use\n", channel);
++ local_irq_restore(flags);
++ return -EBUSY;
++ } else {
++ printk(KERN_ERR PFX ": client already has channel\n", channel);
++ }
++ }
++
++ chan->client = client;
++ chan->in_use = 1;
++
++ if (!chan->irq_claimed) {
++ dmadbg(__FUNCTION__ ": requesting irq %d\n", chan->irq);
++
++ err = request_irq(chan->irq, s3c2410_dma_irq, SA_INTERRUPT,
++ client->name, (void *)chan);
++
++ if (err) {
++ chan->in_use = 0;
++ local_irq_restore(flags);
++
++ printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n",
++ client->name, chan->irq, chan->number);
++ return err;
++ }
++
++ chan->irq_claimed = 1;
++ chan->irq_enabled = 1;
++ }
++
++ local_irq_restore(flags);
++
++ /* need to setup */
++
++ dmadbg(__FUNCTION__ ": channel initialised, %p\n", chan);
++
++ return 0;
++}
++
++/* s3c2410_dma_free
++ *
++ * release the given channel back to the system, will stop and flush
++ * any outstanding transfers, and ensure the channel is ready for the
++ * next claimant.
++ *
++ * Note, although a warning is currently printed if the freeing client
++ * info is not the same as the registrant's client info, the free is still
++ * allowed to go through.
++*/
++
++int s3c2410_dma_free(dmach_t channel, s3c2410_dma_client_t *client)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++ unsigned long flags;
++
++ check_channel(channel);
++
++ local_irq_save(flags);
++
++
++ if (chan->client != client) {
++ printk(KERN_WARNING PFX ": possible channel free from different client (channel %p, passed %p)\n",
++ channel, chan->client, client);
++ }
++
++ /* sort out stopping and freeing the channel */
++
++ if (chan->state != S3C2410_DMA_IDLE) {
++ dmadbg(__FUNCTION__ ": need to stop dma channel %p\n", chan);
++
++ /* possibly flush the channel */
++ s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP);
++ }
++
++ chan->client = NULL;
++ chan->in_use = 0;
++
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan)
++{
++ unsigned long tmp;
++ int flags;
++
++ dmadbg("%s:\n", __FUNCTION__);
++
++ dbg_showchan(chan);
++
++ local_irq_save(flags);
++
++ s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP);
++
++ tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
++ tmp |= S3C2410_DMASKTRIG_STOP;
++ dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);
++
++#if 0
++ /* should also clear interrupts, according to WinCE BSP */
++ tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
++ tmp |= S3C2410_DCON_NORELOAD;
++ dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
++#endif
++
++ chan->state = S3C2410_DMA_IDLE;
++ chan->load_state = S3C2410_DMALOAD_NONE;
++
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++/* s3c2410_dma_flush
++ *
++ * stop the channel, and remove all current and pending transfers
++*/
++
++static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan)
++{
++ s3c2410_dma_buf_t *buf, *next;
++ unsigned long flags;
++
++ dmadbg("%s:\n", __FUNCTION__);
++
++ local_irq_save(flags);
++
++ if (chan->state != S3C2410_DMA_IDLE) {
++ dmadbg("%s:\n", __FUNCTION__ ": stopping channel...\n");
++ s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP);
++ }
++
++ buf = chan->curr;
++ if (buf == NULL)
++ buf = chan->next;
++
++ chan->curr = chan->next = chan->end = NULL;
++
++ if (buf != NULL) {
++ for ( ; buf != NULL; buf = next) {
++ next = buf->next;
++
++ dmadbg(__FUNCTION__ ": free buffer %p, next %p\n",
++ buf, buf->next);
++
++ s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT);
++ s3c2410_dma_freebuf(buf);
++ }
++ }
++
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++
++int
++s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++
++ check_channel(channel);
++
++ switch (op) {
++ case S3C2410_DMAOP_START:
++ return s3c2410_dma_start(chan);
++
++ case S3C2410_DMAOP_STOP:
++ return s3c2410_dma_dostop(chan);
++
++ case S3C2410_DMAOP_PAUSE:
++ return -ENOENT;
++
++ case S3C2410_DMAOP_RESUME:
++ return -ENOENT;
++
++ case S3C2410_DMAOP_FLUSH:
++ return s3c2410_dma_flush(chan);
++
++ case S3C2410_DMAOP_TIMEOUT:
++ return 0;
++
++ }
++
++ return -ENOENT; /* unknown, don't bother */
++}
++
++
++/* DMA configuration for each channel
++ *
++ * DISRCC -> source of the DMA (AHB,APB)
++ * DISRC -> source address of the DMA
++ * DIDSTC -> destination of the DMA (AHB,APD)
++ * DIDST -> destination address of the DMA
++*/
++
++/* s3c2410_dma_config
++ *
++ * xfersize: size of unit in bytes (1,2,4)
++ * dcon: base value of the DCONx register
++*/
++
++int s3c2410_dma_config(dmach_t channel,
++ int xferunit,
++ int dcon)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++
++ dmadbg(__FUNCTION__ ": chan=%d, xfer_unit=%d, dcon=%08x\n",
++ channel, xferunit, dcon);
++
++ check_channel(channel);
++
++ switch (xferunit) {
++ case 1:
++ dcon |= S3C2410_DCON_BYTE;
++ break;
++
++ case 2:
++ dcon |= S3C2410_DCON_HALFWORD;
++ break;
++
++ case 4:
++ dcon |= S3C2410_DCON_WORD;
++ break;
++
++ default:
++ /* todo: error bad transfer size */
++ dmadbg(__FUNCTION__ ": bad transfer size %d\n", xferunit);
++ return -EINVAL;
++ }
++
++ dcon |= S3C2410_DCON_HWTRIG;
++ dcon |= S3C2410_DCON_INTREQ;
++
++ dmadbg(__FUNCTION__ ": dcon now %08x\n", dcon);
++
++ chan->dcon = dcon;
++ chan->xfer_unit = xferunit;
++
++ return 0;
++}
++
++
++int s3c2410_dma_setflags(dmach_t channel, unsigned int flags)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++
++ check_channel(channel);
++
++ dmadbg(__FUNCTION__ ": chan=%p, flags=%08x\n", chan, flags);
++
++ chan->flags = flags;
++
++ return 0;
++}
++
++/* do we need to protect the settings of the fields from
++ * irq?
++*/
++
++int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++
++ check_channel(channel);
++
++ dmadbg(__FUNCTION__ ": chan=%p, op rtn=%p\n", chan, rtn);
++
++ chan->op_fn = rtn;
++
++ return 0;
++}
++
++int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++
++ check_channel(channel);
++
++ dmadbg(__FUNCTION__ ": chan=%p, callback rtn=%p\n", chan, rtn);
++
++ chan->callback_fn = rtn;
++
++ return 0;
++}
++
++/* s3c2410_dma_devconfig
++ *
++ * configure the dma source/destination hardware type and address
++ *
++ * source: S3C2410_DMASRC_HW: source is hardware
++ * S3C2410_DMASRC_MEM: source is memory
++ *
++ * hwcfg: the value for xxxSTCn register,
++ * bit 0: 0=increment pointer, 1=leave pointer
++ * bit 1: 0=soucre is AHB, 1=soucre is APB
++ *
++ * devaddr: physical address of the source
++*/
++
++int s3c2410_dma_devconfig(int channel,
++ s3c2410_dmasrc_t source,
++ int hwcfg,
++ unsigned long devaddr)
++{
++ s3c2410_dma_chan_t *chan = &s3c2410_chans[channel];
++
++ check_channel(channel);
++
++ dmadbg(__FUNCTION__ ": source=%d, hwcfg=%08x, devaddr=%08lx\n",
++ (int)source, hwcfg, devaddr);
++
++ chan->source = source;
++ chan->dev_addr = devaddr;
++
++ switch (source) {
++ case S3C2410_DMASRC_HW:
++ /* source is hardware */
++ dmadbg(__FUNCTION__ ": hw source, devaddr=%08lx, hwcfg=%d\n",
++ devaddr, hwcfg);
++ dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3);
++ dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr);
++ dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));
++
++ chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
++ return 0;
++
++ case S3C2410_DMASRC_MEM:
++ /* source is memory */
++ dmadbg(__FUNCTION__ ": mem source, devaddr=%08lx, hwcfg=%d\n",
++ devaddr, hwcfg);
++ dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0));
++ dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr);
++ dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);
++
++ chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
++ return 0;
++ }
++
++ printk(KERN_ERR PFX " invalid source type (%d)\n", channel, source);
++ return -EINVAL;
++}
++
++/* initialisation code */
++
++static int __init s3c2410_init_dma(void)
++{
++ int channel;
++ s3c2410_dma_chan_t *cp;
++
++ printk("S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics\n");
++
++ for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) {
++ cp = &s3c2410_chans[channel];
++
++ memset(cp, 0, sizeof(s3c2410_dma_chan_t));
++
++ /* dma channel irqs are in order.. */
++ cp->number = channel;
++ cp->irq = channel + IRQ_DMA0;
++ cp->regs = (unsigned long)ioremap(S3C2410_PA_DMA + (channel*0x40),
++ 0x40);
++
++ /* point current stats somewhere */
++ cp->stats = &cp->stats_store;
++ cp->stats_store.timeout_shortest = LONG_MAX;
++
++ /* basic channel configuration */
++
++ cp->load_timeout = 1<<18;
++
++ printk("DMA channel %d at %08lx, irq %d\n",
++ cp->number, cp->regs, cp->irq);
++ }
++
++ return 0;
++}
++
++__initcall(s3c2410_init_dma);
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/irq.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/irq.c
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/irq.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/irq.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,626 @@
++/* linux/arch/arm/mach-bast/irq.c
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/ptrace.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h> /* contains asm/arch/irqs.h */
++#include <asm/mach/irq.h>
++#include <asm/arch/irq.h>
++
++#include <asm/io.h>
++#include <asm/arch/map.h>
++#include <asm/arch-s3c2410/S3C2410-irq.h>
++#include <asm/arch-s3c2410/S3C2410-lcd.h>
++
++#define irqdbf(x)
++
++static void
++bast_s3c_mask(unsigned int irqno)
++{
++ unsigned long mask;
++
++ irqno -= IRQ_EINT0;
++
++ mask = __raw_readl(S3C2410_INTMSK);
++ mask |= 1UL << irqno;
++ __raw_writel(mask, S3C2410_INTMSK);
++}
++
++static inline void
++bast_s3c_ack(unsigned int bit)
++{
++ unsigned long bitval = (1UL << bit);
++
++ __raw_writel(bitval, S3C2410_SRCPND);
++ __raw_writel(bitval, S3C2410_INTPND);
++}
++
++static void
++bast_s3c_mask_ack(unsigned int irqno)
++{
++ bast_s3c_mask(irqno);
++ bast_s3c_ack(irqno - IRQ_EINT0);
++}
++
++static void
++bast_s3c_unmask(unsigned int irqno)
++{
++ unsigned long mask;
++
++ irqno -= IRQ_EINT0;
++
++ mask = __raw_readl(S3C2410_INTMSK);
++ mask &= ~(1UL << irqno);
++ __raw_writel(mask, S3C2410_INTMSK);
++
++}
++
++/* S3C2410_EINTMASK
++ * S3C2410_EINTPEND
++ */
++
++#define EXTINT_OFF (IRQ_EINT4 - 4)
++
++static void
++bast_extint_mask(unsigned int irqno)
++{
++ unsigned long mask;
++
++ irqno -= EXTINT_OFF;
++
++ mask = __raw_readl(S3C2410_EINTMASK);
++ mask |= ( 1UL << irqno);
++ __raw_writel(mask, S3C2410_EINTMASK);
++
++ if (irqno <= (IRQ_EINT7 - EXTINT_OFF)) {
++ /* check to see if all need masking */
++
++ if ((mask & (0xf << 4)) == (0xf << 4)) {
++ /* all masked, mask the parent */
++ bast_s3c_mask(IRQ_EINT4t7);
++ }
++ } else {
++ /* todo: the same check as above for the rest of the irq regs...*/
++
++ }
++}
++
++static void
++bast_extint_mask_ack(unsigned int irqno)
++{
++ unsigned long req;
++
++ //printk("bast_extint_mask_ack: irqno=%d\n", irqno);
++
++ bast_extint_mask(irqno);
++
++ irqno -= EXTINT_OFF;
++
++ __raw_writel((1UL << irqno), S3C2410_EINTPEND);
++ req = __raw_readl(S3C2410_EINTPEND);
++ req &= ~__raw_readl(S3C2410_EINTMASK);
++
++ if (irqno <= (IRQ_EINT7 - EXTINT_OFF)) {
++ if ((req & 0xf0) == 0)
++ bast_s3c_ack(IRQ_EINT4t7 - IRQ_EINT0);
++ } else {
++ if ((req >> 8) == 0)
++ bast_s3c_ack(IRQ_EINT8t23 - IRQ_EINT0);
++ }
++}
++
++static void
++bast_extint_unmask(unsigned int irqno)
++{
++ unsigned long mask;
++
++ //printk("bast_extint_unmask: irqno=%d\n", irqno);
++
++ irqno -= EXTINT_OFF;
++
++ mask = __raw_readl(S3C2410_EINTMASK);
++ mask &= ~( 1UL << irqno);
++ __raw_writel(mask, S3C2410_EINTMASK);
++
++ if (irqno <= (IRQ_EINT7 - EXTINT_OFF)) {
++ bast_s3c_unmask(IRQ_EINT4t7);
++ } else {
++ bast_s3c_unmask(IRQ_EINT8t23);
++ }
++}
++
++/* handle PC104 ISA interrupts from the system CPLD */
++
++/* table of ISA irq nos to the relevant mask... zero means
++ * the irq is not implemented
++*/
++static unsigned char bast_pc104_irqmasks[] = {
++ 0, /* 0 */
++ 0, /* 1 */
++ 0, /* 2 */
++ 1, /* 3 */
++ 0, /* 4 */
++ 2, /* 5 */
++ 0, /* 6 */
++ 4, /* 7 */
++ 0, /* 8 */
++ 0, /* 9 */
++ 8, /* 10 */
++ 0, /* 11 */
++ 0, /* 12 */
++ 0, /* 13 */
++ 0, /* 14 */
++ 0, /* 15 */
++};
++
++static void
++bast_pc104_mask(unsigned int irqno)
++{
++ unsigned long temp;
++
++ temp = __raw_readb(BAST_VA_PC104_IRQMASK);
++ temp &= ~bast_pc104_irqmasks[irqno];
++ __raw_writeb(temp, BAST_VA_PC104_IRQMASK);
++
++ if (temp == 0)
++ bast_extint_mask(IRQ_ISA);
++}
++
++static void
++bast_pc104_mask_ack(unsigned int irqno)
++{
++ unsigned long temp;
++
++ /* mask the actual interrupt */
++ temp = __raw_readb(BAST_VA_PC104_IRQMASK);
++ temp &= ~bast_pc104_irqmasks[irqno];
++ __raw_writeb(temp, BAST_VA_PC104_IRQMASK);
++
++ /* mask and ack the combo from the chip */
++ bast_extint_mask_ack(IRQ_ISA);
++}
++
++static void
++bast_pc104_unmask(unsigned int irqno)
++{
++ unsigned long temp;
++
++ temp = __raw_readb(BAST_VA_PC104_IRQMASK);
++ temp |= bast_pc104_irqmasks[irqno];
++ __raw_writeb(temp, BAST_VA_PC104_IRQMASK);
++
++ bast_extint_unmask(IRQ_ISA);
++}
++
++/* handle the interrupts generated from the main interrupt
++
++ * source on the CPU
++ */
++
++
++/* we have currently made a set of mask_ack routines that
++ * work for each different type of interrupt, the only thing that
++ * they assume is that they are called only from the interrupt handler
++ * as they tend to mask the entire parent irq for the sub source as well...
++ *
++ * it is possible that to speed things up we could do the same for the unmask
++ * code, and make one unmask for each of the parent interrupts...
++ *
++ * IMHO, this parent/sub interrupt relationship is annoying as it means
++ * checking a number of registers for each interrupt... why not have one
++ * big interrupt source and priority encoder?
++ */
++
++#define INTMSK_UART0 (1UL << (IRQ_UART0 - IRQ_EINT0))
++#define INTMSK_UART1 (1UL << (IRQ_UART1 - IRQ_EINT0))
++#define INTMSK_UART2 (1UL << (IRQ_UART2 - IRQ_EINT0))
++#define INTMSK_ADCPARENT (1UL << (IRQ_ADCPARENT - IRQ_EINT0))
++
++
++
++static void
++bast_int_sub_mask(unsigned int irqno)
++{
++ unsigned long mask, submask;
++
++ submask = __raw_readl(S3C2410_INTSUBMSK);
++ mask = __raw_readl(S3C2410_INTMSK);
++
++ submask |= (1UL << (irqno - IRQ_S3CUART_RX0));
++
++ irqdbf((__FUNCTION__ ": irqno=%d\n", irqno));
++
++ switch (irqno) {
++ case IRQ_S3CUART_RX0 ... IRQ_S3CUART_ERR0:
++ /* if all interrupts masked, mask the parent */
++ if (((submask >> (IRQ_S3CUART_RX0 - IRQ_S3CUART_RX0)) & 7) == 7)
++ mask |= INTMSK_UART0;
++ break;
++
++ case IRQ_S3CUART_RX1 ... IRQ_S3CUART_ERR1:
++ /* if all interrupts masked, mask the parent */
++ if (((submask >> (IRQ_S3CUART_RX1 - IRQ_S3CUART_RX0)) & 7) == 7)
++ mask |= INTMSK_UART1;
++ break;
++
++ case IRQ_S3CUART_RX2 ... IRQ_S3CUART_ERR2:
++ /* if all interrupts masked, mask the parent */
++ if (((submask >> (IRQ_S3CUART_RX2 - IRQ_S3CUART_RX0)) & 7) == 7)
++ mask |= INTMSK_UART2;
++ break;
++
++ case IRQ_ADC:
++ case IRQ_TC:
++ /* if all interrupts masked, mask the parent */
++ if (((submask >> (IRQ_TC - IRQ_S3CUART_RX0)) & 3) == 3)
++ mask |= (1UL << (IRQ_ADCPARENT - IRQ_EINT0));
++ break;
++ }
++
++ /* write back masks */
++ __raw_writel(submask, S3C2410_INTSUBMSK);
++ __raw_writel(mask, S3C2410_INTMSK);
++}
++
++static void
++bast_int_sub_unmask(unsigned int irqno)
++{
++ unsigned long mask, submask;
++
++ irqdbf((__FUNCTION__ ": irqno=%d\n", irqno));
++
++ submask = __raw_readl(S3C2410_INTSUBMSK);
++ mask = __raw_readl(S3C2410_INTMSK);
++
++ submask &= ~(1UL << (irqno - IRQ_S3CUART_RX0));
++
++ switch (irqno) {
++ case IRQ_S3CUART_RX0 ... IRQ_S3CUART_ERR0:
++ mask &= ~INTMSK_UART0;
++ break;
++
++ case IRQ_S3CUART_RX1 ... IRQ_S3CUART_ERR1:
++ mask &= ~INTMSK_UART1;
++ break;
++
++ case IRQ_S3CUART_RX2 ... IRQ_S3CUART_ERR2:
++ mask &= ~INTMSK_UART2;
++ break;
++
++ case IRQ_ADC:
++ case IRQ_TC:
++ mask &= ~INTMSK_ADCPARENT;
++ break;
++ }
++
++ /* write back masks */
++ __raw_writel(submask, S3C2410_INTSUBMSK);
++ __raw_writel(mask, S3C2410_INTMSK);
++}
++
++#define SUB_MASK_PARENT (0)
++
++static void
++bast_int_uart0_mask_ack(unsigned int irqno)
++{
++ unsigned long temp;
++ unsigned long maskbit;
++
++ irqno -= IRQ_S3CUART_RX0;
++ maskbit = 1UL << irqno;
++
++ irqdbf((__FUNCTION__ ": irqno=%d\n", irqno));
++
++ /* mask sub */
++ temp = __raw_readl(S3C2410_INTSUBMSK);
++ __raw_writel(temp | maskbit, S3C2410_INTSUBMSK);
++
++ __raw_writel(maskbit, S3C2410_SUBSRCPND);
++
++#if SUB_MASK_PARENT
++ /* we mask all to make our life easier, and possibly avoid the
++ * problem of acking the intterupt and missing another from the
++ * same sub-src */
++ temp = __raw_readl(S3C2410_INTMSK);
++ __raw_writel(temp | INTMSK_UART0, S3C2410_INTMSK);
++#endif
++
++ /* ack interrupt */
++ __raw_writel(INTMSK_UART0, S3C2410_SRCPND);
++ __raw_writel(INTMSK_UART0, S3C2410_INTPND);
++}
++
++static void
++bast_int_uart1_mask_ack(unsigned int irqno)
++{
++ unsigned long temp;
++ unsigned long maskbit;
++
++ irqno -= IRQ_S3CUART_RX0;
++ maskbit = 1UL << irqno;
++
++ irqdbf((__FUNCTION__ ": irqno=%d\n", irqno));
++
++ /* mask sub */
++ temp = __raw_readl(S3C2410_INTSUBMSK);
++ __raw_writel(temp | maskbit, S3C2410_INTSUBMSK);
++
++ __raw_writel(maskbit, S3C2410_SUBSRCPND);
++
++#if SUB_MASK_PARENT
++ /* we mask all to make our life easier, and possibly avoid the
++ * problem of acking the intterupt and missing another from the
++ * same sub-src */
++ temp = __raw_readl(S3C2410_INTMSK);
++ __raw_writel(temp | INTMSK_UART1, S3C2410_INTMSK);
++#endif
++
++ /* ack interrupt */
++ __raw_writel(INTMSK_UART1, S3C2410_SRCPND);
++ __raw_writel(INTMSK_UART1, S3C2410_INTPND);
++}
++
++static void
++bast_int_uart2_mask_ack(unsigned int irqno)
++{
++ unsigned long temp;
++ unsigned long maskbit;
++
++ irqno -= IRQ_S3CUART_RX0;
++ maskbit = 1UL << irqno;
++
++ irqdbf((__FUNCTION__ ": irqno=%d\n", irqno));
++
++ /* mask sub */
++ temp = __raw_readl(S3C2410_INTSUBMSK);
++ __raw_writel(temp | maskbit, S3C2410_INTSUBMSK);
++
++ __raw_writel(maskbit, S3C2410_SUBSRCPND);
++
++#if SUB_MASK_PARENT
++ /* we mask all to make our life easier, and possibly avoid the
++ * problem of acking the intterupt and missing another from the
++ * same sub-src */
++ temp = __raw_readl(S3C2410_INTMSK);
++ __raw_writel(temp | INTMSK_UART2, S3C2410_INTMSK);
++#endif
++
++ /* ack interrupt */
++ __raw_writel(INTMSK_UART2, S3C2410_SRCPND);
++ __raw_writel(INTMSK_UART2, S3C2410_INTPND);
++}
++
++static void
++bast_int_adcsub_mask_ack(unsigned int irqno)
++{
++ unsigned long temp;
++ unsigned long maskbit;
++
++ irqno -= IRQ_S3CUART_RX0;
++ maskbit = 1UL << irqno;
++
++ /* mask sub */
++ temp = __raw_readl(S3C2410_INTSUBMSK);
++ __raw_writel(temp | maskbit, S3C2410_INTSUBMSK);
++
++ __raw_writel(maskbit, S3C2410_SUBSRCPND);
++
++#if SUB_MASK_PARENT
++ /* we mask all to make our life easier, and possibly avoid the
++ * problem of acking the intterupt and missing another from the
++ * same sub-src */
++ temp = __raw_readl(S3C2410_INTMSK);
++ __raw_writel(temp | INTMSK_ADCPARENT, S3C2410_INTMSK);
++#endif
++
++ /* ack interrupt */
++ __raw_writel(INTMSK_ADCPARENT, S3C2410_SRCPND);
++ __raw_writel(INTMSK_ADCPARENT, S3C2410_INTPND);
++}
++
++#define INTMSK_LCDPARENT (1UL << (IRQ_LCD - IRQ_EINT0))
++
++static void
++bast_int_lcd_mask_ack(unsigned int irqno)
++{
++ unsigned long temp;
++ unsigned long maskbit;
++
++ irqno -= IRQ_LCD_FIFO;
++ maskbit = 1L << irqno;
++
++ temp = __raw_readl(S3C2410_LCDINTMSK);
++ __raw_writel(temp | maskbit, S3C2410_LCDINTMSK);
++
++ __raw_writel(maskbit, S3C2410_LCDSRCPND);
++ __raw_writel(maskbit, S3C2410_LCDINTPND);
++
++ __raw_writel(INTMSK_LCDPARENT, S3C2410_SRCPND);
++ __raw_writel(INTMSK_LCDPARENT, S3C2410_INTPND);
++}
++
++static void
++bast_int_lcd_mask(unsigned int irqno)
++{
++ unsigned long temp;
++ unsigned long maskbit;
++
++ irqno -= IRQ_LCD_FIFO;
++ maskbit = 1L << irqno;
++
++ temp = __raw_readl(S3C2410_LCDINTMSK);
++ __raw_writel(temp | maskbit, S3C2410_LCDINTMSK);
++}
++
++static void
++bast_int_lcd_unmask(unsigned int irqno)
++{
++ unsigned long temp;
++ unsigned long maskbit;
++
++ irqno -= IRQ_LCD_FIFO;
++ maskbit = 1L << irqno;
++
++ temp = __raw_readl(S3C2410_LCDINTMSK);
++ __raw_writel(temp & ~maskbit, S3C2410_LCDINTMSK);
++
++ bast_s3c_unmask(IRQ_LCD);
++}
++
++
++extern struct irqdesc irq_desc[NR_IRQS];
++
++void
++bast_init_irq(void)
++{
++ struct irqdesc *ptr;
++ unsigned long pend;
++ int irq;
++ int i;
++
++ ptr = irq_desc;
++
++ /* try and ensure all interrupts are acked, since they may be in
++ * an in-determinate state from initialisaiton
++ */
++
++ for (i = 0; i < 4; i++) {
++ pend = __raw_readl(S3C2410_EINTPEND);
++ if (pend == 0)
++ break;
++ __raw_writel(pend, S3C2410_EINTPEND);
++ printk("irq: clearing pending ext status %08x\n", (int)pend);
++ }
++
++ for (i = 0; i < 4; i++) {
++ pend = __raw_readl(S3C2410_INTPND);
++ if (pend == 0)
++ break;
++ __raw_writel(pend, S3C2410_SRCPND);
++ __raw_writel(pend, S3C2410_INTPND);
++ printk("irq: clearing pending status %08x\n", (int)pend);
++ }
++
++ for (i = 0; i < 4; i++) {
++ pend = __raw_readl(S3C2410_SUBSRCPND);
++
++ if (pend == 0)
++ break;
++
++ printk("irq: clearing subpending status %08x\n", (int)pend);
++ __raw_writel(pend, S3C2410_SUBSRCPND);
++ }
++
++ /* initialise irqs */
++
++ printk("bast_init_irq:\n");
++
++ for (irq = 0; irq < NR_IRQS; irq++, ptr++) {
++ switch (irq) {
++ case 3:
++ case 5:
++ case 7:
++ case 10:
++ /* IRQ from PC104 ISA */
++ if (machine_is_bast()) {
++ ptr->valid = 1;
++ ptr->probe_ok = 1;
++ ptr->mask_ack = bast_pc104_mask_ack;
++ ptr->mask = bast_pc104_mask;
++ ptr->unmask = bast_pc104_unmask;
++ }
++ break;
++
++ case IRQ_EINT0 ... IRQ_EINT3:
++ case IRQ_BATT_FLT ... IRQ_UART1:
++ case IRQ_USBD ... IRQ_ADCPARENT:
++ ptr->valid = 1;
++ ptr->probe_ok = 0;
++ ptr->mask_ack = bast_s3c_mask_ack;
++ ptr->mask = bast_s3c_mask;
++ ptr->unmask = bast_s3c_unmask;
++ break;
++
++ case IRQ_EINT4 ... IRQ_EINT23:
++ //printk("irqno %d is external interrupt\n", irq);
++
++ ptr->valid = 1;
++ ptr->probe_ok = 1;
++ ptr->mask_ack = bast_extint_mask_ack;
++ ptr->mask = bast_extint_mask;
++ ptr->unmask = bast_extint_unmask;
++ break;
++
++ case IRQ_S3CUART_RX0 ... IRQ_S3CUART_ERR0:
++ ptr->valid = 1;
++ ptr->probe_ok = 0;
++ ptr->mask_ack = bast_int_uart0_mask_ack;
++ ptr->mask = bast_int_sub_mask;
++ ptr->unmask = bast_int_sub_unmask;
++ break;
++
++ case IRQ_S3CUART_RX1 ... IRQ_S3CUART_ERR1:
++ ptr->valid = 1;
++ ptr->probe_ok = 0;
++ ptr->mask_ack = bast_int_uart1_mask_ack;
++ ptr->mask = bast_int_sub_mask;
++ ptr->unmask = bast_int_sub_unmask;
++ break;
++
++ case IRQ_S3CUART_RX2 ... IRQ_S3CUART_ERR2:
++ ptr->valid = 1;
++ ptr->probe_ok = 0;
++ ptr->mask_ack = bast_int_uart2_mask_ack;
++ ptr->mask = bast_int_sub_mask;
++ ptr->unmask = bast_int_sub_unmask;
++ break;
++
++ case IRQ_LCD_FIFO:
++ case IRQ_LCD_FRAME:
++ ptr->valid = 1;
++ ptr->probe_ok = 0;
++ ptr->mask_ack = bast_int_lcd_mask_ack;
++ ptr->mask = bast_int_lcd_mask;
++ ptr->unmask = bast_int_lcd_unmask;
++ break;
++
++ case IRQ_ADC:
++ case IRQ_TC:
++ ptr->valid = 1;
++ ptr->probe_ok = 0;
++ ptr->mask_ack = bast_int_adcsub_mask_ack;
++ ptr->mask = bast_int_sub_mask;
++ ptr->unmask = bast_int_sub_unmask;
++ break;
++
++ default:
++
++ }
++ }
++}
++
++
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/mm.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/mm.c
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/mm.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/mm.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,209 @@
++
++/* linux/arch/arm/mach-bast/mm.c
++ *
++ * Copyright (C) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST memory configuration (http://www.simtec.co.uk/products/EB110ITX/)
++ *
++ * 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
++ *
++ * Changelog:
++ * 12-May-2003 BJD Created file
++ * 19-Aug-2003 BJD Cleanup, header fix, added URL
++ *
++ */
++
++#include <linux/sched.h>
++#include <linux/mm.h>
++#include <linux/init.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/pgtable.h>
++#include <asm/page.h>
++#include <asm/mach-types.h>
++
++#include <asm/mach/map.h>
++
++/* include address info */
++#include <asm/arch/map.h>
++
++#include <asm/arch-s3c2410/S3C2410-gpio.h>
++#include <asm/arch-s3c2410/S3C2410-clock.h>
++
++/* macros for virtual address mods for the io space entries */
++#define VA_C5(item) ((item) + BAST_VAM_CS5)
++#define VA_C4(item) ((item) + BAST_VAM_CS4)
++#define VA_C3(item) ((item) + BAST_VAM_CS3)
++#define VA_C2(item) ((item) + BAST_VAM_CS2)
++
++/* macros to modify the physical addresses for io space */
++
++#define PA_CS2(item) ((item) + S3C2410_CS2)
++#define PA_CS3(item) ((item) + S3C2410_CS3)
++#define PA_CS4(item) ((item) + S3C2410_CS4)
++#define PA_CS5(item) ((item) + S3C2410_CS5)
++
++/* if we didn't have so many things to fit in, we could have just done the
++ * whole lot in one go... however we need to get some of the space that
++ * is inbetween the physical registers back to use elsewhere...
++*/
++
++static struct map_desc bast_io_desc[] __initdata = {
++ { S3C2410_VA_IRQ, S3C2410_PA_IRQ, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_MEMCTRL, S3C2410_PA_MEMCTRL, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_USBHOST, S3C2410_PA_USBHOST, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_DMA, S3C2410_PA_DMA, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_CLKPWR, S3C2410_PA_CLKPWR, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_LCD, S3C2410_PA_LCD, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_NAND, S3C2410_PA_NAND, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_UART, S3C2410_PA_UART, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_TIMER, S3C2410_PA_TIMER, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_USBDEV, S3C2410_PA_USBDEV, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_WATCHDOG, S3C2410_PA_WATCHDOG, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_IIC, S3C2410_PA_IIC, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_IIS, S3C2410_PA_IIS, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_GPIO, S3C2410_PA_GPIO, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_RTC, S3C2410_PA_RTC, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_ADC, S3C2410_PA_ADC, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_SPI, S3C2410_PA_SPI, SZ_1M, DOMAIN_IO, 0, 1 },
++ { S3C2410_VA_SDI, S3C2410_PA_SDI, SZ_1M, DOMAIN_IO, 0, 1 },
++
++ /* we could possibly compress the next set down into a set of smaller tables
++ * pagetables, but that would mean using an L2 section, and it still means
++ * we cannot actually feed the same register to an LDR due to 16K spacing
++ */
++
++ /* bast CPLD control registers, and external interrupt controls */
++ { BAST_VA_CTRL1, BAST_PA_CTRL1, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_CTRL2, BAST_PA_CTRL2, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_CTRL3, BAST_PA_CTRL3, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_CTRL4, BAST_PA_CTRL4, SZ_1M, DOMAIN_IO, 0, 1 },
++
++ /* PC104 IRQ mux */
++ { BAST_VA_PC104_IRQREQ, BAST_PA_PC104_IRQREQ, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_PC104_IRQRAW, BAST_PA_PC104_IRQRAW, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_PC104_IRQMASK, BAST_PA_PC104_IRQMASK, SZ_1M, DOMAIN_IO, 0, 1 },
++
++ /* onboard 8bit lcd port */
++
++ { BAST_VA_LCD_RCMD1, BAST_PA_LCD_RCMD1, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_LCD_WCMD1, BAST_PA_LCD_WCMD1, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_LCD_RDATA1, BAST_PA_LCD_RDATA1, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_LCD_WDATA1, BAST_PA_LCD_WDATA1, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_LCD_RCMD2, BAST_PA_LCD_RCMD2, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_LCD_WCMD2, BAST_PA_LCD_WCMD2, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_LCD_RDATA2, BAST_PA_LCD_RDATA2, SZ_1M, DOMAIN_IO, 0, 1 },
++ { BAST_VA_LCD_WDATA2, BAST_PA_LCD_WDATA2, SZ_1M, DOMAIN_IO, 0, 1 },
++
++ /* peripheral space... one for each of fast/slow/byte/16bit */
++ /* note, ide is only decoded in word space, even though some registers
++ * are only 8bit */
++
++ /* slow, byte */
++ { VA_C2(BAST_VA_ISAIO), PA_CS2(BAST_PA_ISAIO), SZ_16M, DOMAIN_IO, 0,1 },
++ { VA_C2(BAST_VA_ISAMEM), PA_CS2(BAST_PA_ISAMEM), SZ_16M, DOMAIN_IO, 0,1 },
++ { VA_C2(BAST_VA_ASIXNET), PA_CS3(BAST_PA_ASIXNET), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C2(BAST_VA_SUPERIO), PA_CS2(BAST_PA_SUPERIO), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C2(BAST_VA_DM9000), PA_CS2(BAST_PA_DM9000), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C2(BAST_VA_IDEPRI), PA_CS3(BAST_PA_IDEPRI), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C2(BAST_VA_IDESEC), PA_CS3(BAST_PA_IDESEC), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C2(BAST_VA_IDEPRIAUX), PA_CS3(BAST_PA_IDEPRIAUX), SZ_1M, DOMAIN_IO,0,1 },
++ { VA_C2(BAST_VA_IDESECAUX), PA_CS3(BAST_PA_IDESECAUX), SZ_1M, DOMAIN_IO,0,1 },
++
++ /* slow, word */
++ { VA_C3(BAST_VA_ISAIO), PA_CS3(BAST_PA_ISAIO), SZ_16M, DOMAIN_IO, 0,1 },
++ { VA_C3(BAST_VA_ISAMEM), PA_CS3(BAST_PA_ISAMEM), SZ_16M, DOMAIN_IO, 0,1 },
++ { VA_C3(BAST_VA_ASIXNET), PA_CS3(BAST_PA_ASIXNET), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C3(BAST_VA_SUPERIO), PA_CS3(BAST_PA_SUPERIO), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C3(BAST_VA_DM9000), PA_CS3(BAST_PA_DM9000), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C3(BAST_VA_IDEPRI), PA_CS3(BAST_PA_IDEPRI), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C3(BAST_VA_IDESEC), PA_CS3(BAST_PA_IDESEC), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C3(BAST_VA_IDEPRIAUX), PA_CS3(BAST_PA_IDEPRIAUX), SZ_1M, DOMAIN_IO,0,1 },
++ { VA_C3(BAST_VA_IDESECAUX), PA_CS3(BAST_PA_IDESECAUX), SZ_1M, DOMAIN_IO,0,1 },
++
++ /* fast, byte */
++ { VA_C4(BAST_VA_ISAIO), PA_CS4(BAST_PA_ISAIO), SZ_16M, DOMAIN_IO, 0,1 },
++ { VA_C4(BAST_VA_ISAMEM), PA_CS4(BAST_PA_ISAMEM), SZ_16M, DOMAIN_IO, 0,1 },
++ { VA_C4(BAST_VA_ASIXNET), PA_CS5(BAST_PA_ASIXNET), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C4(BAST_VA_SUPERIO), PA_CS4(BAST_PA_SUPERIO), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C4(BAST_VA_DM9000), PA_CS4(BAST_PA_DM9000), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C4(BAST_VA_IDEPRI), PA_CS5(BAST_PA_IDEPRI), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C4(BAST_VA_IDESEC), PA_CS5(BAST_PA_IDESEC), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C4(BAST_VA_IDEPRIAUX), PA_CS5(BAST_PA_IDEPRIAUX), SZ_1M, DOMAIN_IO,0,1 },
++ { VA_C4(BAST_VA_IDESECAUX), PA_CS5(BAST_PA_IDESECAUX), SZ_1M, DOMAIN_IO,0,1 },
++
++ /* fast, word */
++ { VA_C5(BAST_VA_ISAIO), PA_CS5(BAST_PA_ISAIO), SZ_16M, DOMAIN_IO, 0,1 },
++ { VA_C5(BAST_VA_ISAMEM), PA_CS5(BAST_PA_ISAMEM), SZ_16M, DOMAIN_IO, 0,1 },
++ { VA_C5(BAST_VA_ASIXNET), PA_CS5(BAST_PA_ASIXNET), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C5(BAST_VA_SUPERIO), PA_CS5(BAST_PA_SUPERIO), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C5(BAST_VA_DM9000), PA_CS5(BAST_PA_DM9000), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C5(BAST_VA_IDEPRI), PA_CS5(BAST_PA_IDEPRI), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C5(BAST_VA_IDESEC), PA_CS5(BAST_PA_IDESEC), SZ_1M, DOMAIN_IO, 0,1 },
++ { VA_C5(BAST_VA_IDEPRIAUX), PA_CS5(BAST_PA_IDEPRIAUX), SZ_1M, DOMAIN_IO,0,1 },
++ { VA_C5(BAST_VA_IDESECAUX), PA_CS5(BAST_PA_IDESECAUX), SZ_1M, DOMAIN_IO,0,1 },
++
++ LAST_DESC
++};
++
++static struct map_desc vr1000_io_desc[] __initdata = {
++ { VR1000_VA_DEBUG, VR1000_PA_DEBUG, SZ_1M, DOMAIN_IO, 0, 1 },
++ LAST_DESC
++};
++
++/* clock info */
++
++int s3c2410_fclk;
++int s3c2410_hclk;
++int s3c2410_pclk;
++
++#ifndef MHZ
++#define MHZ (1000*1000)
++#endif
++
++#define print_mhz(m) ((m) / MHZ), ((m) % MHZ)
++
++void __init bast_map_io(void)
++{
++ unsigned long tmp;
++
++ //printk("Initialising IO memory map:\n");
++ iotable_init(bast_io_desc);
++
++
++ if (machine_is_vr1000()) {
++ iotable_init(vr1000_io_desc);
++ //printascii("initialised vr1000 io descriptors\n");
++ }
++
++ s3c2410_fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), 12*MHZ);
++
++ tmp = __raw_readl(S3C2410_CLKDIVN);
++ //printk("tmp=%08x, fclk=%d\n", tmp, s3c2410_fclk);
++
++ /* work out clock scalings */
++
++ s3c2410_hclk = s3c2410_fclk / ((tmp & S3C2410_CLKDIVN_HDIVN) ? 2 : 1);
++ s3c2410_pclk = s3c2410_hclk / ((tmp & S3C2410_CLKDIVN_PDIVN) ? 2 : 1);
++
++ printk("S3C2410: %d.%06d MHz, memory %d.%06d MHz, pclk %d.%06d MHz\n",
++ print_mhz(s3c2410_fclk), print_mhz(s3c2410_hclk),
++ print_mhz(s3c2410_pclk));
++}
++
++
++
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/pcipool.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/pcipool.c
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/pcipool.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/pcipool.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,378 @@
++/*
++ NOTE:
++
++ this code was lifted straight out of drivers/pci/pci.c;
++ when compiling for the SAMSUNG UBS Controller
++ usb-ohci.c driver needs these routines even when the architecture
++ has no pci bus...
++ From Ni PalDuk Gulga
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/pci.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/bitops.h>
++
++#include <asm/page.h>
++
++#include "pcipool.h"
++
++/*
++ * Pool allocator ... wraps the pci_alloc_consistent page allocator, so
++ * small blocks are easily used by drivers for bus mastering controllers.
++ * This should probably be sharing the guts of the slab allocator.
++ */
++
++struct pci_pool { /* the pool */
++ struct list_head page_list;
++ spinlock_t lock;
++ size_t blocks_per_page;
++ size_t size;
++ int flags;
++ struct pci_dev *dev;
++ size_t allocation;
++ char name [32];
++ wait_queue_head_t waitq;
++};
++
++struct pci_page { /* cacheable header for 'allocation' bytes */
++ struct list_head page_list;
++ void *vaddr;
++ dma_addr_t dma;
++ unsigned long bitmap [0];
++};
++
++#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000)
++#define POOL_POISON_BYTE 0xa7
++
++// #define CONFIG_PCIPOOL_DEBUG
++
++
++/**
++ * pci_pool_create - Creates a pool of pci consistent memory blocks, for dma.
++ * @name: name of pool, for diagnostics
++ * @pdev: pci device that will be doing the DMA
++ * @size: size of the blocks in this pool.
++ * @align: alignment requirement for blocks; must be a power of two
++ * @allocation: returned blocks won't cross this boundary (or zero)
++ * @flags: SLAB_* flags (not all are supported).
++ *
++ * Returns a pci allocation pool with the requested characteristics, or
++ * null if one can't be created. Given one of these pools, pci_pool_alloc()
++ * may be used to allocate memory. Such memory will all have "consistent"
++ * DMA mappings, accessible by the device and its driver without using
++ * cache flushing primitives. The actual size of blocks allocated may be
++ * larger than requested because of alignment.
++ *
++ * If allocation is nonzero, objects returned from pci_pool_alloc() won't
++ * cross that size boundary. This is useful for devices which have
++ * addressing restrictions on individual DMA transfers, such as not crossing
++ * boundaries of 4KBytes.
++ */
++struct pci_pool *
++pci_pool_create (const char *name, struct pci_dev *pdev,
++ size_t size, size_t align, size_t allocation, int flags)
++{
++ struct pci_pool *retval;
++
++ if (align == 0)
++ align = 1;
++ if (size == 0)
++ return 0;
++ else if (size < align)
++ size = align;
++ else if ((size % align) != 0) {
++ size += align + 1;
++ size &= ~(align - 1);
++ }
++
++ if (allocation == 0) {
++ if (PAGE_SIZE < size)
++ allocation = size;
++ else
++ allocation = PAGE_SIZE;
++ // FIXME: round up for less fragmentation
++ } else if (allocation < size)
++ return 0;
++
++ if (!(retval = kmalloc (sizeof *retval, flags)))
++ return retval;
++
++#ifdef CONFIG_PCIPOOL_DEBUG
++ flags |= SLAB_POISON;
++#endif
++
++ strncpy (retval->name, name, sizeof retval->name);
++ retval->name [sizeof retval->name - 1] = 0;
++
++ retval->dev = pdev;
++ INIT_LIST_HEAD (&retval->page_list);
++ spin_lock_init (&retval->lock);
++ retval->size = size;
++ retval->flags = flags;
++ retval->allocation = allocation;
++ retval->blocks_per_page = allocation / size;
++ init_waitqueue_head (&retval->waitq);
++
++#ifdef CONFIG_PCIPOOL_DEBUG
++ printk (KERN_DEBUG "pcipool create %s/%s size %d, %d/page (%d alloc)\n",
++ pdev ? pdev->slot_name : NULL, retval->name, size,
++ retval->blocks_per_page, allocation);
++#endif
++
++ return retval;
++}
++
++
++static struct pci_page *
++pool_alloc_page (struct pci_pool *pool, int mem_flags)
++{
++ struct pci_page *page;
++ int mapsize;
++
++ mapsize = pool->blocks_per_page;
++ mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG;
++ mapsize *= sizeof (long);
++
++ page = (struct pci_page *) kmalloc (mapsize + sizeof *page, mem_flags);
++ if (!page)
++ return 0;
++ page->vaddr = pci_alloc_consistent (pool->dev,
++ pool->allocation, &page->dma);
++ if (page->vaddr) {
++ memset (page->bitmap, 0xff, mapsize); // bit set == free
++ if (pool->flags & SLAB_POISON)
++ memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
++ list_add (&page->page_list, &pool->page_list);
++ } else {
++ kfree (page);
++ page = 0;
++ }
++ return page;
++}
++
++
++static inline int
++is_page_busy (int blocks, unsigned long *bitmap)
++{
++ while (blocks > 0) {
++ if (*bitmap++ != ~0UL)
++ return 1;
++ blocks -= BITS_PER_LONG;
++ }
++ return 0;
++}
++
++static void
++pool_free_page (struct pci_pool *pool, struct pci_page *page)
++{
++ dma_addr_t dma = page->dma;
++
++ if (pool->flags & SLAB_POISON)
++ memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
++ pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma);
++ list_del (&page->page_list);
++ kfree (page);
++}
++
++
++/**
++ * pci_pool_destroy - destroys a pool of pci memory blocks.
++ * @pool: pci pool that will be destroyed
++ *
++ * Caller guarantees that no more memory from the pool is in use,
++ * and that nothing will try to use the pool after this call.
++ */
++void
++pci_pool_destroy (struct pci_pool *pool)
++{
++ unsigned long flags;
++
++#ifdef CONFIG_PCIPOOL_DEBUG
++ printk (KERN_DEBUG "pcipool destroy %s/%s\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name);
++#endif
++
++ spin_lock_irqsave (&pool->lock, flags);
++ while (!list_empty (&pool->page_list)) {
++ struct pci_page *page;
++ page = list_entry (pool->page_list.next,
++ struct pci_page, page_list);
++ if (is_page_busy (pool->blocks_per_page, page->bitmap)) {
++ printk (KERN_ERR "pci_pool_destroy %s/%s, %p busy\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name, page->vaddr);
++ /* leak the still-in-use consistent memory */
++ list_del (&page->page_list);
++ kfree (page);
++ } else
++ pool_free_page (pool, page);
++ }
++ spin_unlock_irqrestore (&pool->lock, flags);
++ kfree (pool);
++}
++
++
++/**
++ * pci_pool_alloc - get a block of consistent memory
++ * @pool: pci pool that will produce the block
++ * @mem_flags: SLAB_KERNEL or SLAB_ATOMIC
++ * @handle: pointer to dma address of block
++ *
++ * This returns the kernel virtual address of a currently unused block,
++ * and reports its dma address through the handle.
++ * If such a memory block can't be allocated, null is returned.
++ */
++void *
++pci_pool_alloc (struct pci_pool *pool, int mem_flags, dma_addr_t *handle)
++{
++ unsigned long flags;
++ struct list_head *entry;
++ struct pci_page *page;
++ int map, block;
++ size_t offset;
++ void *retval;
++
++restart:
++ spin_lock_irqsave (&pool->lock, flags);
++ list_for_each (entry, &pool->page_list) {
++ int i;
++ page = list_entry (entry, struct pci_page, page_list);
++ /* only cachable accesses here ... */
++ for (map = 0, i = 0;
++ i < pool->blocks_per_page;
++ i += BITS_PER_LONG, map++) {
++ if (page->bitmap [map] == 0)
++ continue;
++ block = ffz (~ page->bitmap [map]);
++ if ((i + block) < pool->blocks_per_page) {
++ clear_bit (block, &page->bitmap [map]);
++ offset = (BITS_PER_LONG * map) + block;
++ offset *= pool->size;
++ goto ready;
++ }
++ }
++ }
++ if (!(page = pool_alloc_page (pool, mem_flags))) {
++ if (mem_flags == SLAB_KERNEL) {
++ DECLARE_WAITQUEUE (wait, current);
++
++ current->state = TASK_INTERRUPTIBLE;
++ add_wait_queue (&pool->waitq, &wait);
++ spin_unlock_irqrestore (&pool->lock, flags);
++
++ schedule_timeout (POOL_TIMEOUT_JIFFIES);
++
++ current->state = TASK_RUNNING;
++ remove_wait_queue (&pool->waitq, &wait);
++ goto restart;
++ }
++ retval = 0;
++ goto done;
++ }
++
++ clear_bit (0, &page->bitmap [0]);
++ offset = 0;
++ready:
++ retval = offset + page->vaddr;
++ *handle = offset + page->dma;
++done:
++ spin_unlock_irqrestore (&pool->lock, flags);
++ return retval;
++}
++
++
++static struct pci_page *
++pool_find_page (struct pci_pool *pool, dma_addr_t dma)
++{
++ unsigned long flags;
++ struct list_head *entry;
++ struct pci_page *page;
++
++ spin_lock_irqsave (&pool->lock, flags);
++ list_for_each (entry, &pool->page_list) {
++ page = list_entry (entry, struct pci_page, page_list);
++ if (dma < page->dma)
++ continue;
++ if (dma < (page->dma + pool->allocation))
++ goto done;
++ }
++ page = 0;
++done:
++ spin_unlock_irqrestore (&pool->lock, flags);
++ return page;
++}
++
++
++/**
++ * pci_pool_free - put block back into pci pool
++ * @pool: the pci pool holding the block
++ * @vaddr: virtual address of block
++ * @dma: dma address of block
++ *
++ * Caller promises neither device nor driver will again touch this block
++ * unless it is first re-allocated.
++ */
++void
++pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma)
++{
++ struct pci_page *page;
++ unsigned long flags;
++ int map, block;
++
++ if ((page = pool_find_page (pool, dma)) == 0) {
++ printk (KERN_ERR "pci_pool_free %s/%s, %p/%x (bad dma)\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name, vaddr, dma);
++ return;
++ }
++#ifdef CONFIG_PCIPOOL_DEBUG
++ if (((dma - page->dma) + (void *)page->vaddr) != vaddr) {
++ printk (KERN_ERR "pci_pool_free %s/%s, %p (bad vaddr)/%x\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name, vaddr, dma);
++ return;
++ }
++#endif
++
++ block = dma - page->dma;
++ block /= pool->size;
++ map = block / BITS_PER_LONG;
++ block %= BITS_PER_LONG;
++
++#ifdef CONFIG_PCIPOOL_DEBUG
++ if (page->bitmap [map] & (1UL << block)) {
++ printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name, dma);
++ return;
++ }
++#endif
++ if (pool->flags & SLAB_POISON)
++ memset (vaddr, POOL_POISON_BYTE, pool->size);
++
++ spin_lock_irqsave (&pool->lock, flags);
++ set_bit (block, &page->bitmap [map]);
++ if (waitqueue_active (&pool->waitq))
++ wake_up (&pool->waitq);
++ /*
++ * Resist a temptation to do
++ * if (!is_page_busy(bpp, page->bitmap)) pool_free_page(pool, page);
++ * it is not interrupt safe. Better have empty pages hang around.
++ */
++ spin_unlock_irqrestore (&pool->lock, flags);
++}
++
++
++EXPORT_SYMBOL (pci_pool_create);
++EXPORT_SYMBOL (pci_pool_destroy);
++EXPORT_SYMBOL (pci_pool_alloc);
++EXPORT_SYMBOL (pci_pool_free);
++
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/pcipool.h kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/pcipool.h
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/pcipool.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/pcipool.h 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,8 @@
++
++struct pci_pool *pci_pool_create (const char *name, struct pci_dev *dev,
++ size_t size, size_t align, size_t allocation, int flags);
++void pci_pool_destroy (struct pci_pool *pool);
++
++void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle);
++void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr);
++
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-bast/s3c2410-pcibuf.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/s3c2410-pcibuf.c
+--- kernel-source-2.4.27-8/arch/arm/mach-bast/s3c2410-pcibuf.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-bast/s3c2410-pcibuf.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,270 @@
++/*
++ * linux/arch/arm/mach-s3c2410.c
++ * Special pci_map/unmap_single routines for S3C2410
++ *
++ * SW.LEE <hitchcar at sec.samsung.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.
++ *
++ * 06/13/2001 - created.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++
++#include "pcipool.h"
++
++/*
++ * simple buffer allocator for copying of unsafe to safe buffers
++ * uses __alloc/__free for actual buffers
++ * keeps track of safe buffers we've allocated so we can recover the
++ * unsafe buffers.
++ */
++
++#define MAX_SAFE 32
++#define SIZE_SMALL 1024
++#define SIZE_LARGE (16*1024)
++
++static long mapped_alloc_size;
++static char *safe_buffers[MAX_SAFE][2];
++
++
++static struct pci_pool *small_buffer_cache, *large_buffer_cache;
++
++static int
++init_safe_buffers(struct pci_dev *dev)
++{
++ small_buffer_cache = pci_pool_create("pci_small_buffer",
++ dev,
++ SIZE_SMALL,
++ 0 /* byte alignment */,
++ 0 /* no page-crossing issues */,
++ GFP_KERNEL | GFP_DMA);
++
++ if (small_buffer_cache == 0)
++ return -1;
++
++ large_buffer_cache = pci_pool_create("pci_large_buffer",
++ dev,
++ SIZE_LARGE,
++ 0 /* byte alignment */,
++ 0 /* no page-crossing issues */,
++ GFP_KERNEL | GFP_DMA);
++ if (large_buffer_cache == 0)
++ return -1;
++
++ return 0;
++}
++
++/* allocate a 'safe' buffer and keep track of it */
++static char *
++alloc_safe_buffer(char *unsafe, int size, dma_addr_t *pbus)
++{
++ char *safe;
++ dma_addr_t busptr;
++ struct pci_pool *pool;
++ int i;
++
++ if (0) printk("alloc_safe_buffer(size=%d)\n", size);
++
++ if (size <= SIZE_SMALL)
++ pool = small_buffer_cache;
++ else
++ if (size < SIZE_LARGE)
++ pool = large_buffer_cache;
++ else
++ return 0;
++
++ safe = pci_pool_alloc(pool, SLAB_ATOMIC, &busptr);
++ if (safe == 0)
++ return 0;
++
++ for (i = 0; i < MAX_SAFE; i++)
++ if (safe_buffers[i][0] == 0) {
++ break;
++ }
++
++ if (i == MAX_SAFE) {
++ panic(__FILE__ ": exceeded MAX_SAFE buffers");
++ }
++
++ /* place the size index and the old buffer ptr in the first 8 bytes
++ * and return a ptr + 12 to caller
++ */
++ ((int *)safe)[0] = i;
++ ((char **)safe)[1] = (char *)pool;
++ ((char **)safe)[2] = unsafe;
++
++ busptr += sizeof(int) + sizeof(char *) + sizeof(char *);
++
++ safe_buffers[i][0] = (void *)busptr;
++ safe_buffers[i][1] = (void *)safe;
++
++ safe += sizeof(int) + sizeof(char *) + sizeof(char *);
++
++ *pbus = busptr;
++ return safe;
++}
++
++/* determine if a buffer is from our "safe" pool */
++static char *
++find_safe_buffer(char *busptr, char **unsafe)
++{
++ int i;
++ char *buf;
++
++ for (i = 0; i < MAX_SAFE; i++) {
++ if (safe_buffers[i][0] == busptr) {
++ if (0) printk("find_safe_buffer(%p) found @ %d\n", busptr, i);
++ buf = safe_buffers[i][1];
++ *unsafe = ((char **)buf)[2];
++ return buf + sizeof(int) + sizeof(char *) + sizeof(char *);
++ }
++ }
++
++ return (char *)0;
++}
++
++static void
++free_safe_buffer(char *buf)
++{
++ int index;
++ struct pci_pool *pool;
++ char *dma;
++
++ if (0) printk("free_safe_buffer(buf=%p)\n", buf);
++
++ /* retrieve the buffer size index */
++ buf -= sizeof(int) + sizeof(char*) + sizeof(char*);
++ index = ((int *)buf)[0];
++ pool = (struct pci_pool *)((char **)buf)[1];
++
++ if (0) printk("free_safe_buffer(%p) index %d\n",
++ buf, index);
++
++ if (index < 0 || index >= MAX_SAFE) {
++ printk(__FILE__ ": free_safe_buffer() corrupt buffer\n");
++ return;
++ }
++
++ dma = safe_buffers[index][0];
++ safe_buffers[index][0] = 0;
++
++ pci_pool_free(pool, buf, (u32)dma);
++}
++
++/*
++ NOTE:
++ replace pci_map/unmap_single with local routines which will
++ do buffer copies if buffer is above 1mb...
++*/
++
++/*
++ * see if a buffer address is in an 'unsafe' range. if it is
++ * allocate a 'safe' buffer and copy the unsafe buffer into it.
++ * substitute the safe buffer for the unsafe one.
++ * (basically move the buffer from an unsafe area to a safe one)
++ *
++ * we assume calls to map_single are symmetric with calls to unmap_single...
++ */
++dma_addr_t
++s3c2410_map_single(struct pci_dev *hwdev, void *virtptr,
++ size_t size, int direction)
++{
++ dma_addr_t busptr;
++
++ mapped_alloc_size += size;
++
++ if (0) printk("pci_map_single(hwdev=%p,ptr=%p,size=%d,dir=%x) "
++ "alloced=%ld\n",
++ hwdev, virtptr, size, direction, mapped_alloc_size);
++
++ busptr = virt_to_bus(virtptr);
++
++ /* we assume here that a buffer will never be >=64k */
++ if ( (((unsigned long)busptr) & 0x100000) ||
++ ((((unsigned long)busptr)+size) & 0x100000) )
++ {
++ char *safe;
++
++ safe = alloc_safe_buffer(virtptr, size, &busptr);
++ if (safe == 0) {
++ printk("unable to map unsafe buffer %p!\n", virtptr);
++ return 0;
++ }
++
++ if (0) printk("unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
++ virtptr, (void *)virt_to_bus(virtptr),
++ safe, (void *)busptr);
++
++ memcpy(safe, virtptr, size);
++ consistent_sync(safe, size, direction);
++
++ return busptr;
++ }
++
++ consistent_sync(virtptr, size, direction);
++ return busptr;
++}
++
++/*
++ * see if a mapped address was really a "safe" buffer and if so,
++ * copy the data from the safe buffer back to the unsafe buffer
++ * and free up the safe buffer.
++ * (basically return things back to the way they should be)
++ */
++void
++s3c2410_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
++ size_t size, int direction)
++{
++ char *safe, *unsafe;
++ void *buf;
++
++ /* hack; usb-ohci.c never sends hwdev==NULL, all others do */
++ if (hwdev == NULL) {
++ return;
++ }
++
++ mapped_alloc_size -= size;
++
++ if (0) printk("pci_unmap_single(hwdev=%p,ptr=%p,size=%d,dir=%x) "
++ "alloced=%ld\n",
++ hwdev, (void *)dma_addr, size, direction,
++ mapped_alloc_size);
++
++ if ((safe = find_safe_buffer((void *)dma_addr, &unsafe))) {
++ if (0) printk("copyback unsafe %p, safe %p, size %d\n",
++ unsafe, safe, size);
++
++ consistent_sync(safe, size, PCI_DMA_FROMDEVICE);
++ memcpy(unsafe, safe, size);
++ free_safe_buffer(safe);
++ } else {
++ /* assume this is normal memory */
++ buf = bus_to_virt(dma_addr);
++ consistent_sync(buf, size, PCI_DMA_FROMDEVICE);
++ }
++}
++
++EXPORT_SYMBOL(s3c2410_map_single);
++EXPORT_SYMBOL(s3c2410_unmap_single);
++
++static int __init s3c2410_init_safe_buffers(void)
++{
++ printk("Initializing S3C2410 buffer pool for DMA workaround\n");
++ init_safe_buffers(NULL);
++ return 0;
++}
++
++static void free_safe_buffers(void)
++{
++ pci_pool_destroy(small_buffer_cache);
++ pci_pool_destroy(large_buffer_cache);
++}
++
++module_init(s3c2410_init_safe_buffers);
++module_exit(free_safe_buffers);
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-integrator/pci_v3.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-integrator/pci_v3.c
+--- kernel-source-2.4.27-8/arch/arm/mach-integrator/pci_v3.c 2003-06-13 15:51:29.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-integrator/pci_v3.c 2005-02-18 17:48:34.000000000 +0000
+@@ -21,7 +21,6 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+ #include <linux/config.h>
+-#include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/pci.h>
+ #include <linux/ptrace.h>
+@@ -32,6 +31,7 @@
+ #include <linux/init.h>
+
+ #include <asm/hardware.h>
++#include <asm/io.h>
+ #include <asm/irq.h>
+ #include <asm/system.h>
+ #include <asm/mach/pci.h>
+@@ -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 kernel-source-2.4.27-8/arch/arm/mach-s3c2410/pcipool.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-s3c2410/pcipool.c
+--- kernel-source-2.4.27-8/arch/arm/mach-s3c2410/pcipool.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-s3c2410/pcipool.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,378 @@
++/*
++ NOTE:
++
++ this code was lifted straight out of drivers/pci/pci.c;
++ when compiling for the SAMSUNG UBS Controller
++ usb-ohci.c driver needs these routines even when the architecture
++ has no pci bus...
++ From Ni PalDuk Gulga
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/pci.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/bitops.h>
++
++#include <asm/page.h>
++
++#include "pcipool.h"
++
++/*
++ * Pool allocator ... wraps the pci_alloc_consistent page allocator, so
++ * small blocks are easily used by drivers for bus mastering controllers.
++ * This should probably be sharing the guts of the slab allocator.
++ */
++
++struct pci_pool { /* the pool */
++ struct list_head page_list;
++ spinlock_t lock;
++ size_t blocks_per_page;
++ size_t size;
++ int flags;
++ struct pci_dev *dev;
++ size_t allocation;
++ char name [32];
++ wait_queue_head_t waitq;
++};
++
++struct pci_page { /* cacheable header for 'allocation' bytes */
++ struct list_head page_list;
++ void *vaddr;
++ dma_addr_t dma;
++ unsigned long bitmap [0];
++};
++
++#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000)
++#define POOL_POISON_BYTE 0xa7
++
++// #define CONFIG_PCIPOOL_DEBUG
++
++
++/**
++ * pci_pool_create - Creates a pool of pci consistent memory blocks, for dma.
++ * @name: name of pool, for diagnostics
++ * @pdev: pci device that will be doing the DMA
++ * @size: size of the blocks in this pool.
++ * @align: alignment requirement for blocks; must be a power of two
++ * @allocation: returned blocks won't cross this boundary (or zero)
++ * @flags: SLAB_* flags (not all are supported).
++ *
++ * Returns a pci allocation pool with the requested characteristics, or
++ * null if one can't be created. Given one of these pools, pci_pool_alloc()
++ * may be used to allocate memory. Such memory will all have "consistent"
++ * DMA mappings, accessible by the device and its driver without using
++ * cache flushing primitives. The actual size of blocks allocated may be
++ * larger than requested because of alignment.
++ *
++ * If allocation is nonzero, objects returned from pci_pool_alloc() won't
++ * cross that size boundary. This is useful for devices which have
++ * addressing restrictions on individual DMA transfers, such as not crossing
++ * boundaries of 4KBytes.
++ */
++struct pci_pool *
++pci_pool_create (const char *name, struct pci_dev *pdev,
++ size_t size, size_t align, size_t allocation, int flags)
++{
++ struct pci_pool *retval;
++
++ if (align == 0)
++ align = 1;
++ if (size == 0)
++ return 0;
++ else if (size < align)
++ size = align;
++ else if ((size % align) != 0) {
++ size += align + 1;
++ size &= ~(align - 1);
++ }
++
++ if (allocation == 0) {
++ if (PAGE_SIZE < size)
++ allocation = size;
++ else
++ allocation = PAGE_SIZE;
++ // FIXME: round up for less fragmentation
++ } else if (allocation < size)
++ return 0;
++
++ if (!(retval = kmalloc (sizeof *retval, flags)))
++ return retval;
++
++#ifdef CONFIG_PCIPOOL_DEBUG
++ flags |= SLAB_POISON;
++#endif
++
++ strncpy (retval->name, name, sizeof retval->name);
++ retval->name [sizeof retval->name - 1] = 0;
++
++ retval->dev = pdev;
++ INIT_LIST_HEAD (&retval->page_list);
++ spin_lock_init (&retval->lock);
++ retval->size = size;
++ retval->flags = flags;
++ retval->allocation = allocation;
++ retval->blocks_per_page = allocation / size;
++ init_waitqueue_head (&retval->waitq);
++
++#ifdef CONFIG_PCIPOOL_DEBUG
++ printk (KERN_DEBUG "pcipool create %s/%s size %d, %d/page (%d alloc)\n",
++ pdev ? pdev->slot_name : NULL, retval->name, size,
++ retval->blocks_per_page, allocation);
++#endif
++
++ return retval;
++}
++
++
++static struct pci_page *
++pool_alloc_page (struct pci_pool *pool, int mem_flags)
++{
++ struct pci_page *page;
++ int mapsize;
++
++ mapsize = pool->blocks_per_page;
++ mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG;
++ mapsize *= sizeof (long);
++
++ page = (struct pci_page *) kmalloc (mapsize + sizeof *page, mem_flags);
++ if (!page)
++ return 0;
++ page->vaddr = pci_alloc_consistent (pool->dev,
++ pool->allocation, &page->dma);
++ if (page->vaddr) {
++ memset (page->bitmap, 0xff, mapsize); // bit set == free
++ if (pool->flags & SLAB_POISON)
++ memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
++ list_add (&page->page_list, &pool->page_list);
++ } else {
++ kfree (page);
++ page = 0;
++ }
++ return page;
++}
++
++
++static inline int
++is_page_busy (int blocks, unsigned long *bitmap)
++{
++ while (blocks > 0) {
++ if (*bitmap++ != ~0UL)
++ return 1;
++ blocks -= BITS_PER_LONG;
++ }
++ return 0;
++}
++
++static void
++pool_free_page (struct pci_pool *pool, struct pci_page *page)
++{
++ dma_addr_t dma = page->dma;
++
++ if (pool->flags & SLAB_POISON)
++ memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
++ pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma);
++ list_del (&page->page_list);
++ kfree (page);
++}
++
++
++/**
++ * pci_pool_destroy - destroys a pool of pci memory blocks.
++ * @pool: pci pool that will be destroyed
++ *
++ * Caller guarantees that no more memory from the pool is in use,
++ * and that nothing will try to use the pool after this call.
++ */
++void
++pci_pool_destroy (struct pci_pool *pool)
++{
++ unsigned long flags;
++
++#ifdef CONFIG_PCIPOOL_DEBUG
++ printk (KERN_DEBUG "pcipool destroy %s/%s\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name);
++#endif
++
++ spin_lock_irqsave (&pool->lock, flags);
++ while (!list_empty (&pool->page_list)) {
++ struct pci_page *page;
++ page = list_entry (pool->page_list.next,
++ struct pci_page, page_list);
++ if (is_page_busy (pool->blocks_per_page, page->bitmap)) {
++ printk (KERN_ERR "pci_pool_destroy %s/%s, %p busy\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name, page->vaddr);
++ /* leak the still-in-use consistent memory */
++ list_del (&page->page_list);
++ kfree (page);
++ } else
++ pool_free_page (pool, page);
++ }
++ spin_unlock_irqrestore (&pool->lock, flags);
++ kfree (pool);
++}
++
++
++/**
++ * pci_pool_alloc - get a block of consistent memory
++ * @pool: pci pool that will produce the block
++ * @mem_flags: SLAB_KERNEL or SLAB_ATOMIC
++ * @handle: pointer to dma address of block
++ *
++ * This returns the kernel virtual address of a currently unused block,
++ * and reports its dma address through the handle.
++ * If such a memory block can't be allocated, null is returned.
++ */
++void *
++pci_pool_alloc (struct pci_pool *pool, int mem_flags, dma_addr_t *handle)
++{
++ unsigned long flags;
++ struct list_head *entry;
++ struct pci_page *page;
++ int map, block;
++ size_t offset;
++ void *retval;
++
++restart:
++ spin_lock_irqsave (&pool->lock, flags);
++ list_for_each (entry, &pool->page_list) {
++ int i;
++ page = list_entry (entry, struct pci_page, page_list);
++ /* only cachable accesses here ... */
++ for (map = 0, i = 0;
++ i < pool->blocks_per_page;
++ i += BITS_PER_LONG, map++) {
++ if (page->bitmap [map] == 0)
++ continue;
++ block = ffz (~ page->bitmap [map]);
++ if ((i + block) < pool->blocks_per_page) {
++ clear_bit (block, &page->bitmap [map]);
++ offset = (BITS_PER_LONG * map) + block;
++ offset *= pool->size;
++ goto ready;
++ }
++ }
++ }
++ if (!(page = pool_alloc_page (pool, mem_flags))) {
++ if (mem_flags == SLAB_KERNEL) {
++ DECLARE_WAITQUEUE (wait, current);
++
++ current->state = TASK_INTERRUPTIBLE;
++ add_wait_queue (&pool->waitq, &wait);
++ spin_unlock_irqrestore (&pool->lock, flags);
++
++ schedule_timeout (POOL_TIMEOUT_JIFFIES);
++
++ current->state = TASK_RUNNING;
++ remove_wait_queue (&pool->waitq, &wait);
++ goto restart;
++ }
++ retval = 0;
++ goto done;
++ }
++
++ clear_bit (0, &page->bitmap [0]);
++ offset = 0;
++ready:
++ retval = offset + page->vaddr;
++ *handle = offset + page->dma;
++done:
++ spin_unlock_irqrestore (&pool->lock, flags);
++ return retval;
++}
++
++
++static struct pci_page *
++pool_find_page (struct pci_pool *pool, dma_addr_t dma)
++{
++ unsigned long flags;
++ struct list_head *entry;
++ struct pci_page *page;
++
++ spin_lock_irqsave (&pool->lock, flags);
++ list_for_each (entry, &pool->page_list) {
++ page = list_entry (entry, struct pci_page, page_list);
++ if (dma < page->dma)
++ continue;
++ if (dma < (page->dma + pool->allocation))
++ goto done;
++ }
++ page = 0;
++done:
++ spin_unlock_irqrestore (&pool->lock, flags);
++ return page;
++}
++
++
++/**
++ * pci_pool_free - put block back into pci pool
++ * @pool: the pci pool holding the block
++ * @vaddr: virtual address of block
++ * @dma: dma address of block
++ *
++ * Caller promises neither device nor driver will again touch this block
++ * unless it is first re-allocated.
++ */
++void
++pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma)
++{
++ struct pci_page *page;
++ unsigned long flags;
++ int map, block;
++
++ if ((page = pool_find_page (pool, dma)) == 0) {
++ printk (KERN_ERR "pci_pool_free %s/%s, %p/%x (bad dma)\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name, vaddr, dma);
++ return;
++ }
++#ifdef CONFIG_PCIPOOL_DEBUG
++ if (((dma - page->dma) + (void *)page->vaddr) != vaddr) {
++ printk (KERN_ERR "pci_pool_free %s/%s, %p (bad vaddr)/%x\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name, vaddr, dma);
++ return;
++ }
++#endif
++
++ block = dma - page->dma;
++ block /= pool->size;
++ map = block / BITS_PER_LONG;
++ block %= BITS_PER_LONG;
++
++#ifdef CONFIG_PCIPOOL_DEBUG
++ if (page->bitmap [map] & (1UL << block)) {
++ printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n",
++ pool->dev ? pool->dev->slot_name : NULL,
++ pool->name, dma);
++ return;
++ }
++#endif
++ if (pool->flags & SLAB_POISON)
++ memset (vaddr, POOL_POISON_BYTE, pool->size);
++
++ spin_lock_irqsave (&pool->lock, flags);
++ set_bit (block, &page->bitmap [map]);
++ if (waitqueue_active (&pool->waitq))
++ wake_up (&pool->waitq);
++ /*
++ * Resist a temptation to do
++ * if (!is_page_busy(bpp, page->bitmap)) pool_free_page(pool, page);
++ * it is not interrupt safe. Better have empty pages hang around.
++ */
++ spin_unlock_irqrestore (&pool->lock, flags);
++}
++
++
++EXPORT_SYMBOL (pci_pool_create);
++EXPORT_SYMBOL (pci_pool_destroy);
++EXPORT_SYMBOL (pci_pool_alloc);
++EXPORT_SYMBOL (pci_pool_free);
++
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-s3c2410/pcipool.h kernel-source-2.4.27-8-arm-1/arch/arm/mach-s3c2410/pcipool.h
+--- kernel-source-2.4.27-8/arch/arm/mach-s3c2410/pcipool.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-s3c2410/pcipool.h 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,8 @@
++
++struct pci_pool *pci_pool_create (const char *name, struct pci_dev *dev,
++ size_t size, size_t align, size_t allocation, int flags);
++void pci_pool_destroy (struct pci_pool *pool);
++
++void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle);
++void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr);
++
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-s3c2410/s3c2410-pcibuf.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-s3c2410/s3c2410-pcibuf.c
+--- kernel-source-2.4.27-8/arch/arm/mach-s3c2410/s3c2410-pcibuf.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-s3c2410/s3c2410-pcibuf.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,270 @@
++/*
++ * linux/arch/arm/mach-s3c2410.c
++ * Special pci_map/unmap_single routines for S3C2410
++ *
++ * SW.LEE <hitchcar at sec.samsung.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.
++ *
++ * 06/13/2001 - created.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++
++#include "pcipool.h"
++
++/*
++ * simple buffer allocator for copying of unsafe to safe buffers
++ * uses __alloc/__free for actual buffers
++ * keeps track of safe buffers we've allocated so we can recover the
++ * unsafe buffers.
++ */
++
++#define MAX_SAFE 32
++#define SIZE_SMALL 1024
++#define SIZE_LARGE (16*1024)
++
++static long mapped_alloc_size;
++static char *safe_buffers[MAX_SAFE][2];
++
++
++static struct pci_pool *small_buffer_cache, *large_buffer_cache;
++
++static int
++init_safe_buffers(struct pci_dev *dev)
++{
++ small_buffer_cache = pci_pool_create("pci_small_buffer",
++ dev,
++ SIZE_SMALL,
++ 0 /* byte alignment */,
++ 0 /* no page-crossing issues */,
++ GFP_KERNEL | GFP_DMA);
++
++ if (small_buffer_cache == 0)
++ return -1;
++
++ large_buffer_cache = pci_pool_create("pci_large_buffer",
++ dev,
++ SIZE_LARGE,
++ 0 /* byte alignment */,
++ 0 /* no page-crossing issues */,
++ GFP_KERNEL | GFP_DMA);
++ if (large_buffer_cache == 0)
++ return -1;
++
++ return 0;
++}
++
++/* allocate a 'safe' buffer and keep track of it */
++static char *
++alloc_safe_buffer(char *unsafe, int size, dma_addr_t *pbus)
++{
++ char *safe;
++ dma_addr_t busptr;
++ struct pci_pool *pool;
++ int i;
++
++ if (0) printk("alloc_safe_buffer(size=%d)\n", size);
++
++ if (size <= SIZE_SMALL)
++ pool = small_buffer_cache;
++ else
++ if (size < SIZE_LARGE)
++ pool = large_buffer_cache;
++ else
++ return 0;
++
++ safe = pci_pool_alloc(pool, SLAB_ATOMIC, &busptr);
++ if (safe == 0)
++ return 0;
++
++ for (i = 0; i < MAX_SAFE; i++)
++ if (safe_buffers[i][0] == 0) {
++ break;
++ }
++
++ if (i == MAX_SAFE) {
++ panic(__FILE__ ": exceeded MAX_SAFE buffers");
++ }
++
++ /* place the size index and the old buffer ptr in the first 8 bytes
++ * and return a ptr + 12 to caller
++ */
++ ((int *)safe)[0] = i;
++ ((char **)safe)[1] = (char *)pool;
++ ((char **)safe)[2] = unsafe;
++
++ busptr += sizeof(int) + sizeof(char *) + sizeof(char *);
++
++ safe_buffers[i][0] = (void *)busptr;
++ safe_buffers[i][1] = (void *)safe;
++
++ safe += sizeof(int) + sizeof(char *) + sizeof(char *);
++
++ *pbus = busptr;
++ return safe;
++}
++
++/* determine if a buffer is from our "safe" pool */
++static char *
++find_safe_buffer(char *busptr, char **unsafe)
++{
++ int i;
++ char *buf;
++
++ for (i = 0; i < MAX_SAFE; i++) {
++ if (safe_buffers[i][0] == busptr) {
++ if (0) printk("find_safe_buffer(%p) found @ %d\n", busptr, i);
++ buf = safe_buffers[i][1];
++ *unsafe = ((char **)buf)[2];
++ return buf + sizeof(int) + sizeof(char *) + sizeof(char *);
++ }
++ }
++
++ return (char *)0;
++}
++
++static void
++free_safe_buffer(char *buf)
++{
++ int index;
++ struct pci_pool *pool;
++ char *dma;
++
++ if (0) printk("free_safe_buffer(buf=%p)\n", buf);
++
++ /* retrieve the buffer size index */
++ buf -= sizeof(int) + sizeof(char*) + sizeof(char*);
++ index = ((int *)buf)[0];
++ pool = (struct pci_pool *)((char **)buf)[1];
++
++ if (0) printk("free_safe_buffer(%p) index %d\n",
++ buf, index);
++
++ if (index < 0 || index >= MAX_SAFE) {
++ printk(__FILE__ ": free_safe_buffer() corrupt buffer\n");
++ return;
++ }
++
++ dma = safe_buffers[index][0];
++ safe_buffers[index][0] = 0;
++
++ pci_pool_free(pool, buf, (u32)dma);
++}
++
++/*
++ NOTE:
++ replace pci_map/unmap_single with local routines which will
++ do buffer copies if buffer is above 1mb...
++*/
++
++/*
++ * see if a buffer address is in an 'unsafe' range. if it is
++ * allocate a 'safe' buffer and copy the unsafe buffer into it.
++ * substitute the safe buffer for the unsafe one.
++ * (basically move the buffer from an unsafe area to a safe one)
++ *
++ * we assume calls to map_single are symmetric with calls to unmap_single...
++ */
++dma_addr_t
++s3c2410_map_single(struct pci_dev *hwdev, void *virtptr,
++ size_t size, int direction)
++{
++ dma_addr_t busptr;
++
++ mapped_alloc_size += size;
++
++ if (0) printk("pci_map_single(hwdev=%p,ptr=%p,size=%d,dir=%x) "
++ "alloced=%ld\n",
++ hwdev, virtptr, size, direction, mapped_alloc_size);
++
++ busptr = virt_to_bus(virtptr);
++
++ /* we assume here that a buffer will never be >=64k */
++ if ( (((unsigned long)busptr) & 0x100000) ||
++ ((((unsigned long)busptr)+size) & 0x100000) )
++ {
++ char *safe;
++
++ safe = alloc_safe_buffer(virtptr, size, &busptr);
++ if (safe == 0) {
++ printk("unable to map unsafe buffer %p!\n", virtptr);
++ return 0;
++ }
++
++ if (0) printk("unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
++ virtptr, (void *)virt_to_bus(virtptr),
++ safe, (void *)busptr);
++
++ memcpy(safe, virtptr, size);
++ consistent_sync(safe, size, direction);
++
++ return busptr;
++ }
++
++ consistent_sync(virtptr, size, direction);
++ return busptr;
++}
++
++/*
++ * see if a mapped address was really a "safe" buffer and if so,
++ * copy the data from the safe buffer back to the unsafe buffer
++ * and free up the safe buffer.
++ * (basically return things back to the way they should be)
++ */
++void
++s3c2410_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
++ size_t size, int direction)
++{
++ char *safe, *unsafe;
++ void *buf;
++
++ /* hack; usb-ohci.c never sends hwdev==NULL, all others do */
++ if (hwdev == NULL) {
++ return;
++ }
++
++ mapped_alloc_size -= size;
++
++ if (0) printk("pci_unmap_single(hwdev=%p,ptr=%p,size=%d,dir=%x) "
++ "alloced=%ld\n",
++ hwdev, (void *)dma_addr, size, direction,
++ mapped_alloc_size);
++
++ if ((safe = find_safe_buffer((void *)dma_addr, &unsafe))) {
++ if (0) printk("copyback unsafe %p, safe %p, size %d\n",
++ unsafe, safe, size);
++
++ consistent_sync(safe, size, PCI_DMA_FROMDEVICE);
++ memcpy(unsafe, safe, size);
++ free_safe_buffer(safe);
++ } else {
++ /* assume this is normal memory */
++ buf = bus_to_virt(dma_addr);
++ consistent_sync(buf, size, PCI_DMA_FROMDEVICE);
++ }
++}
++
++EXPORT_SYMBOL(s3c2410_map_single);
++EXPORT_SYMBOL(s3c2410_unmap_single);
++
++static int __init s3c2410_init_safe_buffers(void)
++{
++ printk("Initializing S3C2410 buffer pool for DMA workaround\n");
++ init_safe_buffers(NULL);
++ return 0;
++}
++
++static void free_safe_buffers(void)
++{
++ pci_pool_destroy(small_buffer_cache);
++ pci_pool_destroy(large_buffer_cache);
++}
++
++module_init(s3c2410_init_safe_buffers);
++module_exit(free_safe_buffers);
+diff -urN kernel-source-2.4.27-8/arch/arm/mach-sa1100/pm.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/pm.c
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/pm.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/pm.c 2005-02-18 17:48:34.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 <jcorrall at mac.com>
++ * Saved the GPIO levels for resume after sleep.
+ */
+ #include <linux/config.h>
+ #include <linux/init.h>
+@@ -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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/sa1100_usb.h kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/sa1100_usb.h
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/sa1100_usb.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/sa1100_usb.h 2005-02-18 17:48:34.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 <asm/byteorder.h>
++
++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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/sa1111-ohci.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/sa1111-ohci.c
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/sa1111-ohci.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/sa1111-ohci.c 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,140 @@
++#include <linux/config.h>
++#include <linux/sched.h>
++#include <linux/pci.h>
++#include <linux/mm.h>
++
++#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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb-char.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb-char.c
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb-char.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb-char.c 2005-02-18 17:48:34.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 at 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 <linux/module.h>
++#include <linux/config.h>
++#include <linux/miscdevice.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/cache.h>
++#include <linux/poll.h>
++#include <linux/circ_buf.h>
++#include <linux/timer.h>
++
++#include <asm/io.h>
++#include <asm/semaphore.h>
++#include <asm/proc/page.h>
++#include <asm/mach-types.h>
++
++#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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb-char.h kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb-char.h
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb-char.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb-char.h 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb-eth.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb-eth.c
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb-eth.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb-eth.c 2005-02-18 17:48:34.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 at 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/timer.h>
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/random.h>
++
++#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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_ctl.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_ctl.c
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_ctl.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_ctl.c 2005-02-18 17:48:34.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/proc_fs.h>
++#include <linux/tqueue.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++#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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_ctl.h kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_ctl.h
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_ctl.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_ctl.h 2005-02-18 17:48:34.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 <asm/dma.h> /* 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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_ep0.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_ep0.c
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_ep0.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_ep0.c 2005-02-18 17:48:34.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 at 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 <linux/delay.h>
++#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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_recv.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_recv.c
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_recv.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_recv.c 2005-02-18 17:48:34.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 <linux/module.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <asm/dma.h>
++#include <asm/system.h>
++
++#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 kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_send.c kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_send.c
+--- kernel-source-2.4.27-8/arch/arm/mach-sa1100/usb_send.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mach-sa1100/usb_send.c 2005-02-18 17:48:34.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 at iXcelerator.com
++ */
++
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <linux/delay.h> // for the massive_attack hack 28Feb01ww
++#include <asm/hardware.h>
++#include <asm/dma.h>
++#include <asm/system.h>
++#include <asm/byteorder.h>
++
++#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 kernel-source-2.4.27-8/arch/arm/mm/Makefile kernel-source-2.4.27-8-arm-1/arch/arm/mm/Makefile
+--- kernel-source-2.4.27-8/arch/arm/mm/Makefile 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/Makefile 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/alignment.c kernel-source-2.4.27-8-arm-1/arch/arm/mm/alignment.c
+--- kernel-source-2.4.27-8/arch/arm/mm/alignment.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/alignment.c 2005-02-18 17:48:34.000000000 +0000
+@@ -11,7 +11,6 @@
+ #include <linux/config.h>
+ #include <linux/compiler.h>
+ #include <linux/signal.h>
+-#include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+@@ -19,7 +18,6 @@
+ #include <linux/ptrace.h>
+ #include <linux/mman.h>
+ #include <linux/mm.h>
+-#include <linux/interrupt.h>
+ #include <linux/proc_fs.h>
+ #include <linux/bitops.h>
+ #include <linux/init.h>
+@@ -30,13 +28,11 @@
+ #include <asm/pgtable.h>
+ #include <asm/unaligned.h>
+
+-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 kernel-source-2.4.27-8/arch/arm/mm/fault-armv.c kernel-source-2.4.27-8-arm-1/arch/arm/mm/fault-armv.c
+--- kernel-source-2.4.27-8/arch/arm/mm/fault-armv.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/fault-armv.c 2005-02-18 17:48:34.000000000 +0000
+@@ -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 <linux/config.h>
+-#include <linux/signal.h>
+ #include <linux/sched.h>
+ #include <linux/kernel.h>
+-#include <linux/errno.h>
+-#include <linux/string.h>
+ #include <linux/types.h>
+ #include <linux/ptrace.h>
+-#include <linux/mman.h>
+ #include <linux/mm.h>
+-#include <linux/interrupt.h>
+-#include <linux/proc_fs.h>
+ #include <linux/bitops.h>
+ #include <linux/init.h>
+
+-#include <asm/system.h>
+-#include <asm/uaccess.h>
+ #include <asm/pgalloc.h>
+ #include <asm/pgtable.h>
++#include <asm/io.h>
+
+-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 kernel-source-2.4.27-8/arch/arm/mm/fault-common.c kernel-source-2.4.27-8-arm-1/arch/arm/mm/fault-common.c
+--- kernel-source-2.4.27-8/arch/arm/mm/fault-common.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/fault-common.c 2005-02-18 17:48:34.000000000 +0000
+@@ -11,21 +11,17 @@
+ #include <linux/config.h>
+ #include <linux/signal.h>
+ #include <linux/sched.h>
+-#include <linux/kernel.h>
+-#include <linux/errno.h>
+ #include <linux/string.h>
+-#include <linux/types.h>
+ #include <linux/ptrace.h>
+-#include <linux/mman.h>
+ #include <linux/mm.h>
+ #include <linux/interrupt.h>
+-#include <linux/proc_fs.h>
+ #include <linux/init.h>
+
+ #include <asm/system.h>
+-#include <asm/uaccess.h>
+ #include <asm/pgtable.h>
+-#include <asm/unaligned.h>
++#include <asm/uaccess.h>
++
++#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 kernel-source-2.4.27-8/arch/arm/mm/fault.h kernel-source-2.4.27-8-arm-1/arch/arm/mm/fault.h
+--- kernel-source-2.4.27-8/arch/arm/mm/fault.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/fault.h 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/init.c kernel-source-2.4.27-8-arm-1/arch/arm/mm/init.c
+--- kernel-source-2.4.27-8/arch/arm/mm/init.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/init.c 2005-02-18 17:48:34.000000000 +0000
+@@ -9,7 +9,6 @@
+ */
+ #include <linux/config.h>
+ #include <linux/signal.h>
+-#include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+@@ -18,7 +17,6 @@
+ #include <linux/mman.h>
+ #include <linux/mm.h>
+ #include <linux/swap.h>
+-#include <linux/swapctl.h>
+ #include <linux/smp.h>
+ #include <linux/init.h>
+ #include <linux/bootmem.h>
+diff -urN kernel-source-2.4.27-8/arch/arm/mm/ioremap.c kernel-source-2.4.27-8-arm-1/arch/arm/mm/ioremap.c
+--- kernel-source-2.4.27-8/arch/arm/mm/ioremap.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/ioremap.c 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/mm-armv.c kernel-source-2.4.27-8-arm-1/arch/arm/mm/mm-armv.c
+--- kernel-source-2.4.27-8/arch/arm/mm/mm-armv.c 2003-11-28 18:26:19.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/mm-armv.c 2005-02-18 17:48:34.000000000 +0000
+@@ -9,7 +9,6 @@
+ *
+ * Page table sludge for ARM v3 and v4 processor architectures.
+ */
+-#include <linux/sched.h>
+ #include <linux/mm.h>
+ #include <linux/init.h>
+ #include <linux/bootmem.h>
+@@ -390,6 +389,9 @@
+ init_maps->bufferable = 0;
+
+ create_mapping(init_maps);
++
++ flush_cache_all();
++ flush_tlb_all();
+ }
+
+ /*
+diff -urN kernel-source-2.4.27-8/arch/arm/mm/proc-arm1020.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm1020.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm1020.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm1020.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/proc-arm1020E.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm1020E.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm1020E.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm1020E.S 2005-02-18 17:48:34.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 <linux/linkage.h>
++#include <linux/config.h>
++#include <asm/assembler.h>
++#include <asm/constants.h>
++#include <asm/procinfo.h>
++#include <asm/hardware.h>
++
++/*
++ * 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 kernel-source-2.4.27-8/arch/arm/mm/proc-arm1022.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm1022.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm1022.S 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm1022.S 2005-02-18 17:48:34.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 <linux/linkage.h>
++#include <linux/config.h>
++#include <asm/assembler.h>
++#include <asm/constants.h>
++#include <asm/procinfo.h>
++#include <asm/hardware.h>
++
++/*
++ * 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 kernel-source-2.4.27-8/arch/arm/mm/proc-arm1026.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm1026.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm1026.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm1026.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/proc-arm6,7.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm6,7.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm6,7.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm6,7.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/proc-arm720.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm720.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm720.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm720.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/proc-arm920.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm920.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm920.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm920.S 2005-02-18 17:48:34.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,20 @@
+ */
+ .align 5
+ ENTRY(cpu_arm920_dcache_invalidate_range)
+-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH
++ mov r2, #0
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+ tst r0, #DCACHELINESIZE - 1
+ mcrne p15, 0, r0, c7, c10, 1 @ clean D entry
++ mcrne p15, 0, r2, c7, c10, 4 @ bjd - drain WB
+ tst r1, #DCACHELINESIZE - 1
+ mcrne p15, 0, r1, c7, c10, 1 @ clean D entry
++ mcrne p15, 0, r2, c7, c10, 4 @ bjd - drain WB
+ #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 +284,17 @@
+ */
+ .align 5
+ ENTRY(cpu_arm920_dcache_clean_range)
+-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH
++ mov r2, #0
++#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 +314,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
+@@ -321,6 +323,7 @@
+ subs r1, r1, #2 * DCACHELINESIZE
+ bne 1b
+ #endif
++ mcr p15, 0, r1, c7, c10, 4 @ bjd - drain WB
+ mov pc, lr
+
+ /*
+@@ -333,7 +336,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 +368,14 @@
+ 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
++ bpl 1b @@ bjd
+
+ mov r0, #0
+ mcr p15, 0, r0, c7, c10, 4 @ drain WB
+@@ -408,6 +409,8 @@
+ */
+ .align 5
+ ENTRY(cpu_arm920_tlb_invalidate_range)
++ bic r0, r0, # (PAGESIZE - 1) & 0x00ff @@ bjd
++ bic r1, r1, # (PAGESIZE - 1) & 0xff00 @@ bjd
+ sub r3, r1, r0
+ cmp r3, #256 * PAGESIZE @ arbitary, should be tuned
+ bhi cpu_arm920_tlb_invalidate_all
+@@ -415,16 +418,11 @@
+ mov r3, #0
+ mcr p15, 0, r3, c7, c10, 4 @ drain WB
+
+- 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,8 +455,8 @@
+ 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
++@ mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
++ mcr p15, 0, ip, c7, c7, 0 @ bjd invalidate I+ D cache
+ #else
+ @ && 'Clean & Invalidate whole DCache'
+ @ && Re-written to use Index Ops.
+@@ -514,7 +512,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?
+@@ -644,11 +642,17 @@
+
+ .section ".proc.info", #alloc, #execinstr
+
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++# define MMU_FLAGS 0x00000c1a
++#else
++# define MMU_FLAGS 0x00000c1e
++#endif
++
+ .type __arm920_proc_info,#object
+ __arm920_proc_info:
+ .long 0x41009200
+ .long 0xff00fff0
+- .long 0x00000c1e @ mmuflags
++ .long MMU_FLAGS @ mmuflags
+ b __arm920_setup
+ .long cpu_arch_name
+ .long cpu_elf_name
+diff -urN kernel-source-2.4.27-8/arch/arm/mm/proc-arm922.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm922.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm922.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm922.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/proc-arm925.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm925.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm925.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm925.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/proc-arm926.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm926.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-arm926.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-arm926.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/mm/proc-sa110.S kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-sa110.S
+--- kernel-source-2.4.27-8/arch/arm/mm/proc-sa110.S 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/mm/proc-sa110.S 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/arm/tools/constants.h kernel-source-2.4.27-8-arm-1/arch/arm/tools/constants.h
+--- kernel-source-2.4.27-8/arch/arm/tools/constants.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/tools/constants.h 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,26 @@
++/*
++ * This file is automatically generated from arch/arm/tools/getconstants.c.
++ * Do not edit! Only include this file in assembly (.S) files!
++ */
++
++#define TSK_SIGPENDING 8
++#define TSK_ADDR_LIMIT 12
++#define TSK_NEED_RESCHED 20
++#define TSK_PTRACE 24
++#define TSK_USED_MATH 576
++#define TSS_SAVE 796
++#define TSS_FPESAVE 636
++#define TSS_DOMAIN 800
++#define HPTE_TYPE_SMALL 2
++#define HPTE_AP_READ 2720
++#define HPTE_AP_WRITE 1360
++#define LPTE_PRESENT 1
++#define LPTE_YOUNG 2
++#define LPTE_BUFFERABLE 4
++#define LPTE_CACHEABLE 8
++#define LPTE_USER 16
++#define LPTE_WRITE 32
++#define LPTE_EXEC 64
++#define LPTE_DIRTY 128
++#define PAGE_SZ 4096
++#define SYS_ERROR0 10420224
+diff -urN kernel-source-2.4.27-8/arch/arm/tools/mach-types kernel-source-2.4.27-8-arm-1/arch/arm/tools/mach-types
+--- kernel-source-2.4.27-8/arch/arm/tools/mach-types 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/tools/mach-types 2005-02-18 17:48:34.000000000 +0000
+@@ -6,7 +6,7 @@
+ # To add an entry into this database, please see Documentation/arm/README,
+ # or contact rmk at 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 kernel-source-2.4.27-8/arch/arm/vmlinux.lds kernel-source-2.4.27-8-arm-1/arch/arm/vmlinux.lds
+--- kernel-source-2.4.27-8/arch/arm/vmlinux.lds 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/arm/vmlinux.lds 2005-02-18 17:48:34.000000000 +0000
+@@ -0,0 +1,108 @@
++/* ld script to make ARM Linux kernel
++ * taken from the i386 version by Russell King
++ * Written by Martin Mares <mj at atrey.karlin.mff.cuni.cz>
++ */
++OUTPUT_ARCH(arm)
++ENTRY(stext)
++SECTIONS
++{
++ . = 0xC0008000;
++ .init : { /* Init code and data */
++ _stext = .;
++ __init_begin = .;
++ *(.text.init)
++ __proc_info_begin = .;
++ *(.proc.info)
++ __proc_info_end = .;
++ __arch_info_begin = .;
++ *(.arch.info)
++ __arch_info_end = .;
++ __tagtable_begin = .;
++ *(.taglist)
++ __tagtable_end = .;
++ *(.data.init)
++ . = ALIGN(16);
++ __setup_start = .;
++ *(.setup.init)
++ __setup_end = .;
++ __initcall_start = .;
++ *(.initcall.init)
++ __initcall_end = .;
++ . = ALIGN(4096);
++ __init_end = .;
++ }
++
++ /DISCARD/ : { /* Exit code and data */
++ *(.text.exit)
++ *(.data.exit)
++ *(.exitcall.exit)
++ }
++
++ .text : { /* Real text segment */
++ _text = .; /* Text and read-only data */
++ *(.text)
++ *(.fixup)
++ *(.gnu.warning)
++ *(.rodata)
++ *(.rodata.*)
++ *(.glue_7)
++ *(.glue_7t)
++ *(.got) /* Global offset table */
++
++ _etext = .; /* End of text section */
++ }
++
++ .kstrtab : { *(.kstrtab) }
++
++ . = ALIGN(16);
++ __ex_table : { /* Exception table */
++ __start___ex_table = .;
++ *(__ex_table)
++ __stop___ex_table = .;
++ }
++
++ __ksymtab : { /* Kernel symbol table */
++ __start___ksymtab = .;
++ *(__ksymtab)
++ __stop___ksymtab = .;
++ }
++
++ . = ALIGN(8192);
++
++ .data : {
++ /*
++ * first, the init task union, aligned
++ * to an 8192 byte boundary.
++ */
++ *(.init.task)
++
++ /*
++ * then the cacheline aligned data
++ */
++ . = ALIGN(32);
++ *(.data.cacheline_aligned)
++
++ /*
++ * and the usual data section
++ */
++ *(.data)
++ CONSTRUCTORS
++
++ _edata = .;
++ }
++
++ .bss : {
++ __bss_start = .; /* BSS */
++ *(.bss)
++ *(COMMON)
++ _end = . ;
++ }
++ /* Stabs debugging sections. */
++ .stab 0 : { *(.stab) }
++ .stabstr 0 : { *(.stabstr) }
++ .stab.excl 0 : { *(.stab.excl) }
++ .stab.exclstr 0 : { *(.stab.exclstr) }
++ .stab.index 0 : { *(.stab.index) }
++ .stab.indexstr 0 : { *(.stab.indexstr) }
++ .comment 0 : { *(.comment) }
++}
+diff -urN kernel-source-2.4.27-8/arch/i386/config.in kernel-source-2.4.27-8-arm-1/arch/i386/config.in
+--- kernel-source-2.4.27-8/arch/i386/config.in 2005-01-19 09:57:37.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/i386/config.in 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/i386/kernel/Makefile kernel-source-2.4.27-8-arm-1/arch/i386/kernel/Makefile
+--- kernel-source-2.4.27-8/arch/i386/kernel/Makefile 2005-01-19 09:57:37.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/i386/kernel/Makefile 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/i386/kernel/apm.c kernel-source-2.4.27-8-arm-1/arch/i386/kernel/apm.c
+--- kernel-source-2.4.27-8/arch/i386/kernel/apm.c 2003-08-25 12:44:39.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/i386/kernel/apm.c 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/i386/lib/Makefile kernel-source-2.4.27-8-arm-1/arch/i386/lib/Makefile
+--- kernel-source-2.4.27-8/arch/i386/lib/Makefile 2001-09-10 15:31:30.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/i386/lib/Makefile 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/i386/math-emu/Makefile kernel-source-2.4.27-8-arm-1/arch/i386/math-emu/Makefile
+--- kernel-source-2.4.27-8/arch/i386/math-emu/Makefile 2000-12-29 22:07:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/i386/math-emu/Makefile 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/ia64/config.in kernel-source-2.4.27-8-arm-1/arch/ia64/config.in
+--- kernel-source-2.4.27-8/arch/ia64/config.in 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/ia64/config.in 2005-02-18 17:48:34.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 kernel-source-2.4.27-8/arch/m68k/config.in kernel-source-2.4.27-8-arm-1/arch/m68k/config.in
+--- kernel-source-2.4.27-8/arch/m68k/config.in 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/m68k/config.in 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/arch/mips/config.in kernel-source-2.4.27-8-arm-1/arch/mips/config.in
+--- kernel-source-2.4.27-8/arch/mips/config.in 2002-11-28 23:53:09.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/mips/config.in 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/arch/parisc/config.in kernel-source-2.4.27-8-arm-1/arch/parisc/config.in
+--- kernel-source-2.4.27-8/arch/parisc/config.in 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/parisc/config.in 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/arch/ppc/config.in kernel-source-2.4.27-8-arm-1/arch/ppc/config.in
+--- kernel-source-2.4.27-8/arch/ppc/config.in 2004-08-08 00:26:04.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/arch/ppc/config.in 2005-02-18 17:48:35.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/arch/sh/config.in kernel-source-2.4.27-8-arm-1/arch/sh/config.in
+--- kernel-source-2.4.27-8/arch/sh/config.in 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/sh/config.in 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/arch/sparc/config.in kernel-source-2.4.27-8-arm-1/arch/sparc/config.in
+--- kernel-source-2.4.27-8/arch/sparc/config.in 2004-02-18 13:36:30.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/sparc/config.in 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/arch/sparc64/config.in kernel-source-2.4.27-8-arm-1/arch/sparc64/config.in
+--- kernel-source-2.4.27-8/arch/sparc64/config.in 2005-01-19 09:57:38.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/arch/sparc64/config.in 2005-02-18 17:48:35.000000000 +0000
+@@ -43,6 +43,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 kernel-source-2.4.27-8/drivers/Makefile kernel-source-2.4.27-8-arm-1/drivers/Makefile
+--- kernel-source-2.4.27-8/drivers/Makefile 2003-11-28 18:26:19.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/acorn/char/i2c.c kernel-source-2.4.27-8-arm-1/drivers/acorn/char/i2c.c
+--- kernel-source-2.4.27-8/drivers/acorn/char/i2c.c 2004-01-05 13:53:56.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/char/i2c.c 2005-02-18 17:48:35.000000000 +0000
+@@ -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",
++ 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 kernel-source-2.4.27-8/drivers/acorn/net/Makefile kernel-source-2.4.27-8-arm-1/drivers/acorn/net/Makefile
+--- kernel-source-2.4.27-8/drivers/acorn/net/Makefile 2001-02-09 00:32:44.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/net/Makefile 2005-02-18 17:48:35.000000000 +0000
+@@ -13,5 +13,6 @@
+ obj-$(CONFIG_ARM_ETHERH) += etherh.o
+ obj-$(CONFIG_ARM_ETHER3) += ether3.o
+ obj-$(CONFIG_ARM_ETHER1) += ether1.o
++obj-$(CONFIG_ARM_ETHERS) += ethers.o
+
+ include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27-8/drivers/acorn/net/cirrus.h kernel-source-2.4.27-8-arm-1/drivers/acorn/net/cirrus.h
+--- kernel-source-2.4.27-8/drivers/acorn/net/cirrus.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/net/cirrus.h 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,235 @@
++#ifndef CIRRUS_H
++#define CIRRUS_H
++
++/*
++ * linux/drivers/net/cirrus.h
++ *
++ * Author: Abraham van der Merwe <abraham at 2d3d.co.za>
++ *
++ * A Cirrus Logic CS8900A driver for Linux
++ * based on the cs89x0 driver written by Russell Nelson,
++ * Donald Becker, and others.
++ *
++ * 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.
++ */
++
++/*
++ * Ports
++ */
++
++#define PP_Address (0x0a >> 1) /* PacketPage Pointer Port (Section 4.10.10) */
++#define PP_Data (0x0c >> 1) /* PacketPage Data Port (Section 4.10.10) */
++
++/*
++ * Registers
++ */
++
++#define PP_ProductID 0x0000 /* Section 4.3.1 Product Identification Code */
++#define PP_MemBase 0x002c /* Section 4.9.2 Memory Base Address Register */
++#define PP_IntNum 0x0022 /* Section 3.2.3 Interrupt Number */
++#define PP_EEPROMCommand 0x0040 /* Section 4.3.11 EEPROM Command */
++#define PP_EEPROMData 0x0042 /* Section 4.3.12 EEPROM Data */
++#define PP_RxCFG 0x0102 /* Section 4.4.6 Receiver Configuration */
++#define PP_RxCTL 0x0104 /* Section 4.4.8 Receiver Control */
++#define PP_TxCFG 0x0106 /* Section 4.4.9 Transmit Configuration */
++#define PP_BufCFG 0x010a /* Section 4.4.12 Buffer Configuration */
++#define PP_LineCTL 0x0112 /* Section 4.4.16 Line Control */
++#define PP_SelfCTL 0x0114 /* Section 4.4.18 Self Control */
++#define PP_BusCTL 0x0116 /* Section 4.4.20 Bus Control */
++#define PP_TestCTL 0x0118 /* Section 4.4.22 Test Control */
++#define PP_ISQ 0x0120 /* Section 4.4.5 Interrupt Status Queue */
++#define PP_TxEvent 0x0128 /* Section 4.4.10 Transmitter Event */
++#define PP_BufEvent 0x012c /* Section 4.4.13 Buffer Event */
++#define PP_RxMISS 0x0130 /* Section 4.4.14 Receiver Miss Counter */
++#define PP_TxCOL 0x0132 /* Section 4.4.15 Transmit Collision Counter */
++#define PP_SelfST 0x0136 /* Section 4.4.19 Self Status */
++#define PP_BusST 0x0138 /* Section 4.4.21 Bus Status */
++#define PP_TxCMD 0x0144 /* Section 4.4.11 Transmit Command */
++#define PP_TxLength 0x0146 /* Section 4.5.2 Transmit Length */
++#define PP_IA 0x0158 /* Section 4.6.2 Individual Address (IEEE Address) */
++#define PP_RxStatus 0x0400 /* Section 4.7.1 Receive Status */
++#define PP_RxLength 0x0402 /* Section 4.7.1 Receive Length (in bytes) */
++#define PP_RxFrame 0x0404 /* Section 4.7.2 Receive Frame Location */
++#define PP_TxFrame 0x0a00 /* Section 4.7.2 Transmit Frame Location */
++
++/*
++ * Values
++ */
++
++/* PP_IntNum */
++#define INTRQ0 0x0000
++#define INTRQ1 0x0001
++#define INTRQ2 0x0002
++#define INTRQ3 0x0003
++
++/* PP_ProductID */
++#define EISA_REG_CODE 0x630e
++#define REVISION(x) (((x) & 0x1f00) >> 8)
++#define VERSION(x) ((x) & ~0x1f00)
++
++#define CS8900A 0x0000
++#define REV_B 7
++#define REV_C 8
++#define REV_D 9
++
++/* PP_RxCFG */
++#define Skip_1 0x0040
++#define StreamE 0x0080
++#define RxOKiE 0x0100
++#define RxDMAonly 0x0200
++#define AutoRxDMAE 0x0400
++#define BufferCRC 0x0800
++#define CRCerroriE 0x1000
++#define RuntiE 0x2000
++#define ExtradataiE 0x4000
++
++/* PP_RxCTL */
++#define IAHashA 0x0040
++#define PromiscuousA 0x0080
++#define RxOKA 0x0100
++#define MulticastA 0x0200
++#define IndividualA 0x0400
++#define BroadcastA 0x0800
++#define CRCerrorA 0x1000
++#define RuntA 0x2000
++#define ExtradataA 0x4000
++
++/* PP_TxCFG */
++#define Loss_of_CRSiE 0x0040
++#define SQErroriE 0x0080
++#define TxOKiE 0x0100
++#define Out_of_windowiE 0x0200
++#define JabberiE 0x0400
++#define AnycolliE 0x0800
++#define T16colliE 0x8000
++
++/* PP_BufCFG */
++#define SWint_X 0x0040
++#define RxDMAiE 0x0080
++#define Rdy4TxiE 0x0100
++#define TxUnderruniE 0x0200
++#define RxMissiE 0x0400
++#define Rx128iE 0x0800
++#define TxColOvfiE 0x1000
++#define MissOvfloiE 0x2000
++#define RxDestiE 0x8000
++
++/* PP_LineCTL */
++#define SerRxON 0x0040
++#define SerTxON 0x0080
++#define AUIonly 0x0100
++#define AutoAUI_10BT 0x0200
++#define ModBackoffE 0x0800
++#define PolarityDis 0x1000
++#define L2_partDefDis 0x2000
++#define LoRxSquelch 0x4000
++
++/* PP_SelfCTL */
++#define RESET 0x0040
++#define SWSuspend 0x0100
++#define HWSleepE 0x0200
++#define HWStandbyE 0x0400
++#define HC0E 0x1000
++#define HC1E 0x2000
++#define HCB0 0x4000
++#define HCB1 0x8000
++
++/* PP_BusCTL */
++#define ResetRxDMA 0x0040
++#define DMAextend 0x0100
++#define UseSA 0x0200
++#define MemoryE 0x0400
++#define DMABurst 0x0800
++#define IOCHRDYE 0x1000
++#define RxDMAsize 0x2000
++#define EnableRQ 0x8000
++
++/* PP_TestCTL */
++#define DisableLT 0x0080
++#define ENDECloop 0x0200
++#define AUIloop 0x0400
++#define DisableBackoff 0x0800
++#define FDX 0x4000
++
++/* PP_ISQ */
++#define RegNum(x) ((x) & 0x3f)
++#define RegContent(x) ((x) & ~0x3d)
++
++#define RxEvent 0x0004
++#define TxEvent 0x0008
++#define BufEvent 0x000c
++#define RxMISS 0x0010
++#define TxCOL 0x0012
++
++/* PP_RxStatus */
++#define IAHash 0x0040
++#define Dribblebits 0x0080
++#define RxOK 0x0100
++#define Hashed 0x0200
++#define IndividualAdr 0x0400
++#define Broadcast 0x0800
++#define CRCerror 0x1000
++#define Runt 0x2000
++#define Extradata 0x4000
++
++#define HashTableIndex(x) ((x) >> 0xa)
++
++/* PP_TxCMD */
++#define After5 0
++#define After381 1
++#define After1021 2
++#define AfterAll 3
++#define TxStart(x) ((x) << 6)
++
++#define Force 0x0100
++#define Onecoll 0x0200
++#define InhibitCRC 0x1000
++#define TxPadDis 0x2000
++
++/* PP_BusST */
++#define TxBidErr 0x0080
++#define Rdy4TxNOW 0x0100
++
++/* PP_TxEvent */
++#define Loss_of_CRS 0x0040
++#define SQEerror 0x0080
++#define TxOK 0x0100
++#define Out_of_window 0x0200
++#define Jabber 0x0400
++#define T16coll 0x8000
++
++#define TX_collisions(x) (((x) >> 0xb) & ~0x8000)
++
++/* PP_BufEvent */
++#define SWint 0x0040
++#define RxDMAFrame 0x0080
++#define Rdy4Tx 0x0100
++#define TxUnderrun 0x0200
++#define RxMiss 0x0400
++#define Rx128 0x0800
++#define RxDest 0x8000
++
++/* PP_RxMISS */
++#define MissCount(x) ((x) >> 6)
++
++/* PP_TxCOL */
++#define ColCount(x) ((x) >> 6)
++
++/* PP_SelfST */
++#define T3VActive 0x0040
++#define INITD 0x0080
++#define SIBUSY 0x0100
++#define EEPROMpresent 0x0200
++#define EEPROMOK 0x0400
++#define ELpresent 0x0800
++#define EEsize 0x1000
++
++/* PP_EEPROMCommand */
++#define EEWriteRegister 0x0100
++#define EEReadRegister 0x0200
++#define EEEraseRegister 0x0300
++#define ELSEL 0x0400
++
++#endif /* #ifndef CIRRUS_H */
+diff -urN kernel-source-2.4.27-8/drivers/acorn/net/ether1.c kernel-source-2.4.27-8-arm-1/drivers/acorn/net/ether1.c
+--- kernel-source-2.4.27-8/drivers/acorn/net/ether1.c 2003-08-25 12:44:40.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/net/ether1.c 2005-02-18 17:48:35.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/acorn/net/ether3.c kernel-source-2.4.27-8-arm-1/drivers/acorn/net/ether3.c
+--- kernel-source-2.4.27-8/drivers/acorn/net/ether3.c 2003-08-25 12:44:40.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/net/ether3.c 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/acorn/net/etherh.c kernel-source-2.4.27-8-arm-1/drivers/acorn/net/etherh.c
+--- kernel-source-2.4.27-8/drivers/acorn/net/etherh.c 2003-08-25 12:44:40.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/net/etherh.c 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/acorn/net/ethers.c kernel-source-2.4.27-8-arm-1/drivers/acorn/net/ethers.c
+--- kernel-source-2.4.27-8/drivers/acorn/net/ethers.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/net/ethers.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,704 @@
++
++/*
++ * linux/drivers/net/cirrus.c
++ *
++ * Author: Abraham van der Merwe <abraham at 2d3d.co.za>
++ *
++ * RiscStation version by Ben Dooks <ben at simtec.co.uk>
++ *
++ * A Cirrus Logic CS8900A driver for Linux
++ * based on the cs89x0 driver written by Russell Nelson,
++ * Donald Becker, and others.
++ *
++ * 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.
++ */
++
++/*
++ * At the moment the driver does not support memory mode operation.
++ * It is trivial to implement this, but not worth the effort.
++ */
++
++/*
++ * TODO:
++ *
++ * 1. If !ready in send_start(), queue buffer and send it in interrupt handler
++ * when we receive a BufEvent with Rdy4Tx, send it again. dangerous!
++ * 2. how do we prevent interrupt handler destroying integrity of get_stats()?
++ * 3. Change reset code to check status.
++ * 4. Implement set_mac_address and remove fake mac address
++ * 5. Link status detection stuff
++ * 6. Write utility to write EEPROM, do self testing, etc.
++ * 7. Implement DMA routines (I need a board w/ DMA support for that)
++ * 8. Power management
++ * 9. Add support for multiple ethernet chips
++ * 10. Add support for other cs89xx chips (need hardware for that)
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++
++#include "cirrus.h"
++
++#if 0
++#define DEBUG
++#endif
++
++/* #define FULL_DUPLEX */
++
++#ifdef CONFIG_SA1100_FRODO
++# define CIRRUS_DEFAULT_IO FRODO_ETH_IO + 0x300
++# define CIRRUS_DEFAULT_IRQ FRODO_ETH_IRQ
++#elif CONFIG_SA1100_CERF
++# define CIRRUS_DEFAULT_IO CERF_ETH_IO + 0x300
++# define CIRRUS_DEFAULT_IRQ CERF_ETH_IRQ
++#else
++# define CIRRUS_DEFAULT_IO 0
++# define CIRRUS_DEFAULT_IRQ 0
++#endif /* #ifdef CONFIG_SA1100_CERF */
++
++typedef struct {
++ struct net_device_stats stats;
++ u16 txlen;
++} cirrus_t;
++
++typedef struct {
++ u16 io_base; /* I/O Base Address */
++ u16 irq; /* Interrupt Number */
++ u16 dma; /* DMA Channel Numbers */
++ u32 mem_base; /* Memory Base Address */
++ u32 rom_base; /* Boot PROM Base Address */
++ u32 rom_mask; /* Boot PROM Address Mask */
++ u8 mac[6]; /* Individual Address */
++} cirrus_eeprom_t;
++
++/*
++ * I/O routines
++ */
++
++static u16 cirrus_read (struct net_device *dev,u16 reg)
++{
++ outw (reg,dev->base_addr + PP_Address);
++ return (inw (dev->base_addr + PP_Data));
++}
++
++static void cirrus_write (struct net_device *dev,u16 reg,u16 value)
++{
++ outw (reg,dev->base_addr + PP_Address);
++ outw (value,dev->base_addr + PP_Data);
++}
++
++static inline void cirrus_set (struct net_device *dev,u16 reg,u16 value)
++{
++ cirrus_write (dev,reg,cirrus_read (dev,reg) | value);
++}
++
++static inline void cirrus_clear (struct net_device *dev,u16 reg,u16 value)
++{
++ cirrus_write (dev,reg,cirrus_read (dev,reg) & ~value);
++}
++
++static inline void cirrus_frame_read (struct net_device *dev,struct sk_buff *skb,u16 length)
++{
++#ifdef DEBUG
++ printk("cirrus_frame_read: length=%d\n", length);
++#endif
++ insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2);
++}
++
++static inline void cirrus_frame_write (struct net_device *dev,struct sk_buff *skb)
++{
++#ifdef DEBUG
++ printk("cirrus_frame_write: skb->len=%d\n", skb->len);
++#endif
++ outsw (dev->base_addr,skb->data,(skb->len + 1) / 2);
++}
++
++/*
++ * Debugging functions
++ */
++
++#ifdef DEBUG
++static inline int printable (int c)
++{
++ return ((c >= 32 && c <= 126) ||
++ (c >= 174 && c <= 223) ||
++ (c >= 242 && c <= 243) ||
++ (c >= 252 && c <= 253));
++}
++
++static void dump16 (struct net_device *dev,const u8 *s,size_t len)
++{
++ int i;
++ char str[128];
++
++ if (!len) return;
++
++ *str = '\0';
++
++ for (i = 0; i < len; i++) {
++ if (i && !(i % 4)) strcat (str," ");
++ sprintf (str,"%s%.2x ",str,s[i]);
++ }
++
++ for ( ; i < 16; i++) {
++ if (i && !(i % 4)) strcat (str," ");
++ strcat (str," ");
++ }
++
++ strcat (str," ");
++ for (i = 0; i < len; i++) sprintf (str,"%s%c",str,printable (s[i]) ? s[i] : '.');
++
++ printk (KERN_DEBUG "%s: %s\n",dev->name,str);
++}
++
++static void hexdump (struct net_device *dev,const void *ptr,size_t size)
++{
++ const u8 *s = (u8 *) ptr;
++ int i;
++ for (i = 0; i < size / 16; i++, s += 16) dump16 (dev,s,16);
++ dump16 (dev,s,size % 16);
++}
++
++static void dump_packet (struct net_device *dev,struct sk_buff *skb,const char *type)
++{
++ printk (KERN_INFO "%s: %s %d byte frame %.2x:%.2x:%.2x:%.2x:%.2x:%.2x to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x type %.4x\n",
++ dev->name,
++ type,
++ skb->len,
++ skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5],
++ skb->data[6],skb->data[7],skb->data[8],skb->data[9],skb->data[10],skb->data[11],
++ (skb->data[12] << 8) | skb->data[13]);
++ if (skb->len < 0x100) hexdump (dev,skb->data,skb->len);
++}
++#endif /* #ifdef DEBUG */
++
++/*
++ * Driver functions
++ */
++
++static void cirrus_receive (struct net_device *dev)
++{
++ cirrus_t *priv = (cirrus_t *) dev->priv;
++ struct sk_buff *skb;
++ u16 status,length;
++
++ status = cirrus_read (dev,PP_RxStatus);
++ length = cirrus_read (dev,PP_RxLength);
++
++ if (!(status & RxOK)) {
++ priv->stats.rx_errors++;
++ if ((status & (Runt | Extradata))) priv->stats.rx_length_errors++;
++ if ((status & CRCerror)) priv->stats.rx_crc_errors++;
++ return;
++ }
++
++ if ((skb = dev_alloc_skb (length + 4)) == NULL) {
++ priv->stats.rx_dropped++;
++ return;
++ }
++
++ skb->dev = dev;
++ skb_reserve (skb,2);
++
++ cirrus_frame_read (dev,skb,length);
++
++#ifdef DEBUG
++ dump_packet (dev,skb,"recv");
++#endif /* #ifdef DEBUG */
++
++ skb->protocol = eth_type_trans (skb,dev);
++
++ netif_rx (skb);
++ dev->last_rx = jiffies;
++
++ priv->stats.rx_packets++;
++ priv->stats.rx_bytes += length;
++}
++
++static int cirrus_send_start (struct sk_buff *skb,struct net_device *dev)
++{
++ cirrus_t *priv = (cirrus_t *) dev->priv;
++ u16 status;
++
++ netif_stop_queue (dev);
++
++ cirrus_write (dev,PP_TxCMD,TxStart (After5));
++ cirrus_write (dev,PP_TxLength,skb->len);
++
++ status = cirrus_read (dev,PP_BusST);
++
++ if ((status & TxBidErr)) {
++ printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len);
++ priv->stats.tx_errors++;
++ priv->stats.tx_aborted_errors++;
++ priv->txlen = 0;
++
++ dev_kfree_skb(skb);
++ return (0);
++ }
++
++ if (!(status & Rdy4TxNOW)) {
++ 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);
++ }
++
++ cirrus_frame_write (dev,skb);
++
++#ifdef DEBUG
++ dump_packet (dev,skb,"send");
++#endif /* #ifdef DEBUG */
++
++ dev->trans_start = jiffies;
++
++ dev_kfree_skb (skb);
++
++ priv->txlen = skb->len;
++
++ return (0);
++}
++
++static void cirrus_interrupt (int irq,void *id,struct pt_regs *regs)
++{
++ struct net_device *dev = (struct net_device *) id;
++ cirrus_t *priv;
++ u16 status;
++
++ if (dev->priv == NULL) {
++ printk (KERN_WARNING "%s: irq %d for unknown device.\n",dev->name,irq);
++ return;
++ }
++
++ priv = (cirrus_t *) dev->priv;
++
++ while ((status = cirrus_read (dev,PP_ISQ))) {
++ switch (RegNum (status)) {
++ case RxEvent:
++ cirrus_receive (dev);
++ break;
++
++ case TxEvent:
++ priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL));
++ if (!(RegContent (status) & TxOK)) {
++ priv->stats.tx_errors++;
++ if ((RegContent (status) & Out_of_window)) priv->stats.tx_window_errors++;
++ if ((RegContent (status) & Jabber)) priv->stats.tx_aborted_errors++;
++ break;
++ } else if (priv->txlen) {
++ priv->stats.tx_packets++;
++ priv->stats.tx_bytes += priv->txlen;
++ }
++ priv->txlen = 0;
++ netif_wake_queue (dev);
++ break;
++
++ case BufEvent:
++ if ((RegContent (status) & RxMiss)) {
++ u16 missed = MissCount (cirrus_read (dev,PP_RxMISS));
++ priv->stats.rx_errors += missed;
++ priv->stats.rx_missed_errors += missed;
++ }
++ if ((RegContent (status) & TxUnderrun)) {
++ priv->stats.tx_errors++;
++ priv->stats.tx_fifo_errors++;
++ }
++ /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */
++ priv->txlen = 0;
++ netif_wake_queue (dev);
++ break;
++
++ case TxCOL:
++ priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL));
++ break;
++
++ case RxMISS:
++ status = MissCount (cirrus_read (dev,PP_RxMISS));
++ priv->stats.rx_errors += status;
++ priv->stats.rx_missed_errors += status;
++ break;
++ }
++ }
++}
++
++static void cirrus_transmit_timeout (struct net_device *dev)
++{
++ cirrus_t *priv = (cirrus_t *) dev->priv;
++ priv->stats.tx_errors++;
++ priv->stats.tx_heartbeat_errors++;
++ priv->txlen = 0;
++ netif_wake_queue (dev);
++}
++
++static int cirrus_start (struct net_device *dev)
++{
++ int result;
++
++ /* install interrupt handler */
++ if ((result = request_irq (dev->irq,
++ &cirrus_interrupt, 0,
++ dev->name, dev)) < 0) {
++ printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name,dev->irq);
++ return (result);
++ }
++
++ /* enable the ethernet controller */
++ cirrus_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
++ cirrus_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);
++ cirrus_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
++ cirrus_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
++ cirrus_set (dev,PP_LineCTL,SerRxON | SerTxON);
++ cirrus_set (dev,PP_BusCTL,EnableRQ);
++
++#ifdef FULL_DUPLEX
++ cirrus_set (dev,PP_TestCTL,FDX);
++#endif /* #ifdef FULL_DUPLEX */
++
++ /* start the queue */
++ netif_start_queue (dev);
++
++ MOD_INC_USE_COUNT;
++
++ return (0);
++}
++
++static int cirrus_stop (struct net_device *dev)
++{
++ /* disable ethernet controller */
++ cirrus_write (dev,PP_BusCTL,0);
++ cirrus_write (dev,PP_TestCTL,0);
++ cirrus_write (dev,PP_SelfCTL,0);
++ cirrus_write (dev,PP_LineCTL,0);
++ cirrus_write (dev,PP_BufCFG,0);
++ cirrus_write (dev,PP_TxCFG,0);
++ cirrus_write (dev,PP_RxCTL,0);
++ cirrus_write (dev,PP_RxCFG,0);
++
++ /* uninstall interrupt handler */
++ free_irq (dev->irq,dev);
++
++ /* stop the queue */
++ netif_stop_queue (dev);
++
++ MOD_DEC_USE_COUNT;
++
++ return (0);
++}
++
++static struct net_device_stats *cirrus_get_stats (struct net_device *dev)
++{
++ cirrus_t *priv = (cirrus_t *) dev->priv;
++ return (&priv->stats);
++}
++
++static void cirrus_set_receive_mode (struct net_device *dev)
++{
++ if ((dev->flags & IFF_PROMISC))
++ cirrus_set (dev,PP_RxCTL,PromiscuousA);
++ else
++ cirrus_clear (dev,PP_RxCTL,PromiscuousA);
++
++ if ((dev->flags & IFF_ALLMULTI) && dev->mc_list)
++ cirrus_set (dev,PP_RxCTL,MulticastA);
++ else
++ cirrus_clear (dev,PP_RxCTL,MulticastA);
++}
++
++static int cirrus_eeprom_wait (struct net_device *dev)
++{
++ int i;
++
++ for (i = 0; i < 200; i++) {
++ if (!(cirrus_read (dev,PP_SelfST) & SIBUSY))
++ return (0);
++ udelay (1);
++ }
++
++ return (-1);
++}
++
++static int cirrus_eeprom_read (struct net_device *dev,u16 *value,u16 offset)
++{
++ if (cirrus_eeprom_wait (dev) < 0)
++ return (-1);
++
++ cirrus_write (dev,PP_EEPROMCommand,offset | EEReadRegister);
++
++ if (cirrus_eeprom_wait (dev) < 0)
++ return (-1);
++
++ *value = cirrus_read (dev,PP_EEPROMData);
++
++ return (0);
++}
++
++static int cirrus_eeprom (struct net_device *dev,cirrus_eeprom_t *eeprom)
++{
++ u16 offset,buf[16],*word;
++ u8 checksum = 0,*byte;
++
++ return -ENODEV;
++
++ if (cirrus_eeprom_read (dev,buf,0) < 0) {
++ read_timed_out:
++ printk (KERN_DEBUG "%s: EEPROM read timed out\n",dev->name);
++ return (-ETIMEDOUT);
++ }
++
++ if ((buf[0] >> 8) != 0xa1) {
++ printk (KERN_DEBUG "%s: No EEPROM present\n",dev->name);
++ return (-ENODEV);
++ }
++
++ if ((buf[0] & 0xff) < sizeof (buf)) {
++ eeprom_too_small:
++ printk (KERN_DEBUG "%s: EEPROM too small\n",dev->name);
++ return (-ENODEV);
++ }
++
++ for (offset = 1; offset < (buf[0] & 0xff); offset++) {
++ if (cirrus_eeprom_read (dev,buf + offset,offset) < 0)
++ goto read_timed_out;
++
++ if (buf[offset] == 0xffff)
++ goto eeprom_too_small;
++ }
++
++ if (buf[1] != 0x2020) {
++ printk (KERN_DEBUG "%s: Group Header #1 mismatch\n",dev->name);
++ return (-EIO);
++ }
++
++ if (buf[5] != 0x502c) {
++ printk (KERN_DEBUG "%s: Group Header #2 mismatch\n",dev->name);
++ return (-EIO);
++ }
++
++ if (buf[12] != 0x2158) {
++ printk (KERN_DEBUG "%s: Group Header #3 mismatch\n",dev->name);
++ return (-EIO);
++ }
++
++ eeprom->io_base = buf[2];
++ eeprom->irq = buf[3];
++ eeprom->dma = buf[4];
++ eeprom->mem_base = (buf[7] << 16) | buf[6];
++ eeprom->rom_base = (buf[9] << 16) | buf[8];
++ eeprom->rom_mask = (buf[11] << 16) | buf[10];
++
++ word = (u16 *) eeprom->mac;
++ for (offset = 0; offset < 3; offset++) word[offset] = buf[13 + offset];
++
++ byte = (u8 *) buf;
++ for (offset = 0; offset < sizeof (buf); offset++) checksum += byte[offset];
++
++ if (cirrus_eeprom_read (dev,&offset,0x10) < 0)
++ goto read_timed_out;
++
++ if ((offset >> 8) != (u8) (0x100 - checksum)) {
++ printk (KERN_DEBUG "%s: Checksum mismatch (expected 0x%.2x, got 0x%.2x instead\n",
++ dev->name,
++ (u8) (0x100 - checksum),
++ offset >> 8);
++ return (-EIO);
++ }
++
++ return (0);
++}
++
++/*
++ * Architecture dependant code
++ */
++
++#ifdef CONFIG_SA1100_FRODO
++static void frodo_reset (struct net_device *dev)
++{
++ int i;
++ volatile u16 value;
++
++ /* reset ethernet controller */
++ FRODO_CPLD_ETHERNET |= FRODO_ETH_RESET;
++ mdelay (50);
++ FRODO_CPLD_ETHERNET &= ~FRODO_ETH_RESET;
++ mdelay (50);
++
++ /* we tied SBHE to CHIPSEL, so each memory access ensure the chip is in 16-bit mode */
++ for (i = 0; i < 3; i++) value = cirrus_read (dev,0);
++
++ /* FIXME: poll status bit */
++}
++#endif /* #ifdef CONFIG_SA1100_FRODO */
++
++/*
++ * Driver initialization routines
++ */
++
++static int io = 0;
++static int irq = 0;
++
++int __init ethers_probe (struct net_device *dev)
++{
++ static cirrus_t priv;
++ int i,result;
++ u16 value;
++ cirrus_eeprom_t eeprom;
++
++ printk ("Cirrus Logic CS8920A driver for Linux (V0.02)\n");
++
++ memset (&priv,0,sizeof (cirrus_t));
++
++ ether_setup (dev);
++
++ dev->open = cirrus_start;
++ dev->stop = cirrus_stop;
++ dev->hard_start_xmit = cirrus_send_start;
++ dev->get_stats = cirrus_get_stats;
++ dev->set_multicast_list = cirrus_set_receive_mode;
++ dev->tx_timeout = cirrus_transmit_timeout;
++ dev->watchdog_timeo = HZ;
++
++
++ dev->if_port = IF_PORT_10BASET;
++ dev->priv = (void *) &priv;
++
++ SET_MODULE_OWNER (dev);
++
++#if 0
++ dev->base_addr = CIRRUS_DEFAULT_IO;
++ dev->irq = CIRRUS_DEFAULT_IRQ;
++#else
++ dev->base_addr = 0x180;
++ dev->irq = 11;
++#endif
++ /* module parameters override everything */
++ if (io > 0) dev->base_addr = io;
++ if (irq > 0) dev->irq = irq;
++
++ if (!dev->base_addr) {
++ printk (KERN_ERR
++ "%s: No default I/O base address defined. Use io=... or\n"
++ "%s: define CIRRUS_DEFAULT_IO for your platform\n",
++ dev->name,dev->name);
++ return (-EINVAL);
++ }
++
++ if (!dev->irq) {
++ printk (KERN_ERR
++ "%s: No default IRQ number defined. Use irq=... or\n"
++ "%s: define CIRRUS_DEFAULT_IRQ for your platform\n",
++ dev->name,dev->name);
++ return (-EINVAL);
++ }
++
++ if ((result = check_region (dev->base_addr,16))) {
++ printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n",dev->name,dev->base_addr);
++ return (result);
++ }
++ request_region (dev->base_addr,16,dev->name);
++
++ printk("cirrus: at %04x, irq=%d\n", dev->base_addr, dev->irq);
++
++ /* read the ethernet address from the chip... we assume that all
++ * RiscStations have an EEPROM to set the CS89x0 up with, but this
++ * may not be true for future releases */
++
++ for (i = 0; i < ETH_ALEN; i += 2) {
++ unsigned short mac = cirrus_read(dev, PP_IA + i);
++
++ dev->dev_addr[i] = mac & 0xff;
++ dev->dev_addr[i+1] = (mac >> 8) & 0xff;
++ }
++
++#ifdef CONFIG_SA1100_FRODO
++ frodo_reset (dev);
++#endif /* #ifdef CONFIG_SA1100_FRODO */
++
++ /* if an EEPROM is present, use it's MAC address */
++ if (!cirrus_eeprom (dev,&eeprom))
++ for (i = 0; i < 6; i++)
++ dev->dev_addr[i] = eeprom.mac[i];
++
++#if 0
++ /* verify EISA registration number for Cirrus Logic */
++ if ((value = cirrus_read (dev,PP_ProductID)) != EISA_REG_CODE) {
++ printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value);
++ return (-ENXIO);
++ }
++
++ /* verify chip version */
++ value = cirrus_read (dev,PP_ProductID + 2);
++
++ if (VERSION (value) != CS8900A) {
++ printk (KERN_ERR "%s: unknown chip version 0x%.8x\n",dev->name,VERSION (value));
++ return (-ENXIO);
++ }
++ printk (KERN_INFO "%s: CS8900A rev %c detected\n",dev->name,'B' + REVISION (value) - REV_B);
++#endif
++
++ /* setup interrupt number */
++#if 0
++ cirrus_write (dev,PP_IntNum,0);
++#else
++ cirrus_write (dev,0x370,3);
++#endif
++ printk("cirrus: MAC ");
++
++ /* configure MAC address */
++ for (i = 0; i < ETH_ALEN; i += 2) {
++ printk("%02x ", dev->dev_addr[i]);
++ printk("%02x ", dev->dev_addr[i+1]);
++
++ cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));
++ }
++ printk("\n");
++
++ return (0);
++}
++
++EXPORT_NO_SYMBOLS;
++
++static struct net_device dev;
++
++static int __init cirrus_init (void)
++{
++ memset (&dev,0,sizeof (struct net_device));
++ dev.init = ethers_probe;
++ return (register_netdev (&dev));
++}
++
++static void __exit cirrus_cleanup (void)
++{
++ release_region (dev.base_addr,16);
++ unregister_netdev (&dev);
++}
++
++MODULE_AUTHOR ("Abraham van der Merwe <abraham at 2d3d.co.za>");
++MODULE_DESCRIPTION ("Cirrus Logic CS8900A driver for Linux (V0.02)");
++MODULE_LICENSE ("GPL");
++MODULE_PARM_DESC (io,"I/O Base Address");
++MODULE_PARM (io,"i");
++MODULE_PARM_DESC (irq,"IRQ Number");
++MODULE_PARM (irq,"i");
++
++module_init (cirrus_init);
++module_exit (cirrus_cleanup);
++
+diff -urN kernel-source-2.4.27-8/drivers/acorn/scsi/cumana_1.c kernel-source-2.4.27-8-arm-1/drivers/acorn/scsi/cumana_1.c
+--- kernel-source-2.4.27-8/drivers/acorn/scsi/cumana_1.c 2001-09-13 23:21:32.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/scsi/cumana_1.c 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/acorn/scsi/ecoscsi.c kernel-source-2.4.27-8-arm-1/drivers/acorn/scsi/ecoscsi.c
+--- kernel-source-2.4.27-8/drivers/acorn/scsi/ecoscsi.c 2002-08-03 01:39:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/scsi/ecoscsi.c 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/acorn/scsi/oak.c kernel-source-2.4.27-8-arm-1/drivers/acorn/scsi/oak.c
+--- kernel-source-2.4.27-8/drivers/acorn/scsi/oak.c 2001-10-11 17:04:57.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/acorn/scsi/oak.c 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/i2c/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/i2c/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/i2c/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/i2c/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/i2c/at91_i2c.c kernel-source-2.4.27-8-arm-1/drivers/at91/i2c/at91_i2c.c
+--- kernel-source-2.4.27-8/drivers/at91/i2c/at91_i2c.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/i2c/at91_i2c.c 2005-02-18 17:48:35.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 <phil at stimpy.netroedge.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., 675 Mass Ave, Cambridge, MA 02139, USA.
++
++*/
++
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++
++#include <asm/arch/AT91RM9200_TWI.h>
++#include <asm/arch/pio.h>
++#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 kernel-source-2.4.27-8/drivers/at91/i2c/at91_i2c.h kernel-source-2.4.27-8-arm-1/drivers/at91/i2c/at91_i2c.h
+--- kernel-source-2.4.27-8/drivers/at91/i2c/at91_i2c.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/i2c/at91_i2c.h 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/mtd/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/mtd/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/mtd/at91_dataflash.c kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/at91_dataflash.c
+--- kernel-source-2.4.27-8/drivers/at91/mtd/at91_dataflash.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/at91_dataflash.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,540 @@
++/*
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/arch/AT91RM9200_SPI.h>
++#include <asm/arch/pio.h>
++#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. */
++static struct spi_transfer_list* spi_transfer_desc;
++
++/*
++ * Perform a SPI transfer to access the DataFlash device.
++ */
++static 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.
++ */
++static 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.
++ */
++static 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.
++ */
++static 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
++ */
++static 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
++ */
++static 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.
++ */
++static int add_dataflash(int channel, char *name, int IDsize, int nr_pages, 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 = nr_pages * pagesize;
++ 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, device->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);
++ }
++
++ if (mtd_parts_nr > 0) {
++#ifdef DATAFLASH_ALWAYS_ADD_DEVICE
++ add_mtd_device(device);
++#endif
++ return add_mtd_partitions(device, mtd_parts, mtd_parts_nr);
++ }
++#endif
++ return add_mtd_device(device); /* add whole device */
++}
++
++/*
++ * Detect and initialize DataFlash device connected to specified SPI channel.
++ *
++ * Device Density ID code Nr Pages Page Size Page offset
++ * AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9
++ * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1025 264 9
++ * AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9
++ * AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9
++ * AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10
++ * AT45DB0321B 32Mbit (4M) xx1101xx (0x34) 8192 528 10
++ * AT45DB0642 64Mbit (8M) xx1111xx (0x3c) 8192 1056 11
++ * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11
++ */
++static 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 0x0c: /* 0 0 1 1 */
++ res = add_dataflash(channel, "Atmel AT45DB011B", SZ_128K, 512, 264, 9);
++ break;
++ case 0x14: /* 0 1 0 1 */
++ res = add_dataflash(channel, "Atmel AT45DB021B", SZ_256K, 1025, 264, 9);
++ break;
++ case 0x1c: /* 0 1 1 1 */
++ res = add_dataflash(channel, "Atmel AT45DB041B", SZ_512K, 2048, 264, 9);
++ break;
++ case 0x24: /* 1 0 0 1 */
++ res = add_dataflash(channel, "Atmel AT45DB081B", SZ_1M, 4096, 264, 9);
++ break;
++ case 0x2c: /* 1 0 1 1 */
++ res = add_dataflash(channel, "Atmel AT45DB161B", SZ_2M, 4096, 528, 10);
++ break;
++ case 0x34: /* 1 1 0 1 */
++ res = add_dataflash(channel, "Atmel AT45DB321B", SZ_4M, 8192, 528, 10);
++ break;
++ case 0x3c: /* 1 1 1 1 */
++ res = add_dataflash(channel, "Atmel AT45DB642", SZ_8M, 8192, 1056, 11);
++ break;
++// Currently unsupported since Atmel removed the "Main Memory Program via Buffer" commands.
++// case 0x10: /* 0 1 0 0 */
++// res = add_dataflash(channel, "Atmel AT45DB1282", SZ_16M, 16384, 1056, 11);
++// break;
++ default:
++ printk(KERN_ERR "at91_dataflash: Unknown device (%x)\n", status & 0x3c);
++ }
++ }
++ spi_release_bus(channel);
++
++ return res;
++}
++
++static 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;
++}
++
++static 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 kernel-source-2.4.27-8/drivers/at91/mtd/at91_dataflash.h kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/at91_dataflash.h
+--- kernel-source-2.4.27-8/drivers/at91/mtd/at91_dataflash.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/at91_dataflash.h 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,43 @@
++/*
++ * 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 */
++#undef DATAFLASH_ALWAYS_ADD_DEVICE /* always add whole device when using partitions? */
++
++#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 kernel-source-2.4.27-8/drivers/at91/mtd/at91_nand.c kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/at91_nand.c
+--- kernel-source-2.4.27-8/drivers/at91/mtd/at91_nand.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/at91_nand.c 2005-02-18 17:48: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 at autronix.de)
++ *
++ * Derived from drivers/mtd/spia.c
++ * Copyright (C) 2000 Steven J. Hill (sjhill at 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 <linux/slab.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/arch/hardware.h>
++#include <asm/sizes.h>
++
++#include <asm/arch/pio.h>
++#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 kernel-source-2.4.27-8/drivers/at91/mtd/at91_nand.h kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/at91_nand.h
+--- kernel-source-2.4.27-8/drivers/at91/mtd/at91_nand.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/mtd/at91_nand.h 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/net/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/net/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/net/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/net/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/net/at91_ether.c kernel-source-2.4.27-8-arm-1/drivers/at91/net/at91_ether.c
+--- kernel-source-2.4.27-8/drivers/at91/net/at91_ether.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/net/at91_ether.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,875 @@
++/*
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/config.h>
++#include <linux/mii.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <asm/io.h>
++#include <linux/pci.h>
++#include <linux/crc32.h>
++#include <asm/uaccess.h>
++#include <linux/ethtool.h>
++
++#include <asm/arch/AT91RM9200_EMAC.h>
++#include <asm/arch/pio.h>
++#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]);
++}
++
++/*
++ * 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;
++}
++
++/*
++ * 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 */
++
++ /* Update the MAC address (incase user has changed it) */
++ update_mac_address(dev);
++
++ /* 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;
++ dev->set_mac_address = set_mac_address;
++
++ 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 kernel-source-2.4.27-8/drivers/at91/net/at91_ether.h kernel-source-2.4.27-8-arm-1/drivers/at91/net/at91_ether.h
+--- kernel-source-2.4.27-8/drivers/at91/net/at91_ether.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/net/at91_ether.h 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,79 @@
++/*
++ * 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
++
++
++/* 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 kernel-source-2.4.27-8/drivers/at91/rtc/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/rtc/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/rtc/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/rtc/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/rtc/at91_rtc.c kernel-source-2.4.27-8-arm-1/drivers/at91/rtc/at91_rtc.c
+--- kernel-source-2.4.27-8/drivers/at91/rtc/at91_rtc.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/rtc/at91_rtc.c 2005-02-18 17:48:35.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 <linux/module.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++#include <asm/bitops.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <linux/rtc.h>
++
++#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 kernel-source-2.4.27-8/drivers/at91/serial/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/serial/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/serial/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/serial/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/serial/at91_serial.c kernel-source-2.4.27-8-arm-1/drivers/at91/serial/at91_serial.c
+--- kernel-source-2.4.27-8/drivers/at91/serial/at91_serial.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/serial/at91_serial.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,881 @@
++/*
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/ioport.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/arch/AT91RM9200_USART.h>
++#include <asm/mach/serial_at91rm9200.h>
++#include <asm/arch/pio.h>
++
++
++#if defined(CONFIG_SERIAL_AT91_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++#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;
++
++ /*
++ * Errata #39: RTS0 is not internally connected to PA21. We need to drive
++ * the pin manually.
++ */
++ if (port->mapbase == AT91C_VA_BASE_US0) {
++ if (mctrl & TIOCM_RTS)
++ AT91_SYS->PIOA_CODR = AT91C_PA21_RTS0;
++ else
++ AT91_SYS->PIOA_SODR = AT91C_PA21_RTS0;
++ }
++
++ 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 kernel-source-2.4.27-8/drivers/at91/spi/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/spi/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/spi/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/spi/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/spi/at91_spi.c kernel-source-2.4.27-8-arm-1/drivers/at91/spi/at91_spi.c
+--- kernel-source-2.4.27-8/drivers/at91/spi/at91_spi.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/spi/at91_spi.c 2005-02-18 17:48:35.000000000 +0000
+@@ -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 <linux/init.h>
++#include <linux/module.h>
++#include <asm/semaphore.h>
++#include <linux/pci.h>
++#include <linux/sched.h>
++#include <linux/completion.h>
++
++#include <asm/arch/AT91RM9200_SPI.h>
++#include <asm/arch/pio.h>
++#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(); break;
++ case 1: AT91_CfgPIO_SPI_CS1(); break;
++ case 2: AT91_CfgPIO_SPI_CS2(); break;
++ case 3: AT91_CfgPIO_SPI_CS3(); break;
++ }
++ 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 kernel-source-2.4.27-8/drivers/at91/spi/at91_spi.h kernel-source-2.4.27-8-arm-1/drivers/at91/spi/at91_spi.h
+--- kernel-source-2.4.27-8/drivers/at91/spi/at91_spi.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/spi/at91_spi.h 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/spi/at91_spidev.c kernel-source-2.4.27-8-arm-1/drivers/at91/spi/at91_spidev.c
+--- kernel-source-2.4.27-8/drivers/at91/spi/at91_spidev.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/spi/at91_spidev.c 2005-02-18 17:48:35.000000000 +0000
+@@ -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 <linux/module.h>
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/iobuf.h>
++#include <linux/highmem.h>
++
++#ifdef CONFIG_DEVFS_FS
++#include <linux/devfs_fs_kernel.h>
++#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 kernel-source-2.4.27-8/drivers/at91/usb/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/usb/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/usb/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/usb/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/usb/at91_usb-ohci.c kernel-source-2.4.27-8-arm-1/drivers/at91/usb/at91_usb-ohci.c
+--- kernel-source-2.4.27-8/drivers/at91/usb/at91_usb-ohci.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/usb/at91_usb-ohci.c 2005-02-18 17:48:35.000000000 +0000
+@@ -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 <brad at heeltoe.com>
++ * original OHCI driver modifications, and reworked into a cleaner form
++ * by Russell King <rmk at arm.linux.org.uk>.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++
++#include <asm/arch/AT91RM9200_UHP.h>
++
++/*
++ 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 kernel-source-2.4.27-8/drivers/at91/watchdog/Makefile kernel-source-2.4.27-8-arm-1/drivers/at91/watchdog/Makefile
+--- kernel-source-2.4.27-8/drivers/at91/watchdog/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/watchdog/Makefile 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/at91/watchdog/at91_wdt.c kernel-source-2.4.27-8-arm-1/drivers/at91/watchdog/at91_wdt.c
+--- kernel-source-2.4.27-8/drivers/at91/watchdog/at91_wdt.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/at91/watchdog/at91_wdt.c 2005-02-18 17:48:35.000000000 +0000
+@@ -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 <linux/module.h>
++#include <linux/config.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <asm/uaccess.h>
++#include <linux/init.h>
++
++#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 kernel-source-2.4.27-8/drivers/block/Makefile kernel-source-2.4.27-8-arm-1/drivers/block/Makefile
+--- kernel-source-2.4.27-8/drivers/block/Makefile 2004-08-08 00:26:04.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/block/Makefile 2005-02-18 17:48:35.000000000 +0000
+@@ -35,4 +35,10 @@
+
+ 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 kernel-source-2.4.27-8/drivers/block/ll_rw_blk.c kernel-source-2.4.27-8-arm-1/drivers/block/ll_rw_blk.c
+--- kernel-source-2.4.27-8/drivers/block/ll_rw_blk.c 2004-04-14 14:05:29.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/block/ll_rw_blk.c 2005-02-18 17:48:35.000000000 +0000
+@@ -32,6 +32,19 @@
+ #include <linux/slab.h>
+ #include <linux/module.h>
+
++/* 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
+ */
+@@ -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 kernel-source-2.4.27-8/drivers/cdrom/cdrom.c kernel-source-2.4.27-8-arm-1/drivers/cdrom/cdrom.c
+--- kernel-source-2.4.27-8/drivers/cdrom/cdrom.c 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/cdrom/cdrom.c 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/char/Config.in kernel-source-2.4.27-8-arm-1/drivers/char/Config.in
+--- kernel-source-2.4.27-8/drivers/char/Config.in 2005-01-19 09:57:41.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/Config.in 2005-02-18 17:48:35.000000000 +0000
+@@ -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,20 @@
+ 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
++
++if [ "$CONFIG_ARCH_BAST" = "y" ]; then
++ bool 'Enable dummy keyboard support' CONFIG_DUMMY_KEYB
++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 +192,17 @@
+
+ source drivers/i2c/Config.in
+
++if [ "$CONFIG_I2C" != "n" ]; then
++ dep_tristate ' DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C
++fi
++
++if [ "$CONFIG_I2C" != "n" ]; then
++ dep_tristate ' ST M41ST87 RTC/Ram' CONFIG_I2C_M41ST87 $CONFIG_I2C
++fi
++
++
++source drivers/l3/Config.in
++
+ mainmenu_option next_comment
+ comment 'Mice'
+ tristate 'Bus Mouse Support' CONFIG_BUSMOUSE
+@@ -245,11 +258,14 @@
+ 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 ' S3C2410 watchdog' CONFIG_S3C2410_WATCHDOG $CONFIG_CPU_S3C2410X
++ 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
+@@ -325,6 +341,18 @@
+ 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_BAST" = "y" -o "$CONFIG_ARCH_S3C2410" = "y" ]; then
++ tristate 'BAST Real Time Clock' CONFIG_BAST_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 kernel-source-2.4.27-8/drivers/char/Makefile kernel-source-2.4.27-8-arm-1/drivers/char/Makefile
+--- kernel-source-2.4.27-8/drivers/char/Makefile 2004-08-08 00:26:04.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/Makefile 2005-02-18 17:48:35.000000000 +0000
+@@ -106,12 +106,46 @@
+ 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
++
++# for the moment we have no alternative keyboard
++ ifeq ($(CONFIG_ARCH_BAST),y)
++ KEYMAP = defkeymap.o pc_keyb.o
++ endif
++
+ endif
+
+ ifeq ($(ARCH),sh)
+@@ -173,11 +207,9 @@
+ obj-$(CONFIG_SERIAL) += $(SERIAL)
+ obj-$(CONFIG_PARPORT_SERIAL) += parport_serial.o
+ 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)
+@@ -254,6 +286,9 @@
+ 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_BAST_RTC) += bast-rtc.o
++obj-$(CONFIG_OMAHA_RTC) += omaha-rtc.o
+ ifeq ($(CONFIG_PPC),)
+ obj-$(CONFIG_NVRAM) += nvram.o
+ endif
+@@ -292,6 +327,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
+@@ -320,15 +356,29 @@
+ 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_S3C2410_WATCHDOG) += s3c2410-wdt.o
+ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+ obj-$(CONFIG_INDYDOG) += indydog.o
+ obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o
+
++# I2C char devices
++obj-$(CONFIG_I2C_DS1307) += ds1307.o
++obj-$(CONFIG_I2C_M41ST87) += st-m41st87.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 kernel-source-2.4.27-8/drivers/char/amba_kmi_keyb.c kernel-source-2.4.27-8-arm-1/drivers/char/amba_kmi_keyb.c
+--- kernel-source-2.4.27-8/drivers/char/amba_kmi_keyb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/amba_kmi_keyb.c 2005-02-18 17:48:35.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 at nokia.com: enabled PS/2 reconnection
++ */
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h> /* for in_interrupt */
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/delay.h> /* for udelay */
++#include <linux/kbd_kern.h> /* for keyboard_tasklet */
++#include <linux/kbd_ll.h>
++
++#include <asm/io.h>
++#include <asm/hardware/amba_kmi.h>
++#include <asm/mach/amba_kmi.h>
++#include <asm/keyboard.h>
++
++//#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 <linux/fs.h> /* for struct file_ops */
++#include <linux/poll.h> /* for poll_table */
++#include <linux/miscdevice.h> /* for struct miscdev */
++#include <linux/random.h> /* for add_mouse_randomness */
++#include <linux/slab.h> /* for kmalloc */
++#include <linux/smp_lock.h> /* for {un,}lock_kernel */
++#include <linux/spinlock.h>
++
++#include <asm/uaccess.h>
++
++#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 at 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 at 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 kernel-source-2.4.27-8/drivers/char/anakin_ts.c kernel-source-2.4.27-8-arm-1/drivers/char/anakin_ts.c
+--- kernel-source-2.4.27-8/drivers/char/anakin_ts.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/anakin_ts.c 2005-02-18 17:48:35.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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++#include <linux/fs.h>
++#include <linux/sched.h>
++#include <linux/poll.h>
++#include <linux/miscdevice.h>
++#include <linux/init.h>
++#include <linux/compiler.h>
++#include <linux/interrupt.h>
++
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/irq.h>
++
++/*
++ * 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 <chan at aleph1.co.uk>");
++MODULE_DESCRIPTION("Anakin touchscreen driver");
++MODULE_SUPPORTED_DEVICE("touchscreen/anakin");
+diff -urN kernel-source-2.4.27-8/drivers/char/bast-rtc.c kernel-source-2.4.27-8-arm-1/drivers/char/bast-rtc.c
+--- kernel-source-2.4.27-8/drivers/char/bast-rtc.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/bast-rtc.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,775 @@
++/*
++ * Real Time Clock interface for Linux on S3CXXX
++ *
++ * Copyright (c) 2002 SAMSUGN ELECTRONICS
++ * SW.LEE <hitchcar at sec.samsung.com>
++ *
++ *
++ * BAST changes and cleanup (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * based on Strongarm Copyright (c) 2000 Nils Faerber
++ *
++*/
++
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++#include <asm/bitops.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <linux/rtc.h>
++
++#include <asm/io.h>
++#include <asm/arch-s3c2410/S3C2410-rtc.h>
++
++#include <asm/mach-types.h>
++
++/* todo -
++ * - test alarm
++ * - add configurable periodic interrupt support
++*/
++
++static void rtc_exit(void);
++
++#if DEBUG || 0
++#define PDEBUG(fmt,arg...) \
++ printk(KERN_ERR fmt,##arg)
++#else
++#define PDEBUG(fmt,arg...) \
++ do { } while (0)
++#endif
++
++
++#define DRIVER_VERSION "1.00"
++#define TIMER_FREQ 3686400 /* need to recalculate */
++
++#define RTC_DEF_DIVIDER 32768 - 1
++#define RTC_DEF_TRIM 0
++
++static unsigned long rtc_status;
++static unsigned long rtc_irq_data;
++static unsigned long rtc_freq = 1024;
++static unsigned long rtc_has_irq =1;
++static unsigned char rtc_uie = 0; /* rtc uie enable */
++
++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};
++
++#ifndef BCD_TO_BIN
++#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
++#endif
++
++#ifndef BIN_TO_BCD
++#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
++#endif
++
++#ifndef IS_BCD
++#define IS_BCD(val) (((val & 0xf0) <= 0x90) && (((val) & 0xf) <= 0x9))
++#endif
++
++#define is_leap(year) \
++ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
++
++#define RTC_EPOCH (2000)
++
++
++typedef enum {
++ ENCODE_TIME,
++ ENCODE_ALARM,
++ DECODE_TIME,
++ DECODE_ALARM
++} CODE_RTC ;
++
++
++static void encode_s3c_rtc(struct rtc_time *tm, CODE_RTC flag)
++{
++ unsigned int year, mon, day, hour, min, sec;
++
++ year = tm->tm_year - RTC_EPOCH;
++ mon = tm->tm_mon + 1;
++ day = tm->tm_mday;
++ hour = tm->tm_hour;
++ min = tm->tm_min;
++ sec = tm->tm_sec;
++
++ PDEBUG("encode_s3c_rtc: %s: year=%d, mon=%d, day=%d, hour=%d, min=%d, sec=%d\n",
++ (flag == ENCODE_TIME) ? "time" :"alarm",
++ year, mon, day, hour, min, sec);
++
++ year = BIN_TO_BCD(year);
++ mon = BIN_TO_BCD(mon);
++ day = BIN_TO_BCD(day);
++ hour = BIN_TO_BCD(hour);
++ min = BIN_TO_BCD(min);
++ sec = BIN_TO_BCD(sec);
++
++ PDEBUG("encode_s3c_rtc: year=%02x, mon=%02x, day=%02x, hour=%02x, min=%02x, sec=%02x\n",
++ year, mon, day, hour, min, sec);
++
++ if( flag == ENCODE_TIME ) {
++ __raw_writeb(year, S3C2410_RTCYEAR);
++ __raw_writeb(mon, S3C2410_RTCMON);
++ __raw_writeb(day, S3C2410_RTCDATE);
++ __raw_writeb(hour, S3C2410_RTCHOUR);
++ __raw_writeb(min, S3C2410_RTCMIN);
++ __raw_writeb(sec, S3C2410_RTCSEC);
++ }
++ else {
++ /* As far as I know, alarm func can be applied to h/m/s */
++ /*
++ rALMYEAR = year;
++ rALMMON = mon ;
++ rALMDATE = day;
++ */
++ __raw_writeb(hour, S3C2410_ALMHOUR);
++ __raw_writeb(min, S3C2410_ALMMIN);
++ __raw_writeb(sec, S3C2410_ALMSEC);
++ }
++}
++
++
++static unsigned long decode_s3c_rtc(CODE_RTC flag, int *valid, int debug)
++{
++ uint year,mon,day,hour,min,sec;
++ uint attempts = 3; /* no of attempts if suspect roll-over */
++
++ do {
++ /* read either the alarm or the time...
++ */
++ if ( flag == DECODE_TIME ) {
++ year = __raw_readb(S3C2410_RTCYEAR);
++ mon = __raw_readb(S3C2410_RTCMON);
++ day = __raw_readb(S3C2410_RTCDATE);
++ hour = __raw_readb(S3C2410_RTCHOUR);
++ min = __raw_readb(S3C2410_RTCMIN);
++ sec = __raw_readb(S3C2410_RTCSEC);
++ } else {
++ year = __raw_readb(S3C2410_ALMYEAR);
++ mon = __raw_readb(S3C2410_ALMMON);
++ day = __raw_readb(S3C2410_ALMDATE);
++ hour = __raw_readb(S3C2410_ALMHOUR);
++ min = __raw_readb(S3C2410_ALMMIN);
++ sec = __raw_readb(S3C2410_ALMSEC);
++ }
++
++ /* if we read a value of seconds as zero, then we possibly
++ * had a carry-through whilst reading... so we go back for
++ * several attempts to read the value of the rtc until we
++ * can be reasonably sure that the value has stabilised.
++ */
++ } while ( sec == 0 && attempts-- > 0 );
++
++ /* we use this to check wether the rtc time is valid or not */
++ if (valid != NULL) {
++ *valid = 1;
++
++ if (!IS_BCD(year) || !IS_BCD(mon) || !IS_BCD(day) ||
++ !IS_BCD(hour) || !IS_BCD(min) || !IS_BCD(sec))
++ *valid = 0;
++
++ /* some quick checks on the bcd values for range */
++
++ if (mon > 0x31 || day > 0x31)
++ *valid = 0;
++
++ if (hour > 0x24 || min > 0x59 || sec > 0x59)
++ *valid = 0;
++ }
++
++ if (debug)
++ PDEBUG("bcd: year=%02x, mon=%02x, day=%02x, hour=%02x, min=%02x, sec=%02x\n",
++ year, mon, day, hour, min, sec);
++
++ year = BCD_TO_BIN(year);
++ mon = BCD_TO_BIN(mon);
++ day = BCD_TO_BIN(day);
++ hour = BCD_TO_BIN(hour);
++ min = BCD_TO_BIN(min);
++ sec = BCD_TO_BIN(sec);
++
++ year += RTC_EPOCH;
++
++ if (debug)
++ PDEBUG( "\n year %d mon %d day %d hour %d min %d sec %d \n",
++ year,mon,day,hour,min,sec);
++
++ return mktime (year, mon, day, hour, min, sec);
++}
++
++
++/*
++ * 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;
++}
++
++
++typedef enum {
++ AIE_ON,
++ AIE_OFF,
++ ALM_SET,
++ ALM_READ
++} S3C_ALARM;
++
++
++static u16 s3c_alarm_conf(S3C_ALARM al,void* tm)
++{
++ switch (al ) {
++ case AIE_ON:
++ /* turn on all alarm matching and enable the alarm */
++ __raw_writeb(S3C2410_RTCALM_ALL, S3C2410_RTCALM);
++ return 0;
++
++ case AIE_OFF:
++ __raw_writeb(0, S3C2410_RTCALM);
++ return 0;
++ case ALM_SET:
++ break;
++ case ALM_READ:
++ break;
++ default:
++ break;
++ }
++ return 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
++
++#if 0
++static u16 get_rtc_status(void)
++{
++ u16 ret = 0;
++#if 0
++ if (__raw_readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE)
++ ret |= RTC_IRQF|RTC_UF;
++#endif
++
++ if (__raw_readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN)
++ ret |= RTC_IRQF|RTC_AF;
++
++ /* RTC_PF needs to be set here as well... */
++ return ret;
++}
++#endif
++
++static void
++interrupt_update(int flag)
++{
++ /* update irq data & counter */
++ rtc_irq_data += 0x100;
++ rtc_irq_data |= RTC_IRQF | flag;
++
++ /* wake up waiting process */
++ wake_up_interruptible(&rtc_wait);
++ kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
++}
++
++/* uie_count is a timeout to generate an UF irq if we are not actually
++ * seeing the rtc update (xtal problem, etc)
++ */
++static short uie_count = 250;
++static unsigned long last_time = 0;
++
++void
++bast_rtc_check(void)
++{
++ static unsigned long rtc_cur;
++
++ if (!rtc_uie)
++ return;
++
++ /* this part checks the current time with the last known
++ * time reported and the current time read, and then generating
++ * the update interrupt
++ */
++
++ rtc_cur = decode_s3c_rtc(DECODE_TIME, NULL, 0);
++
++ if (rtc_cur != last_time || uie_count-- < 0) {
++ interrupt_update(RTC_UF);
++
++ last_time = rtc_cur;
++ uie_count = 250;
++ }
++}
++
++
++static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++ PDEBUG("rtc_interrupt: BCDMIN=%02x\n", __raw_readb(S3C2410_RTCMIN));
++
++ interrupt_update(RTC_AF);
++}
++
++
++static void rtc_period_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++ PDEBUG("rtc_period_interrupt \n");
++
++ if (rtc_irq_data & RTC_PF) {
++ /* I am not sure of this computation help me !! */
++ 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)
++{
++ PDEBUG( "rtc_open \n");
++ 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);
++ 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);
++ if ( rtc_irq_data != 0 )
++ return POLLIN | POLLRDNORM;
++
++ PDEBUG(" No descitptor are ready \n");
++ return 0 ; /* means no descriptors are ready */
++}
++
++static loff_t rtc_llseek(struct file *file, loff_t offset, int origin)
++{
++ return -ESPIPE;
++}
++
++
++/* if you use rtc_read Without RTC_IRQ, rtc_read will be blocked.
++ * How about using return -EIO if so ?
++ *
++ */
++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 ( rtc_has_irq == 0 ) return -EIO;
++#if 0
++ if ( file->f_flags & O_NONBLOCK)
++ PDEBUG( " NONBLOCK reading \n");
++ else
++ PDEBUG( " BLOCK read \n");
++#endif
++ 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);
++
++ 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;
++}
++
++#if 0
++typedef enum {
++ UIE_ON ,
++ UIE_OFF,
++ UIE_COUNT
++} TICK_STATUS;
++
++
++static void tick_time_conf(TICK_STATUS flag,short count)
++{
++ if ( count >127 ) printk(KERN_ERR "Wrong Parameter \n");
++
++ switch (flag ) {
++ case UIE_ON : /* TICNT[7] : Tick int enable */
++ rTICNT = ( rTICNT & 0x7f ) | 1<<7 ;
++ break;
++ case UIE_OFF:
++ rTICNT = ( rTICNT & 0x7f );
++ break;
++ case UIE_COUNT: /* TICNT[6:0] : Tick Time Count */
++ rTICNT = ( rTICNT & 0x80) | count;
++ break;
++ }
++ return;
++}
++#endif
++
++typedef enum {
++ IRQP_SET,
++ PIE_OFF,
++ PIE_ON
++} IRQP_FLAGS;
++
++#if 0
++static int irqp_conf(IRQP_FLAGS flag )
++{
++ int ret=0;
++// int period = 0 ;
++ uint period ;
++// period = 1/rtc_freq;
++
++ switch(flag) {
++ case IRQP_SET:
++ PDEBUG(" irqp_conf SET \n");
++ rTCFG0 |= SYS_TIMER01_PRESCALER;
++ rTCFG1 = (rTCFG1&~(0xf<<0))|(SYS_TIMER0_MUX<<0);
++
++ /* Now period 0xf7cd8 = 1015000 */
++ period = PCLK / ((SYS_TIMER01_PRESCALER +1)*(SYS_TIMER0_DIVIDER));
++ rTCNTB0 = period / rtc_freq;
++// printk(KERN_ERR " period 0x%08x rtc_freq 0x%08X TCMTB0 0x%08X \n",
++// period,rtc_freq,period/rtc_freq );
++ if ( rTCNTB0 > 65535 ) { printk(KERN_ERR "OVER MAX INTERVAL \n"); return -1 ;}
++ PDEBUG(" rTCNTB0 0x%08X freq 0x%08X \n",rTCNTB0,(uint)rtc_freq);
++ rTCMPB0 = 0;
++ rTCON = (rTCON&~(0xf))|(0xA); /* the auto reload ,manual update for Timer 1 */
++ rTCON = (rTCON&~(0xf))|(0); /* Stop and clear manual update for Timer1 */
++ break;
++ case PIE_ON:
++ PDEBUG(" irqp_conf ON \n");
++ rTCON = (rTCON&~(0xf))|(0x9); /* Auto reload & Start */
++ break;
++ case PIE_OFF:
++ PDEBUG(" irqp_conf OFF \n");
++ rTCON = (rTCON&~(0xf))|(0); /* Stop */
++ break;
++ default:
++ break;
++ }
++ return ret; /* success */
++}
++#endif
++
++static int rtc_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct rtc_time tm, tm2;
++
++ PDEBUG("rtc_ioctl: inode=%p, file=%p, cmd=%08x, arg=%08x\n",
++ inode, file, cmd, arg);
++
++ switch (cmd) {
++ case RTC_AIE_OFF:
++ spin_lock_irq(&rtc_lock);
++ rtc_irq_data = 0;
++ rtc_has_irq = 0;
++ s3c_alarm_conf(AIE_OFF,NULL);
++ spin_unlock_irq(&rtc_lock);
++ return 0;
++ case RTC_AIE_ON:
++ spin_lock_irq(&rtc_lock);
++ rtc_irq_data = 0;
++ rtc_has_irq = 1;
++ s3c_alarm_conf(AIE_ON,NULL);
++ spin_unlock_irq(&rtc_lock);
++ return 0;
++ case RTC_UIE_OFF:
++ spin_lock_irq(&rtc_lock);
++ rtc_irq_data = 0;
++ rtc_has_irq = 0;
++ rtc_uie = 0;
++ spin_unlock_irq(&rtc_lock);
++ return 0;
++ case RTC_UIE_ON:
++ PDEBUG( " RTC_UIE_ON \n");
++ spin_lock_irq(&rtc_lock);
++ rtc_uie = 1;
++ rtc_irq_data = 0;
++ rtc_has_irq = 1;
++ spin_unlock_irq(&rtc_lock);
++ return 0;
++
++ case RTC_PIE_OFF:
++#if 0
++ spin_lock_irq(&rtc_lock);
++ irqp_conf(PIE_OFF);
++ rtc_irq_data = 0;
++ rtc_has_irq = 0;
++ spin_unlock_irq(&rtc_lock);
++ return 0;
++#else
++ return -EINVAL;
++#endif
++ case RTC_PIE_ON:
++#if 0
++ if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE))
++ return -EACCES;
++ spin_lock_irq(&rtc_lock);
++ irqp_conf(PIE_ON);
++ rtc_irq_data = 0;
++ rtc_has_irq = 1;
++ spin_unlock_irq(&rtc_lock);
++ return 0;
++#else
++ return -EINVAL;
++#endif
++ case RTC_ALM_READ:
++ decodetime (decode_s3c_rtc(DECODE_ALARM, NULL, 1), &tm);
++ break;
++ case RTC_ALM_SET:
++ if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2)))
++ return -EFAULT;
++ decodetime (decode_s3c_rtc(DECODE_TIME, NULL, 1), &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;
++ PDEBUG(" RTC_ALM_SET \n");
++ PDEBUG("\ntm.tm_year %d tm.tm_mon %d tm.tm_mday %d tm.tm_hour %d tm.tm_min %d tm.tm_sec %d \n",
++ tm.tm_year,tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
++ encode_s3c_rtc(&tm,ENCODE_ALARM);
++ return 0;
++
++ case RTC_RD_TIME:
++ PDEBUG("RTC_RD_TIME \n");
++ decodetime (decode_s3c_rtc(DECODE_TIME, NULL, 1), &tm);
++ break;
++ case RTC_SET_TIME:
++ PDEBUG("RTC_SET_TIME \n");
++ 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;
++ PDEBUG("RTC_SET_TIME: year %d mon %d mday %d hour %d min %d sec %d \n",
++ tm.tm_year,tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
++ encode_s3c_rtc(&tm, ENCODE_TIME);
++
++ return 0;
++ case RTC_IRQP_READ:
++ return -EINVAL;
++ case RTC_IRQP_SET:
++ return -EINVAL;
++ 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 s3c_rtc_miscdev = {
++ RTC_MINOR,
++ "rtc",
++ &rtc_fops
++};
++
++static int __init rtc_init(void)
++{
++ int ret;
++ int valid;
++
++ if (machine_is_vr1000()) {
++ printk("Not installing S3C2410 RTC on VR1000\n");
++ return 0;
++ }
++
++ printk("Installing S3C2410 RTC \n");
++ misc_register (&s3c_rtc_miscdev);
++ //create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL);
++
++ ret = request_irq (IRQ_TICK, rtc_interrupt, SA_INTERRUPT,
++ "RTC Tick", NULL);
++ if (ret) {
++ printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_TICK);
++ goto IRQ_TICK_failed;
++ }
++
++ ret = request_irq (IRQ_RTC, rtc_interrupt, SA_INTERRUPT,
++ "RTC Alarm", NULL);
++ if (ret) {
++ printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTC);
++ goto IRQ_TICK_failed;
++ }
++
++#if 0
++ ret = request_irq(IRQ_TIMER0,rtc_period_interrupt,SA_INTERRUPT,"RTC PERIOD",NULL);
++ if (ret) {
++ printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_TIMER0);
++ goto IRQ_TICK_failed;
++ }
++#endif
++
++ printk (KERN_INFO "S3C Real Time Clock driver v" DRIVER_VERSION "\n");
++
++ /* need to check if rtc data is valid or not */
++
++ __raw_writeb(S3C2410_RTCCON_RTCEN, S3C2410_RTCCON);
++
++
++ valid = 0;
++ (void)decode_s3c_rtc(DECODE_TIME, &valid, 1);
++
++ if (!valid) {
++ struct rtc_time tm;
++
++ printk(KERN_ERR "S3C RTC: time value invalid, resetting\n");
++
++ tm.tm_year = 0;
++ tm.tm_mday = 1;
++ tm.tm_mon = 0;
++ tm.tm_hour = 0;
++ tm.tm_min = 0;
++ tm.tm_sec = 0;
++
++ /* ok, do the proper thing to re-set the time */
++ encode_s3c_rtc(&tm, ENCODE_TIME);
++ }
++
++ return 0;
++
++IRQ_TICK_failed:
++ rtc_exit();
++ return ret;
++}
++
++/* ? - marking __exit is causing link to fail? */
++static void rtc_exit(void)
++{
++ free_irq (IRQ_TICK, NULL);
++ free_irq (IRQ_RTC, NULL);
++ misc_deregister (&s3c_rtc_miscdev);
++}
++
++
++module_init(rtc_init);
++module_exit(rtc_exit);
++
++MODULE_AUTHOR("Sangwook Lee <hitchcar at sec.samsung.com>");
++MODULE_DESCRIPTION("S3C Realtime Clock Driver (RTC)");
++EXPORT_NO_SYMBOLS;
++
++
++
++/**
++
++1. Reading from S3C RTC
++ S3C Timer -> seconds time -> Gregorian date -> struct rtc_time
++ decode_s3c_rtc decodetime()
++
++2. Writing into S3C RTC.
++ User App -> struct rtc_time -> encode_s3c_rtc
++
++*/
+diff -urN kernel-source-2.4.27-8/drivers/char/cerf_keyb.c kernel-source-2.4.27-8-arm-1/drivers/char/cerf_keyb.c
+--- kernel-source-2.4.27-8/drivers/char/cerf_keyb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/cerf_keyb.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,380 @@
++/*
++ cerf_keyb.c: This is the end. Daniel is writing a device driver!!!
++*/
++#include <linux/config.h>
++
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/mm.h>
++#include <linux/signal.h>
++#include <linux/init.h>
++#include <linux/kbd_ll.h>
++#include <linux/delay.h>
++#include <linux/random.h>
++#include <linux/poll.h>
++#include <linux/miscdevice.h>
++#include <linux/slab.h>
++#include <linux/kbd_kern.h>
++#include <linux/smp_lock.h>
++#include <linux/timer.h>
++
++#include <asm/keyboard.h>
++#include <asm/bitops.h>
++#include <asm/uaccess.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++
++#include <asm/io.h>
++
++#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 kernel-source-2.4.27-8/drivers/char/clps711x_keyb.c kernel-source-2.4.27-8-arm-1/drivers/char/clps711x_keyb.c
+--- kernel-source-2.4.27-8/drivers/char/clps711x_keyb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/clps711x_keyb.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,547 @@
++/*
++ * drivers/char/clps711x_keyb.c
++ *
++ * Copyright (C) 2001 Thomas Gleixner <gleixner at autronix.de>
++ *
++ * 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 <linux/config.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/ptrace.h>
++#include <linux/signal.h>
++#include <linux/timer.h>
++#include <linux/tqueue.h>
++#include <linux/random.h>
++#include <linux/ctype.h>
++#include <linux/init.h>
++#include <linux/kbd_ll.h>
++#include <linux/kbd_kern.h>
++#include <linux/delay.h>
++#include <linux/proc_fs.h>
++
++#include <asm/bitops.h>
++#include <asm/keyboard.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/uaccess.h>
++
++#include <asm/io.h>
++#include <asm/system.h>
++
++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 kernel-source-2.4.27-8/drivers/char/console.c kernel-source-2.4.27-8-arm-1/drivers/char/console.c
+--- kernel-source-2.4.27-8/drivers/char/console.c 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/console.c 2005-02-18 17:48:35.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 <rmk at arm.linux.org.uk>, July 1998
+ */
+
++#define CONSOLE_WIP
++
+ #include <linux/module.h>
+ #include <linux/sched.h>
+ #include <linux/tty.h>
+@@ -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,87 +1519,146 @@
+ 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;
++ 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;
++ }
++ 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 = ESsquare;
+- return;
++ vc_state = con_write_ctrl_ESsquare;
++ break;
+ case ']':
+- vc_state = ESnonstd;
+- return;
++ vc_state = con_write_ctrl_ESnonstd;
++ break;
+ case '%':
+- vc_state = ESpercent;
+- return;
++ vc_state = con_write_ctrl_ESpercent;
++ break;
+ case 'E':
+ cr(currcons);
+ lf(currcons);
+- return;
++ break;
+ case 'M':
+ ri(currcons);
+- return;
++ break;
+ case 'D':
+ lf(currcons);
+- return;
++ break;
+ case 'H':
+ tab_stop[x >> 5] |= (1 << (x & 31));
+- return;
++ break;
+ case 'Z':
+ respond_ID(tty);
+- return;
++ break;
+ case '7':
+ save_cur(currcons);
+- return;
++ break;
+ case '8':
+ restore_cur(currcons);
+- return;
++ return 1;
+ case '(':
+- vc_state = ESsetG0;
+- return;
++ vc_state = con_write_ctrl_ESsetG0;
++ break;
+ case ')':
+- vc_state = ESsetG1;
+- return;
++ vc_state = con_write_ctrl_ESsetG1;
++ break;
+ case '#':
+- vc_state = EShash;
+- return;
++ vc_state = con_write_ctrl_EShash;
++ break;
+ case 'c':
+ reset_terminal(currcons,1);
+- return;
++ return 1;
+ case '>': /* Numeric keypad */
+ clr_kbd(kbdapplic);
+- return;
++ break;
+ case '=': /* Appl. keypad */
+ set_kbd(kbdapplic);
+- return;
++ break;
+ }
+- return;
+- case ESnonstd:
+- if (c=='P') { /* palette escape sequence */
++ 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<NPAR; npar++)
+ par[npar] = 0 ;
+ npar = 0 ;
+- vc_state = ESpalette;
+- return;
+- } else if (c=='R') { /* reset palette */
+- reset_palette(currcons);
+- vc_state = ESnormal;
+- } else
+- vc_state = ESnormal;
+- return;
+- case ESpalette:
++ vc_state = con_write_ctrl_ESpalette;
++ break;
++ case 'R': /* reset palette */
++ reset_palette (currcons);
++ default:
++ vc_state = con_write_ctrl_ESnormal;
++ }
++ return 0;
++}
++
++static int con_write_ctrl_ESpalette(int currcons, struct tty_struct *tty, unsigned int c)
++{
+ if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
+ par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
+ if (npar==7) {
+@@ -1592,48 +1670,59 @@
+ palette[i] = 16*par[j++];
+ palette[i] += par[j];
+ set_palette(currcons);
+- vc_state = ESnormal;
++ vc_state = con_write_ctrl_ESnormal;
+ }
+ } else
+- vc_state = ESnormal;
+- return;
+- case ESsquare:
++ 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 = ESgetpars;
++ vc_state = con_write_ctrl_ESgetpars;
+ if (c == '[') { /* Function key */
+- vc_state=ESfunckey;
+- return;
++ vc_state = con_write_ctrl_ESfunckey;
++ return 0;
+ }
+ ques = (c=='?');
+ if (ques)
+- return;
+- case ESgetpars:
++ 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<NPAR-1) {
+ npar++;
+- return;
++ return 0;
+ } else if (c>='0' && c<='9') {
+ par[npar] *= 10;
+ par[npar] += c-'0';
+- return;
+- } else vc_state=ESgotpars;
+- case ESgotpars:
+- vc_state = ESnormal;
++ 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;
++ return 0;
+ case 'l':
+ set_mode(currcons,0);
+- return;
++ 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;
++ return 0;
+ }
+ break;
+ case 'm':
+@@ -1643,7 +1732,7 @@
+ complement_mask = par[0]<<8 | par[1];
+ else
+ complement_mask = s_complement_mask;
+- return;
++ return 0;
+ }
+ break;
+ case 'n':
+@@ -1653,69 +1742,69 @@
+ else if (par[0] == 6)
+ cursor_report(currcons,tty);
+ }
+- return;
++ return 0;
+ }
+ if (ques) {
+ ques = 0;
+- return;
++ return 0;
+ }
+ switch(c) {
+ case 'G': case '`':
+ if (par[0]) par[0]--;
+ gotoxy(currcons,par[0],y);
+- return;
++ break;
+ case 'A':
+ if (!par[0]) par[0]++;
+ gotoxy(currcons,x,y-par[0]);
+- return;
++ break;
+ case 'B': case 'e':
+ if (!par[0]) par[0]++;
+ gotoxy(currcons,x,y+par[0]);
+- return;
++ break;
+ case 'C': case 'a':
+ if (!par[0]) par[0]++;
+ gotoxy(currcons,x+par[0],y);
+- return;
++ break;
+ case 'D':
+ if (!par[0]) par[0]++;
+ gotoxy(currcons,x-par[0],y);
+- return;
++ break;
+ case 'E':
+ if (!par[0]) par[0]++;
+ gotoxy(currcons,0,y+par[0]);
+- return;
++ break;
+ case 'F':
+ if (!par[0]) par[0]++;
+ gotoxy(currcons,0,y-par[0]);
+- return;
++ break;
+ case 'd':
+ if (par[0]) par[0]--;
+ gotoxay(currcons,x,par[0]);
+- return;
++ break;
+ case 'H': case 'f':
+ if (par[0]) par[0]--;
+ if (par[1]) par[1]--;
+ gotoxay(currcons,par[1],par[0]);
+- return;
++ break;
+ case 'J':
+ csi_J(currcons,par[0]);
+- return;
++ break;
+ case 'K':
+ csi_K(currcons,par[0]);
+- return;
++ break;
+ case 'L':
+ csi_L(currcons,par[0]);
+- return;
++ break;
+ case 'M':
+ csi_M(currcons,par[0]);
+- return;
++ break;
+ case 'P':
+ csi_P(currcons,par[0]);
+- return;
++ break;
+ case 'c':
+ if (!par[0])
+ respond_ID(tty);
+- return;
++ break;
+ case 'g':
+ if (!par[0])
+ tab_stop[x >> 5] &= ~(1 << (x & 31));
+@@ -1726,16 +1815,16 @@
+ tab_stop[3] =
+ tab_stop[4] = 0;
+ }
+- return;
++ break;
+ case 'm':
+ csi_m(currcons);
+- return;
++ 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);
+- return;
++ break;
+ case 'r':
+ if (!par[0])
+ par[0]++;
+@@ -1748,41 +1837,50 @@
+ bottom=par[1];
+ gotoxay(currcons,0,0);
+ }
+- return;
++ break;
+ case 's':
+ save_cur(currcons);
+- return;
++ break;
+ case 'u':
+ restore_cur(currcons);
+- return;
++ return 1;
+ case 'X':
+ csi_X(currcons, par[0]);
+- return;
++ break;
+ case '@':
+ csi_at(currcons,par[0]);
+- return;
++ break;
+ case ']': /* setterm functions */
+ setterm_command(currcons);
+- return;
++ break;
+ }
+- return;
+- case ESpercent:
+- vc_state = ESnormal;
++ 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;
+- return;
++ break;
+ case 'G': /* prelim official escape code */
+ case '8': /* retained for compatibility */
+ utf = 1;
+- return;
++ break;
+ }
+- return;
+- case ESfunckey:
+- vc_state = ESnormal;
+- return;
+- case EShash:
+- vc_state = ESnormal;
++ 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 =
+@@ -1792,36 +1890,55 @@
+ (video_erase_char & 0xff00) | ' ';
+ do_update_region(currcons, origin, screenbuf_size/2);
+ }
+- return;
+- case ESsetG0:
+- if (c == '0')
++ 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;
+- else if (c == 'B')
++ break;
++ case 'B':
+ G0_charset = LAT1_MAP;
+- else if (c == 'U')
++ break;
++ case 'U':
+ G0_charset = IBMPC_MAP;
+- else if (c == 'K')
++ break;
++ case 'K':
+ G0_charset = USER_MAP;
+- if (charset == 0)
++ break;
++ }
++ if (charset == 0) {
+ translate = set_translate(G0_charset,currcons);
+- vc_state = ESnormal;
+- return;
+- case ESsetG1:
+- if (c == '0')
++ 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;
+- else if (c == 'B')
++ break;
++ case 'B':
+ G1_charset = LAT1_MAP;
+- else if (c == 'U')
++ break;
++ case 'U':
+ G1_charset = IBMPC_MAP;
+- else if (c == 'K')
++ break;
++ case 'K':
+ G1_charset = USER_MAP;
+- if (charset == 1)
++ break;
++ }
++ if (charset == 1) {
+ translate = set_translate(G1_charset,currcons);
+- vc_state = ESnormal;
+- return;
+- default:
+- vc_state = ESnormal;
++ 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 */
++ 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;
+
+diff -urN kernel-source-2.4.27-8/drivers/char/ds1307.c kernel-source-2.4.27-8-arm-1/drivers/char/ds1307.c
+--- kernel-source-2.4.27-8/drivers/char/ds1307.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/ds1307.c 2005-02-18 17:48:35.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++#include <linux/poll.h>
++#include <linux/i2c.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/rtc.h>
++#include <linux/string.h>
++#include <linux/miscdevice.h>
++#include <linux/proc_fs.h>
++
++#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; i<DS1307_RAM_SIZE; i++)
++ {
++ printk ("%02X ", buf[i]);
++ if( (i%8) == 7) printk ("\n");
++ }
++ printk ("\n");
++ }
++}
++
++static void
++ds1307_enable_clock( int enable)
++{
++ unsigned char buf[2], ad[1] = { 0 };
++ struct i2c_msg msgs[2] = {
++ { ds1307_i2c_client->addr , 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; i<DS1307_RAM_SIZE; i++)
++ {
++ p += sprintf(p, "%02X ", ram[i]);
++
++ if( (ram[i] < 32) || (ram[i]>126)) 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 kernel-source-2.4.27-8/drivers/char/ds1307.h kernel-source-2.4.27-8-arm-1/drivers/char/ds1307.h
+--- kernel-source-2.4.27-8/drivers/char/ds1307.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/ds1307.h 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/char/edb7211_keyb.c kernel-source-2.4.27-8-arm-1/drivers/char/edb7211_keyb.c
+--- kernel-source-2.4.27-8/drivers/char/edb7211_keyb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/edb7211_keyb.c 2005-02-18 17:48:35.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 <linux/config.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/ptrace.h>
++#include <linux/signal.h>
++#include <linux/timer.h>
++#include <linux/tqueue.h>
++#include <linux/random.h>
++#include <linux/ctype.h>
++#include <linux/init.h>
++#include <linux/kbd_ll.h>
++#include <linux/kbd_kern.h>
++#include <linux/delay.h>
++
++#include <asm/bitops.h>
++#include <asm/keyboard.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++
++#include <asm/io.h>
++#include <asm/system.h>
++
++
++/*
++ * 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 kernel-source-2.4.27-8/drivers/char/epxa_wdt.c kernel-source-2.4.27-8-arm-1/drivers/char/epxa_wdt.c
+--- kernel-source-2.4.27-8/drivers/char/epxa_wdt.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/epxa_wdt.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,178 @@
++/*
++ * Watchdog driver for the Altera Excalibur EPXA1DB
++ *
++ * (c) Copyright 2003 Krzysztof Marianski <kmarian at konin.lm.pl>
++ * Based on SA11x0 Watchdog driver by Oleg Drokin <green at crimea.edu>
++ *
++ * 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 <kmarian at konin.lm.pl>
++ *
++ * 1/08/2003 Initial release
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/reboot.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++
++#define WATCHDOG00_TYPE (volatile unsigned int*)
++#include <asm/arch/watchdog00.h>
++#include <asm/bitops.h>
++
++#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 <kmarian at konin.lm.pl>");
++MODULE_DESCRIPTION("EPXA Watchdog Timer");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/char/gc_kbmap.h kernel-source-2.4.27-8-arm-1/drivers/char/gc_kbmap.h
+--- kernel-source-2.4.27-8/drivers/char/gc_kbmap.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/gc_kbmap.h 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/char/gc_keyb.c kernel-source-2.4.27-8-arm-1/drivers/char/gc_keyb.c
+--- kernel-source-2.4.27-8/drivers/char/gc_keyb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/gc_keyb.c 2005-02-18 17:48:35.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 <tthaele at papenmeier.de>
++ * - Added Keyboard Sniffer on /dev/sio12 <minor = 12>
++ * - 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 at applieddata.net]
++ * Added smartio device driver on it
++ */
++
++/*
++ * Introduced setkeycode, ketkeycode for the GC+ by Thomas Thaele
++ * <tthaele at papenmeier.de> 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 <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/kbd_ll.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/kbd_kern.h>
++
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/keyboard.h>
++#include <linux/tqueue.h>
++#include <linux/proc_fs.h>
++#include <linux/pm.h>
++
++#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 at 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 at 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 at 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 <tthaele at papenmeier.de>";
++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<num;i++) {
++ while ((Ser4SSSR & SSSR_TNF) == 0);
++ tmp = pBuf[i];
++ Ser4SSDR = (tmp << 8);
++ }
++
++ // Throw away Echo
++ for (i=0;i<num;i++) {
++ while ((Ser4SSSR & SSSR_RNE) == 0);
++ tmp = Ser4SSDR;
++ }
++}
++
++static unchar ReadSSPByte(void)
++{
++ if (Ser4SSSR & SSSR_ROR) {
++ printk("%s() : Overrun\n", __FUNCTION__);
++ return 0;
++ }
++
++ Ser4SSDR = 0x00;
++
++ while ((Ser4SSSR & SSSR_RNE) == 0);
++
++ return ((unchar) Ser4SSDR);
++}
++
++static ulong read_SSP_response(int num)
++{
++ int i;
++ ulong ret;
++
++ // discard leading 0x00 and command echo 0 (command group value)
++ while (ReadSSPByte() == 0);
++ // discard command echo 1 (command code value)
++ ReadSSPByte();
++
++ // data from SMARTIO
++ // It assumes LSB first.
++ // NOTE:Some command uses MSB first order
++ ret = 0;
++ for (i=0;i<num;i++) {
++ ret |= ReadSSPByte() << (8*i);
++ }
++
++ return ret;
++}
++
++typedef struct t_SMARTIO_CMD {
++ unchar Group;
++ unchar Code;
++ unchar Opt[2];
++} SMARTIO_CMD;
++
++static SMARTIO_CMD RD_INT_CMD = { 0x83, 0x01, { 0x00, 0x00 } };
++static SMARTIO_CMD RD_KBD_CMD = { 0x83, 0x02, { 0x00, 0x00 } };
++static SMARTIO_CMD RD_ADC_CMD = { 0x83, 0x28, { 0x00, 0x00 } };
++static SMARTIO_CMD RD_KPD_CMD = { 0x83, 0x04, { 0x00, 0x00 } };
++
++static volatile ushort adc_value;
++static volatile unchar kpd_value;
++static unsigned int kpd_timeout = 10000; // 10000 msec
++
++static ulong kbd_int, kpd_int, adc_int;
++
++static void smartio_interrupt_task(void *data);
++
++static struct tq_struct tq_smartio = {
++ { NULL, NULL }, // struct list_head
++ 0, // unsigned long sync
++ smartio_interrupt_task, // void (*routine)(void *)
++ NULL, // void *data
++};
++
++DECLARE_WAIT_QUEUE_HEAD(smartio_queue);
++DECLARE_WAIT_QUEUE_HEAD(smartio_adc_queue);
++DECLARE_WAIT_QUEUE_HEAD(smartio_kpd_queue);
++DECLARE_WAIT_QUEUE_HEAD(keyboard_done_queue);
++DECLARE_WAIT_QUEUE_HEAD(sniffer_queue);
++
++static spinlock_t smartio_busy_lock = SPIN_LOCK_UNLOCKED;
++static atomic_t smartio_busy = ATOMIC_INIT(0);
++
++static int f_five_pressed = 0;
++static int f_seven_pressed = 0;
++//static int e_null_counter = 0;
++//static int f_null_counter = 0;
++//static int keydown = 0;
++static unchar previous_code = 0;
++//static int e0 = 0;
++
++static void smartio_interrupt_task(void *arg)
++{
++ unchar code;
++ unsigned long flags;
++ unchar dummy;
++
++ spin_lock_irqsave(&smartio_busy_lock, flags);
++ if (atomic_read(&smartio_busy) == 1) {
++ spin_unlock_irqrestore(&smartio_busy_lock, flags);
++ queue_task(&tq_smartio, &tq_timer);
++ }
++ else {
++ atomic_set(&smartio_busy, 1);
++ spin_unlock_irqrestore(&smartio_busy_lock, flags);
++ }
++
++ /* Read SMARTIO Interrupt Status to check which Interrupt is occurred
++ * and Clear SMARTIO Interrupt */
++ send_SSP_msg((unchar *) &RD_INT_CMD, 2);
++ code = (unchar) (read_SSP_response(1) & 0xFF);
++
++#ifdef CONFIG_VT
++ if (code & 0x04) { // Keyboard Interrupt
++ kbd_int++;
++ /* Read Scan code */
++ send_SSP_msg((unchar *) &RD_KBD_CMD, 2);
++ code = (unchar) (read_SSP_response(1) & 0xFF);
++ dummy = code & 0x80;
++ if ((code == 0xE0) || (code == 0xE1) || (code == 0xF0)) { // combined code
++ if (code == 0xF0) {
++ if (!previous_code) {
++ code = 0xE0;
++ previous_code = 0xF0;
++ } else {
++ code = mf_two_kbdmap[code & 0x7F] | dummy;
++ previous_code = 0;
++ }
++ } else if (code == 0xE0) {
++ if (previous_code != 0) {
++ code = mf_two_kbdmap[code & 0x7F] | dummy;
++ previous_code = 0;
++ } else previous_code = code;
++ } else { // 0xE1
++ if (!previous_code) {
++ code = mf_two_kbdmap[code &0x7F] | dummy;
++ previous_code = 0;
++ } else {
++ previous_code = code;
++ }
++ }
++ } else {
++ if (code == 0x03) {
++ f_five_pressed = 1;
++ } else if (code == 0x83) {
++ if (f_five_pressed != 0) {
++ f_five_pressed = 0;
++ code = 0x03;
++ } else if (f_seven_pressed == 0) {
++ f_seven_pressed = 1;
++ code = 2;
++ dummy = 0;
++ } else {
++ f_seven_pressed = 0;
++ code = 2;
++ }
++ }
++ previous_code = 0;
++ code &= 0x7F;
++ code = mf_two_kbdmap[code] | dummy;
++ }
++ sniffed_value = (ushort)code;
++ if (SNIFFER) wake_up_interruptible(&sniffer_queue);
++ if (SNIFFMODE == PASSIVE) {
++ handle_scancode( code, (code & 0x80) ? 0 : 1 );
++ if (code & 0x80) {
++ wake_up_interruptible(&keyboard_done_queue);
++ mdelay(10); // this makes the whole thing a bit more stable
++ // keyboard handling can be corrupted when hitting
++ // thousands of keys like crazy. kbd_translate might catch up
++ // with irq routine? or there is simply a buffer overflow on
++ // the serial device? somehow it looses some key sequences.
++ // if a break code is lost or coruppted the keyboard starts
++ // to autorepeat like crazy and appears to hang.
++ // this needs further investigations! Thomas
++ kbd_press_flag = 0;
++ }
++ else
++ kbd_press_flag = 1;
++ }
++ code = 0; // prevent furthermore if ... then to react!
++ }
++#endif
++ // ADC resolution is 10bit (0x000 ~ 0x3FF)
++ if (code & 0x02) { // ADC Complete Interrupt
++ adc_int++;
++ send_SSP_msg((unchar *) &RD_ADC_CMD, 2);
++ adc_value = (ushort) (read_SSP_response(2) & 0x3FF);
++ wake_up_interruptible(&smartio_adc_queue);
++ }
++
++ if (code & 0x08) { // Keypad interrupt
++ kpd_int++;
++ send_SSP_msg((unchar *) &RD_KPD_CMD, 2);
++ kpd_value = (unchar) (read_SSP_response(1) & 0xFF);
++ wake_up_interruptible(&smartio_kpd_queue);
++ }
++
++ spin_lock_irqsave(&smartio_busy_lock, flags);
++ atomic_set(&smartio_busy, 0);
++ spin_unlock_irqrestore(&smartio_busy_lock, flags);
++
++ enable_irq(ADS_AVR_IRQ);
++
++ wake_up_interruptible(&smartio_queue);
++}
++
++static void gc_sio_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++#ifdef CONFIG_VT
++ kbd_pt_regs = regs;
++#endif
++
++ // *NOTE*
++ // ADS SMARTIO interrupt is cleared after reading interrupt status
++ // from smartio.
++ // disable SMARTIO IRQ here and re-enable at samrtio_bh.
++ // 11/13/00 Woojung
++ disable_irq(ADS_AVR_IRQ);
++
++ queue_task(&tq_smartio, &tq_immediate);
++ mark_bh(IMMEDIATE_BH);
++}
++
++char gc_kbd_unexpected_up(unsigned char keycode)
++{
++ return 0;
++}
++
++static inline void gc_sio_init(void)
++{
++ GPDR |= (GPIO_GPIO10 | GPIO_GPIO12 | GPIO_GPIO13); // Output
++ GPDR &= ~GPIO_GPIO11;
++
++ // Alternative Function
++ GAFR |= (GPIO_GPIO10 | GPIO_GPIO11 | GPIO_GPIO12 | GPIO_GPIO13);
++
++ Ser4SSCR0 = 0xA707;
++ Ser4SSSR = SSSR_ROR;
++ Ser4SSCR1 = 0x0010;
++ Ser4SSCR0 = 0xA787;
++
++ // Reset SMARTIO
++ ADS_AVR_REG &= 0xFE;
++ mdelay(300); // 10 mSec
++ ADS_AVR_REG |= 0x01;
++ mdelay(10); // 10 mSec
++
++}
++
++void __init gc_kbd_init_hw(void)
++{
++ printk (KERN_INFO "Graphics Client keyboard driver v1.0\n");
++
++ k_setkeycode = gc_kbd_setkeycode;
++ k_getkeycode = gc_kbd_getkeycode;
++ k_translate = gc_kbd_translate;
++ k_unexpected_up = gc_kbd_unexpected_up;
++#ifdef CONFIG_MAGIC_SYSRQ
++ k_sysrq_key = 0x54;
++ /* sysrq table??? --rmk */
++#endif
++
++ gc_sio_init();
++
++ if (request_irq(ADS_AVR_IRQ,gc_sio_interrupt,0,"smartio", NULL) != 0)
++ printk("Could not allocate SMARTIO IRQ!\n");
++
++ sio_reset_flag = 1;
++}
++
++/* SMARTIO ADC Interface */
++#define SMARTIO_VERSION 0
++#define SMARTIO_PORT_A 1
++#define SMARTIO_PORT_B 2
++#define SMARTIO_PORT_C 3
++#define SMARTIO_PORT_D 4
++#define SMARTIO_SELECT_OPTION 5
++#define SMARTIO_BACKLITE 6
++#define SMARTIO_KEYPAD 7
++#define SMARTIO_ADC 8
++#define SMARTIO_VEE_PWM 9
++#define SMARTIO_SLEEP 11
++#define SMARTIO_KBD_SNIFFER 12
++
++static SMARTIO_CMD CONV_ADC_CMD = { 0x80, 0x28, { 0x00, 0x00 } };
++static SMARTIO_CMD READ_PORT_CMD = { 0x82, 0x00, { 0x00, 0x00 } };
++
++static SMARTIO_CMD READ_DEVVER_CMD = { 0x82, 0x05, { 0x00, 0x00 } };
++static SMARTIO_CMD READ_DEVTYPE_CMD = { 0x82, 0x06, { 0x00, 0x00 } };
++static SMARTIO_CMD READ_FWLEVEL_CMD = { 0x82, 0x07, { 0x00, 0x00 } };
++
++static int lock_smartio(unsigned long *flags)
++{
++ spin_lock_irqsave(&smartio_busy_lock, *flags);
++ if (atomic_read(&smartio_busy) == 1) {
++ spin_unlock_irqrestore(&smartio_busy_lock, *flags);
++ interruptible_sleep_on(&smartio_queue);
++ }
++ else {
++ atomic_set(&smartio_busy, 1);
++ spin_unlock_irqrestore(&smartio_busy_lock, *flags);
++ }
++
++ return 1;
++}
++
++static int unlock_smartio(unsigned long *flags)
++{
++ spin_lock_irqsave(&smartio_busy_lock, *flags);
++ atomic_set(&smartio_busy, 0);
++ spin_unlock_irqrestore(&smartio_busy_lock, *flags);
++
++ return 1;
++}
++
++static ushort read_sio_adc(int channel)
++{
++ unsigned long flags;
++
++ if ((channel < 0) || (channel > 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 kernel-source-2.4.27-8/drivers/char/gckeymap.c kernel-source-2.4.27-8-arm-1/drivers/char/gckeymap.c
+--- kernel-source-2.4.27-8/drivers/char/gckeymap.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/gckeymap.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,262 @@
++/* Do not edit this file! It was automatically generated by */
++/* loadkeys --mktable defkeymap.map > defkeymap.c */
++
++#include <linux/types.h>
++#include <linux/keyboard.h>
++#include <linux/kd.h>
++
++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 kernel-source-2.4.27-8/drivers/char/gckeymap.map kernel-source-2.4.27-8-arm-1/drivers/char/gckeymap.map
+--- kernel-source-2.4.27-8/drivers/char/gckeymap.map 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/gckeymap.map 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/char/generic_serial.c kernel-source-2.4.27-8-arm-1/drivers/char/generic_serial.c
+--- kernel-source-2.4.27-8/drivers/char/generic_serial.c 2005-01-19 09:57:54.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/generic_serial.c 2005-02-18 17:48:35.000000000 +0000
+@@ -876,6 +876,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;
+@@ -950,6 +953,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);
+@@ -969,6 +977,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;
+@@ -1009,7 +1022,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 kernel-source-2.4.27-8/drivers/char/generic_serial.c.orig kernel-source-2.4.27-8-arm-1/drivers/char/generic_serial.c.orig
+--- kernel-source-2.4.27-8/drivers/char/generic_serial.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/generic_serial.c.orig 2005-01-19 09:57:54.000000000 +0000
+@@ -0,0 +1,1101 @@
++/*
++ * generic_serial.c
++ *
++ * Copyright (C) 1998/1999 R.E.Wolff at BitWizard.nl
++ *
++ * written for the SX serial driver.
++ * Contains the code that should be shared over all the serial drivers.
++ *
++ * Credit for the idea to do it this way might go to Alan Cox.
++ *
++ *
++ * Version 0.1 -- December, 1998. Initial version.
++ * Version 0.2 -- March, 1999. Some more routines. Bugfixes. Etc.
++ * Version 0.5 -- August, 1999. Some more fixes. Reformat for Linus.
++ *
++ * BitWizard is actively maintaining this file. We sometimes find
++ * that someone submitted changes to this file. We really appreciate
++ * your help, but please submit changes through us. We're doing our
++ * best to be responsive. -- REW
++ * */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/tty.h>
++#include <linux/serial.h>
++#include <linux/mm.h>
++#include <linux/generic_serial.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++
++#define DEBUG
++
++static char * tmp_buf;
++static DECLARE_MUTEX(tmp_buf_sem);
++
++static int gs_debug;
++
++#ifdef DEBUG
++#define gs_dprintk(f, str...) if (gs_debug & f) printk (str)
++#else
++#define gs_dprintk(f, str...) /* nothing */
++#endif
++
++#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter %s\n", __FUNCTION__)
++#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit %s\n", __FUNCTION__)
++
++#if NEW_WRITE_LOCKING
++#define DECL /* Nothing */
++#define LOCKIT down (& port->port_write_sem);
++#define RELEASEIT up (&port->port_write_sem);
++#else
++#define DECL unsigned long flags;
++#define LOCKIT save_flags (flags);cli ()
++#define RELEASEIT restore_flags (flags)
++#endif
++
++#define RS_EVENT_WRITE_WAKEUP 1
++
++MODULE_PARM(gs_debug, "i");
++
++
++void gs_put_char(struct tty_struct * tty, unsigned char ch)
++{
++ struct gs_port *port;
++ DECL
++
++ func_enter ();
++
++ if (!tty) return;
++
++ port = tty->driver_data;
++
++ if (!port) return;
++
++ if (! (port->flags & ASYNC_INITIALIZED)) return;
++
++ /* Take a lock on the serial tranmit buffer! */
++ LOCKIT;
++
++ if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
++ /* Sorry, buffer is full, drop character. Update statistics???? -- REW */
++ RELEASEIT;
++ return;
++ }
++
++ port->xmit_buf[port->xmit_head++] = ch;
++ port->xmit_head &= SERIAL_XMIT_SIZE - 1;
++ port->xmit_cnt++; /* Characters in buffer */
++
++ RELEASEIT;
++ func_exit ();
++}
++
++
++#ifdef NEW_WRITE_LOCKING
++
++/*
++> Problems to take into account are:
++> -1- Interrupts that empty part of the buffer.
++> -2- page faults on the access to userspace.
++> -3- Other processes that are also trying to do a "write".
++*/
++
++int gs_write(struct tty_struct * tty, int from_user,
++ const unsigned char *buf, int count)
++{
++ struct gs_port *port;
++ int c, total = 0;
++ int t;
++
++ func_enter ();
++
++ if (!tty) return 0;
++
++ port = tty->driver;
++
++ if (!port) return 0;
++
++ if (! (port->flags & ASYNC_INITIALIZED))
++ return 0;
++
++ /* get exclusive "write" access to this port (problem 3) */
++ /* This is not a spinlock because we can have a disk access (page
++ fault) in copy_from_user */
++ down (& port->port_write_sem);
++
++ while (1) {
++
++ c = count;
++
++ /* This is safe because we "OWN" the "head". Noone else can
++ change the "head": we own the port_write_sem. */
++ /* Don't overrun the end of the buffer */
++ t = SERIAL_XMIT_SIZE - port->xmit_head;
++ if (t < c) c = t;
++
++ /* This is safe because the xmit_cnt can only decrease. This
++ would increase "t", so we might copy too little chars. */
++ /* Don't copy past the "head" of the buffer */
++ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
++ if (t < c) c = t;
++
++ /* Can't copy more? break out! */
++ if (c <= 0) break;
++ if (from_user)
++ if (copy_from_user (port->xmit_buf + port->xmit_head,
++ buf, c)) {
++ up (& port->port_write_sem);
++ return -EFAULT;
++ }
++
++ else
++ memcpy (port->xmit_buf + port->xmit_head, buf, c);
++
++ port -> xmit_cnt += c;
++ port -> xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE -1);
++ buf += c;
++ count -= c;
++ total += c;
++ }
++ up (& port->port_write_sem);
++
++ gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n",
++ (port->flags & GS_TX_INTEN)?"enabled": "disabled");
++
++ if (port->xmit_cnt &&
++ !tty->stopped &&
++ !tty->hw_stopped &&
++ !(port->flags & GS_TX_INTEN)) {
++ port->flags |= GS_TX_INTEN;
++ port->rd->enable_tx_interrupts (port);
++ }
++ func_exit ();
++ return total;
++}
++#else
++/*
++> Problems to take into account are:
++> -1- Interrupts that empty part of the buffer.
++> -2- page faults on the access to userspace.
++> -3- Other processes that are also trying to do a "write".
++*/
++
++int gs_write(struct tty_struct * tty, int from_user,
++ const unsigned char *buf, int count)
++{
++ struct gs_port *port;
++ int c, total = 0;
++ int t;
++ unsigned long flags;
++
++ func_enter ();
++
++ /* The standard serial driver returns 0 in this case.
++ That sounds to me as "No error, I just didn't get to writing any
++ bytes. Feel free to try again."
++ The "official" way to write n bytes from buf is:
++
++ for (nwritten = 0;nwritten < n;nwritten += rv) {
++ rv = write (fd, buf+nwritten, n-nwritten);
++ if (rv < 0) break; // Error: bail out. //
++ }
++
++ which will loop endlessly in this case. The manual page for write
++ agrees with me. In practise almost everybody writes
++ "write (fd, buf,n);" but some people might have had to deal with
++ incomplete writes in the past and correctly implemented it by now...
++ */
++
++ if (!tty) return -EIO;
++
++ port = tty->driver_data;
++ if (!port || !port->xmit_buf || !tmp_buf)
++ return -EIO;
++
++ save_flags(flags);
++ if (from_user) {
++ down(&tmp_buf_sem);
++ while (1) {
++ c = count;
++
++ /* Note: This part can be done without
++ * interrupt routine protection since
++ * the interrupt routines may only modify
++ * shared variables in safe ways, in the worst
++ * case causing us to loop twice in the code
++ * below. See comments below. */
++
++ /* Don't overrun the end of the buffer */
++ t = SERIAL_XMIT_SIZE - port->xmit_head;
++ if (t < c) c = t;
++
++ /* This is safe because the xmit_cnt can only decrease. This
++ would increase "t", so we might copy too little chars. */
++ /* Don't copy past the "head" of the buffer */
++ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
++ if (t < c) c = t;
++
++ /* Can't copy more? break out! */
++ if (c <= 0) break;
++
++ c -= copy_from_user(tmp_buf, buf, c);
++ if (!c) {
++ if (!total)
++ total = -EFAULT;
++ break;
++ }
++ cli();
++ t = SERIAL_XMIT_SIZE - port->xmit_head;
++ if (t < c) c = t;
++ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
++ if (t < c) c = t;
++
++ memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);
++ port->xmit_head = ((port->xmit_head + c) &
++ (SERIAL_XMIT_SIZE-1));
++ port->xmit_cnt += c;
++ restore_flags(flags);
++ buf += c;
++ count -= c;
++ total += c;
++ }
++ up(&tmp_buf_sem);
++ } else {
++ while (1) {
++ cli();
++ c = count;
++
++ /* This is safe because we "OWN" the "head". Noone else can
++ change the "head": we own the port_write_sem. */
++ /* Don't overrun the end of the buffer */
++ t = SERIAL_XMIT_SIZE - port->xmit_head;
++ if (t < c) c = t;
++
++ /* This is safe because the xmit_cnt can only decrease. This
++ would increase "t", so we might copy too little chars. */
++ /* Don't copy past the "head" of the buffer */
++ t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;
++ if (t < c) c = t;
++
++ /* Can't copy more? break out! */
++ if (c <= 0) {
++ restore_flags(flags);
++ break;
++ }
++ memcpy(port->xmit_buf + port->xmit_head, buf, c);
++ port->xmit_head = ((port->xmit_head + c) &
++ (SERIAL_XMIT_SIZE-1));
++ port->xmit_cnt += c;
++ restore_flags(flags);
++ buf += c;
++ count -= c;
++ total += c;
++ }
++ }
++
++ if (port->xmit_cnt &&
++ !tty->stopped &&
++ !tty->hw_stopped &&
++ !(port->flags & GS_TX_INTEN)) {
++ port->flags |= GS_TX_INTEN;
++ port->rd->enable_tx_interrupts (port);
++ }
++ func_exit ();
++ return total;
++}
++
++#endif
++
++
++
++int gs_write_room(struct tty_struct * tty)
++{
++ struct gs_port *port = tty->driver_data;
++ int ret;
++
++ func_enter ();
++ ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
++ if (ret < 0)
++ ret = 0;
++ func_exit ();
++ return ret;
++}
++
++
++int gs_chars_in_buffer(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ func_enter ();
++
++ func_exit ();
++ return port->xmit_cnt;
++}
++
++
++int gs_real_chars_in_buffer(struct tty_struct *tty)
++{
++ struct gs_port *port;
++ func_enter ();
++
++ if (!tty) return 0;
++ port = tty->driver_data;
++
++ if (!port->rd) return 0;
++ if (!port->rd->chars_in_buffer) return 0;
++
++ func_exit ();
++ return port->xmit_cnt + port->rd->chars_in_buffer (port);
++}
++
++
++static int gs_wait_tx_flushed (void * ptr, int timeout)
++{
++ struct gs_port *port = ptr;
++ long end_jiffies;
++ int jiffies_to_transmit, charsleft = 0, rv = 0;
++ int rcib;
++
++ func_enter();
++
++ gs_dprintk (GS_DEBUG_FLUSH, "port=%p.\n", port);
++ if (port) {
++ gs_dprintk (GS_DEBUG_FLUSH, "xmit_cnt=%x, xmit_buf=%p, tty=%p.\n",
++ port->xmit_cnt, port->xmit_buf, port->tty);
++ }
++
++ if (!port || port->xmit_cnt < 0 || !port->xmit_buf) {
++ gs_dprintk (GS_DEBUG_FLUSH, "ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0.\n");
++ func_exit();
++ return -EINVAL; /* This is an error which we don't know how to handle. */
++ }
++
++ rcib = gs_real_chars_in_buffer(port->tty);
++
++ if(rcib <= 0) {
++ gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n");
++ func_exit();
++ return rv;
++ }
++ /* stop trying: now + twice the time it would normally take + seconds */
++ if (timeout == 0) timeout = MAX_SCHEDULE_TIMEOUT;
++ end_jiffies = jiffies;
++ if (timeout != MAX_SCHEDULE_TIMEOUT)
++ end_jiffies += port->baud?(2 * rcib * 10 * HZ / port->baud):0;
++ end_jiffies += timeout;
++
++ gs_dprintk (GS_DEBUG_FLUSH, "now=%lx, end=%lx (%ld).\n",
++ jiffies, end_jiffies, end_jiffies-jiffies);
++
++ /* the expression is actually jiffies < end_jiffies, but that won't
++ work around the wraparound. Tricky eh? */
++ while ((charsleft = gs_real_chars_in_buffer (port->tty)) &&
++ time_after (end_jiffies, jiffies)) {
++ /* Units check:
++ chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies!
++ check! */
++
++ charsleft += 16; /* Allow 16 chars more to be transmitted ... */
++ jiffies_to_transmit = port->baud?(1 + charsleft * 10 * HZ / port->baud):0;
++ /* ^^^ Round up.... */
++ if (jiffies_to_transmit <= 0) jiffies_to_transmit = 1;
++
++ gs_dprintk (GS_DEBUG_FLUSH, "Expect to finish in %d jiffies "
++ "(%d chars).\n", jiffies_to_transmit, charsleft);
++
++ set_current_state (TASK_INTERRUPTIBLE);
++ schedule_timeout(jiffies_to_transmit);
++ if (signal_pending (current)) {
++ gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: ");
++ rv = -EINTR;
++ break;
++ }
++ }
++
++ gs_dprintk (GS_DEBUG_FLUSH, "charsleft = %d.\n", charsleft);
++ set_current_state (TASK_RUNNING);
++
++ func_exit();
++ return rv;
++}
++
++
++
++void gs_flush_buffer(struct tty_struct *tty)
++{
++ struct gs_port *port;
++ unsigned long flags;
++
++ func_enter ();
++
++ if (!tty) return;
++
++ port = tty->driver_data;
++
++ if (!port) return;
++
++ /* XXX Would the write semaphore do? */
++ save_flags(flags); cli();
++ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
++ restore_flags(flags);
++
++ tty_wakeup(tty);
++ func_exit ();
++}
++
++
++void gs_flush_chars(struct tty_struct * tty)
++{
++ struct gs_port *port;
++
++ func_enter ();
++
++ if (!tty) return;
++
++ port = tty->driver_data;
++
++ if (!port) return;
++
++ if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
++ !port->xmit_buf) {
++ func_exit ();
++ return;
++ }
++
++ /* Beats me -- REW */
++ port->flags |= GS_TX_INTEN;
++ port->rd->enable_tx_interrupts (port);
++ func_exit ();
++}
++
++
++void gs_stop(struct tty_struct * tty)
++{
++ struct gs_port *port;
++
++ func_enter ();
++
++ if (!tty) return;
++
++ port = tty->driver_data;
++
++ if (!port) return;
++
++ if (port->xmit_cnt &&
++ port->xmit_buf &&
++ (port->flags & GS_TX_INTEN) ) {
++ port->flags &= ~GS_TX_INTEN;
++ port->rd->disable_tx_interrupts (port);
++ }
++ func_exit ();
++}
++
++
++void gs_start(struct tty_struct * tty)
++{
++ struct gs_port *port;
++
++ if (!tty) return;
++
++ port = tty->driver_data;
++
++ if (!port) return;
++
++ if (port->xmit_cnt &&
++ port->xmit_buf &&
++ !(port->flags & GS_TX_INTEN) ) {
++ port->flags |= GS_TX_INTEN;
++ port->rd->enable_tx_interrupts (port);
++ }
++ func_exit ();
++}
++
++
++void gs_shutdown_port (struct gs_port *port)
++{
++ unsigned long flags;
++
++ func_enter();
++
++ if (!port) return;
++
++ if (!(port->flags & ASYNC_INITIALIZED))
++ return;
++
++ save_flags (flags);
++ cli ();
++
++ if (port->xmit_buf) {
++ free_page((unsigned long) port->xmit_buf);
++ port->xmit_buf = 0;
++ }
++
++ if (port->tty)
++ set_bit(TTY_IO_ERROR, &port->tty->flags);
++
++ port->rd->shutdown_port (port);
++
++ port->flags &= ~ASYNC_INITIALIZED;
++ restore_flags (flags);
++
++ func_exit();
++}
++
++
++void gs_hangup(struct tty_struct *tty)
++{
++ struct gs_port *port;
++
++ func_enter ();
++
++ if (!tty) return;
++
++ port = tty->driver_data;
++ tty = port->tty;
++ if (!tty)
++ return;
++
++ gs_shutdown_port (port);
++ port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE |GS_ACTIVE);
++ port->tty = NULL;
++ port->count = 0;
++
++ wake_up_interruptible(&port->open_wait);
++ func_exit ();
++}
++
++
++void gs_do_softint(void *private_)
++{
++ struct gs_port *port = private_;
++ struct tty_struct *tty;
++
++ func_enter ();
++
++ if (!port) return;
++
++ tty = port->tty;
++
++ if (!tty) return;
++
++ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
++ tty_wakeup(tty);
++ }
++ func_exit ();
++}
++
++
++int gs_block_til_ready(void *port_, struct file * filp)
++{
++ struct gs_port *port = port_;
++ DECLARE_WAITQUEUE(wait, current);
++ int retval;
++ int do_clocal = 0;
++ int CD;
++ struct tty_struct *tty;
++ unsigned long flags;
++
++ func_enter ();
++
++ if (!port) return 0;
++
++ tty = port->tty;
++
++ if (!tty) return 0;
++
++ gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n");
++ /*
++ * 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) || port->flags & ASYNC_CLOSING) {
++ interruptible_sleep_on(&port->close_wait);
++ if (port->flags & ASYNC_HUP_NOTIFY)
++ return -EAGAIN;
++ else
++ return -ERESTARTSYS;
++ }
++
++ gs_dprintk (GS_DEBUG_BTR, "after hung up\n");
++
++ /*
++ * If this is a callout device, then just make sure the normal
++ * device isn't being used.
++ */
++ if (tty->driver.subtype == GS_TYPE_CALLOUT) {
++ if (port->flags & ASYNC_NORMAL_ACTIVE)
++ return -EBUSY;
++ if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
++ (port->flags & ASYNC_SESSION_LOCKOUT) &&
++ (port->session != current->session))
++ return -EBUSY;
++ if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
++ (port->flags & ASYNC_PGRP_LOCKOUT) &&
++ (port->pgrp != current->pgrp))
++ return -EBUSY;
++ port->flags |= ASYNC_CALLOUT_ACTIVE;
++ return 0;
++ }
++
++ gs_dprintk (GS_DEBUG_BTR, "after subtype\n");
++
++ /*
++ * 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 (port->flags & ASYNC_CALLOUT_ACTIVE)
++ return -EBUSY;
++ port->flags |= ASYNC_NORMAL_ACTIVE;
++ return 0;
++ }
++
++ gs_dprintk (GS_DEBUG_BTR, "after nonblock\n");
++
++ if (port->flags & ASYNC_CALLOUT_ACTIVE) {
++ if (port->normal_termios.c_cflag & CLOCAL)
++ do_clocal = 1;
++ } else {
++ if (C_CLOCAL(tty))
++ 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, port->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(&port->open_wait, &wait);
++
++ gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n");
++ save_flags(flags);
++ cli();
++ if (!tty_hung_up_p(filp))
++ port->count--;
++ restore_flags(flags);
++ port->blocked_open++;
++ while (1) {
++ CD = port->rd->get_CD (port);
++ gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD);
++ set_current_state (TASK_INTERRUPTIBLE);
++ if (tty_hung_up_p(filp) ||
++ !(port->flags & ASYNC_INITIALIZED)) {
++ if (port->flags & ASYNC_HUP_NOTIFY)
++ retval = -EAGAIN;
++ else
++ retval = -ERESTARTSYS;
++ break;
++ }
++ if (!(port->flags & ASYNC_CALLOUT_ACTIVE) &&
++ !(port->flags & ASYNC_CLOSING) &&
++ (do_clocal || CD))
++ break;
++ gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n",
++ (int)signal_pending (current), *(long*)(¤t->blocked));
++ if (signal_pending(current)) {
++ retval = -ERESTARTSYS;
++ break;
++ }
++ schedule();
++ }
++ gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n",
++ port->blocked_open);
++ set_current_state (TASK_RUNNING);
++ remove_wait_queue(&port->open_wait, &wait);
++ if (!tty_hung_up_p(filp))
++ port->count++;
++ port->blocked_open--;
++ if (retval)
++ return retval;
++
++ port->flags |= ASYNC_NORMAL_ACTIVE;
++ func_exit ();
++ return 0;
++}
++
++
++void gs_close(struct tty_struct * tty, struct file * filp)
++{
++ unsigned long flags;
++ struct gs_port *port;
++
++ func_enter();
++
++ if (!tty) return;
++
++ port = (struct gs_port *) tty->driver_data;
++
++ if (!port) return;
++
++ if (!port->tty) {
++ /* This seems to happen when this is called from vhangup. */
++ gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->tty is NULL\n");
++ port->tty = tty;
++ }
++
++ save_flags(flags); cli();
++
++ if (tty_hung_up_p(filp)) {
++ restore_flags(flags);
++ port->rd->hungup (port);
++ func_exit ();
++ return;
++ }
++
++ if ((tty->count == 1) && (port->count != 1)) {
++ printk(KERN_ERR "gs: gs_close: bad port count;"
++ " tty->count is 1, port count is %d\n", port->count);
++ port->count = 1;
++ }
++ if (--port->count < 0) {
++ printk(KERN_ERR "gs: gs_close: bad port count: %d\n", port->count);
++ port->count = 0;
++ }
++ if (port->count) {
++ gs_dprintk(GS_DEBUG_CLOSE, "gs_close: count: %d\n", port->count);
++ restore_flags(flags);
++ func_exit ();
++ return;
++ }
++ port->flags |= ASYNC_CLOSING;
++
++ /*
++ * Save the termios structure, since this port may have
++ * separate termios for callout and dialin.
++ */
++ if (port->flags & ASYNC_NORMAL_ACTIVE)
++ port->normal_termios = *tty->termios;
++ if (port->flags & ASYNC_CALLOUT_ACTIVE)
++ port->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 (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
++ tty_wait_until_sent(tty, port->closing_wait); */
++
++ /*
++ * At this point we stop accepting input. To do this, we
++ * disable the receive line status interrupts, and tell the
++ * interrupt driver to stop checking the data ready bit in the
++ * line status register.
++ */
++
++ port->rd->disable_rx_interrupts (port);
++
++ /* close has no way of returning "EINTR", so discard return value */
++ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
++ gs_wait_tx_flushed (port, port->closing_wait);
++
++ port->flags &= ~GS_ACTIVE;
++
++ if (tty->driver.flush_buffer)
++ tty->driver.flush_buffer(tty);
++ tty_ldisc_flush(tty);
++ tty->closing = 0;
++
++ port->event = 0;
++ port->rd->close (port);
++ port->rd->shutdown_port (port);
++ port->tty = 0;
++
++ if (port->blocked_open) {
++ if (port->close_delay) {
++ set_current_state (TASK_INTERRUPTIBLE);
++ schedule_timeout(port->close_delay);
++ }
++ wake_up_interruptible(&port->open_wait);
++ }
++ port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
++ ASYNC_CLOSING | ASYNC_INITIALIZED);
++ wake_up_interruptible(&port->close_wait);
++
++ restore_flags(flags);
++ func_exit ();
++}
++
++
++static unsigned int gs_baudrates[] = {
++ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
++ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
++};
++
++
++void gs_set_termios (struct tty_struct * tty,
++ struct termios * old_termios)
++{
++ struct gs_port *port;
++ int baudrate, tmp, rv;
++ struct termios *tiosp;
++
++ func_enter();
++
++ if (!tty) return;
++
++ port = tty->driver_data;
++
++ if (!port) return;
++
++ tiosp = tty->termios;
++
++ if (gs_debug & GS_DEBUG_TERMIOS) {
++ gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp);
++ }
++
++#if 0
++ /* This is an optimization that is only allowed for dumb cards */
++ /* Smart cards require knowledge of iflags and oflags too: that
++ might change hardware cooking mode.... */
++#endif
++ if (old_termios) {
++ if( (tiosp->c_iflag == old_termios->c_iflag)
++ && (tiosp->c_oflag == old_termios->c_oflag)
++ && (tiosp->c_cflag == old_termios->c_cflag)
++ && (tiosp->c_lflag == old_termios->c_lflag)
++ && (tiosp->c_line == old_termios->c_line)
++ && (memcmp(tiosp->c_cc, old_termios->c_cc, NCC) == 0)) {
++ gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: optimized away\n");
++ return /* 0 */;
++ }
++ } else
++ gs_dprintk(GS_DEBUG_TERMIOS, "gs_set_termios: no old_termios: "
++ "no optimization\n");
++
++ if(old_termios && (gs_debug & GS_DEBUG_TERMIOS)) {
++ if(tiosp->c_iflag != old_termios->c_iflag) printk("c_iflag changed\n");
++ if(tiosp->c_oflag != old_termios->c_oflag) printk("c_oflag changed\n");
++ if(tiosp->c_cflag != old_termios->c_cflag) printk("c_cflag changed\n");
++ if(tiosp->c_lflag != old_termios->c_lflag) printk("c_lflag changed\n");
++ if(tiosp->c_line != old_termios->c_line) printk("c_line changed\n");
++ if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n");
++ }
++
++ baudrate = tiosp->c_cflag & CBAUD;
++ if (baudrate & CBAUDEX) {
++ baudrate &= ~CBAUDEX;
++ if ((baudrate < 1) || (baudrate > 4))
++ tiosp->c_cflag &= ~CBAUDEX;
++ else
++ baudrate += 15;
++ }
++
++ baudrate = gs_baudrates[baudrate];
++ if ((tiosp->c_cflag & CBAUD) == B38400) {
++ if ( (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
++ baudrate = 57600;
++ else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
++ baudrate = 115200;
++ else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
++ baudrate = 230400;
++ else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
++ baudrate = 460800;
++ else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
++ baudrate = (port->baud_base / port->custom_divisor);
++ }
++
++ /* I recommend using THIS instead of the mess in termios (and
++ duplicating the above code). Next we should create a clean
++ interface towards this variable. If your card supports arbitrary
++ baud rates, (e.g. CD1400 or 16550 based cards) then everything
++ will be very easy..... */
++ port->baud = baudrate;
++
++ /* Two timer ticks seems enough to wakeup something like SLIP driver */
++ /* Baudrate/10 is cps. Divide by HZ to get chars per tick. */
++ tmp = (baudrate / 10 / HZ) * 2;
++
++ if (tmp < 0) tmp = 0;
++ if (tmp >= SERIAL_XMIT_SIZE) tmp = SERIAL_XMIT_SIZE-1;
++
++ port->wakeup_chars = tmp;
++
++ /* We should really wait for the characters to be all sent before
++ changing the settings. -- CAL */
++ rv = gs_wait_tx_flushed (port, MAX_SCHEDULE_TIMEOUT);
++ if (rv < 0) return /* rv */;
++
++ rv = port->rd->set_real_termios(port);
++ if (rv < 0) return /* rv */;
++
++ if ((!old_termios ||
++ (old_termios->c_cflag & CRTSCTS)) &&
++ !( tiosp->c_cflag & CRTSCTS)) {
++ tty->stopped = 0;
++ gs_start(tty);
++ }
++
++#ifdef tytso_patch_94Nov25_1726
++ /* This "makes sense", Why is it commented out? */
++
++ if (!(old_termios->c_cflag & CLOCAL) &&
++ (tty->termios->c_cflag & CLOCAL))
++ wake_up_interruptible(&info->open_wait);
++#endif
++
++ func_exit();
++ return /* 0 */;
++}
++
++
++
++/* Must be called with interrupts enabled */
++int gs_init_port(struct gs_port *port)
++{
++ unsigned long flags;
++ unsigned long page;
++
++ save_flags (flags);
++ if (!tmp_buf) {
++ page = get_free_page(GFP_KERNEL);
++
++ cli (); /* Don't expect this to make a difference. */
++ if (tmp_buf)
++ free_page(page);
++ else
++ tmp_buf = (unsigned char *) page;
++ restore_flags (flags);
++
++ if (!tmp_buf) {
++ return -ENOMEM;
++ }
++ }
++
++ if (port->flags & ASYNC_INITIALIZED)
++ return 0;
++
++ if (!port->xmit_buf) {
++ /* We may sleep in get_free_page() */
++ unsigned long tmp;
++
++ tmp = get_free_page(GFP_KERNEL);
++
++ /* Spinlock? */
++ cli ();
++ if (port->xmit_buf)
++ free_page (tmp);
++ else
++ port->xmit_buf = (unsigned char *) tmp;
++ restore_flags (flags);
++
++ if (!port->xmit_buf)
++ return -ENOMEM;
++ }
++
++ cli();
++
++ if (port->tty)
++ clear_bit(TTY_IO_ERROR, &port->tty->flags);
++
++ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
++
++ gs_set_termios(port->tty, NULL);
++
++ port->flags |= ASYNC_INITIALIZED;
++ port->flags &= ~GS_TX_INTEN;
++
++ restore_flags(flags);
++ return 0;
++}
++
++
++int gs_setserial(struct gs_port *port, struct serial_struct *sp)
++{
++ struct serial_struct sio;
++
++ if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
++ return(-EFAULT);
++
++ if (!capable(CAP_SYS_ADMIN)) {
++ if ((sio.baud_base != port->baud_base) ||
++ (sio.close_delay != port->close_delay) ||
++ ((sio.flags & ~ASYNC_USR_MASK) !=
++ (port->flags & ~ASYNC_USR_MASK)))
++ return(-EPERM);
++ }
++
++ port->flags = (port->flags & ~ASYNC_USR_MASK) |
++ (sio.flags & ASYNC_USR_MASK);
++
++ port->baud_base = sio.baud_base;
++ port->close_delay = sio.close_delay;
++ port->closing_wait = sio.closing_wait;
++ port->custom_divisor = sio.custom_divisor;
++
++ gs_set_termios (port->tty, NULL);
++
++ return 0;
++}
++
++
++/*****************************************************************************/
++
++/*
++ * Generate the serial struct info.
++ */
++
++int gs_getserial(struct gs_port *port, struct serial_struct *sp)
++{
++ struct serial_struct sio;
++
++ memset(&sio, 0, sizeof(struct serial_struct));
++ sio.flags = port->flags;
++ sio.baud_base = port->baud_base;
++ sio.close_delay = port->close_delay;
++ sio.closing_wait = port->closing_wait;
++ sio.custom_divisor = port->custom_divisor;
++ sio.hub6 = 0;
++
++ /* If you want you can override these. */
++ sio.type = PORT_UNKNOWN;
++ sio.xmit_fifo_size = -1;
++ sio.line = -1;
++ sio.port = -1;
++ sio.irq = -1;
++
++ if (port->rd->getserial)
++ port->rd->getserial (port, &sio);
++
++ if (copy_to_user(sp, &sio, sizeof(struct serial_struct)))
++ return -EFAULT;
++ return 0;
++
++}
++
++
++void gs_got_break(struct gs_port *port)
++{
++ if (port->flags & ASYNC_SAK) {
++ do_SAK (port->tty);
++ }
++ *(port->tty->flip.flag_buf_ptr) = TTY_BREAK;
++ port->tty->flip.flag_buf_ptr++;
++ port->tty->flip.char_buf_ptr++;
++ port->tty->flip.count++;
++}
++
++
++EXPORT_SYMBOL(gs_put_char);
++EXPORT_SYMBOL(gs_write);
++EXPORT_SYMBOL(gs_write_room);
++EXPORT_SYMBOL(gs_chars_in_buffer);
++EXPORT_SYMBOL(gs_flush_buffer);
++EXPORT_SYMBOL(gs_flush_chars);
++EXPORT_SYMBOL(gs_stop);
++EXPORT_SYMBOL(gs_start);
++EXPORT_SYMBOL(gs_hangup);
++EXPORT_SYMBOL(gs_do_softint);
++EXPORT_SYMBOL(gs_block_til_ready);
++EXPORT_SYMBOL(gs_close);
++EXPORT_SYMBOL(gs_set_termios);
++EXPORT_SYMBOL(gs_init_port);
++EXPORT_SYMBOL(gs_setserial);
++EXPORT_SYMBOL(gs_getserial);
++EXPORT_SYMBOL(gs_got_break);
++
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/char/keyboard.c kernel-source-2.4.27-8-arm-1/drivers/char/keyboard.c
+--- kernel-source-2.4.27-8/drivers/char/keyboard.c 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/keyboard.c 2005-02-18 17:48:35.000000000 +0000
+@@ -21,6 +21,10 @@
+ *
+ * 27-05-97: Added support for the Magic SysRq Key (Martin Mares)
+ * 30-07-98: Dead keys redone, aeb at 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 at arm.linux.org.uk)
+ */
+
+ #include <linux/config.h>
+@@ -30,6 +34,7 @@
+ #include <linux/tty_flip.h>
+ #include <linux/mm.h>
+ #include <linux/string.h>
++#include <linux/timer.h>
+ #include <linux/random.h>
+ #include <linux/init.h>
+
+@@ -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);
+@@ -91,12 +104,16 @@
+ 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) {
+@@ -193,6 +211,13 @@
+ 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;
+@@ -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)
+@@ -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;
+diff -urN kernel-source-2.4.27-8/drivers/char/mem.c kernel-source-2.4.27-8-arm-1/drivers/char/mem.c
+--- kernel-source-2.4.27-8/drivers/char/mem.c 2004-08-08 00:26:04.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/mem.c 2005-02-18 17:48:35.000000000 +0000
+@@ -27,9 +27,6 @@
+ #include <asm/io.h>
+ #include <asm/pgalloc.h>
+
+-#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 kernel-source-2.4.27-8/drivers/char/omaha-rtc.c kernel-source-2.4.27-8-arm-1/drivers/char/omaha-rtc.c
+--- kernel-source-2.4.27-8/drivers/char/omaha-rtc.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/omaha-rtc.c 2005-02-18 17:48:35.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 <nico at cam.org>
++ * - 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 <cih at coventive.com>
++ * - Modify the bug setups RTC clock.
++ *
++ * 0.02 2001-02-27 Nils Faerber <nils@@kernelconcepts.de>
++ * - removed mktime(), added alarm irq clear
++ *
++ * 0.01 2000-10-01 Nils Faerber <nils@@kernelconcepts.de>
++ * - initial release
++ */
++
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++#include <asm/bitops.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <linux/rtc.h>
++#include <linux/mc146818rtc.h>
++
++#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 <support at arm.com>");
++MODULE_DESCRIPTION("Omaha Realtime Clock Driver (RTC)");
++EXPORT_NO_SYMBOLS;
+diff -urN kernel-source-2.4.27-8/drivers/char/omaha_wdt.c kernel-source-2.4.27-8-arm-1/drivers/char/omaha_wdt.c
+--- kernel-source-2.4.27-8/drivers/char/omaha_wdt.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/omaha_wdt.c 2005-02-18 17:48:35.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 <green at crimea.edu>
++ * Based on SoftDog driver by Alan Cox <alan at redhat.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.
++ *
++ * 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 <green at crimea.edu>
++ *
++ * 27/11/2000 Initial release
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/reboot.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/bitops.h>
++
++#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 kernel-source-2.4.27-8/drivers/char/pcmcia/Config.in kernel-source-2.4.27-8-arm-1/drivers/char/pcmcia/Config.in
+--- kernel-source-2.4.27-8/drivers/char/pcmcia/Config.in 2002-11-28 23:53:12.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/pcmcia/Config.in 2005-02-18 17:48:35.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 kernel-source-2.4.27-8/drivers/char/pcmcia/memory_cs.c kernel-source-2.4.27-8-arm-1/drivers/char/pcmcia/memory_cs.c
+--- kernel-source-2.4.27-8/drivers/char/pcmcia/memory_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/pcmcia/memory_cs.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,214 @@
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++
++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 kernel-source-2.4.27-8/drivers/char/s3c2410-wdt.c kernel-source-2.4.27-8-arm-1/drivers/char/s3c2410-wdt.c
+--- kernel-source-2.4.27-8/drivers/char/s3c2410-wdt.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/s3c2410-wdt.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,241 @@
++/*
++ * Watchdog driver for the S3C2410
++ *
++ * (c) Copyright 2003 Simtec Electronics <ben at simtec.co.uk>
++ * based on SA1100 watchdog timer by Oleg Drokin
++ *
++ * (c) Copyright 2000 Oleg Drokin <green at crimea.edu>
++ * Based on SoftDog driver by Alan Cox <alan at redhat.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.
++ *
++ * 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 <green at crimea.edu>
++ *
++ * 27/11/2000 Initial release
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/reboot.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/bitops.h>
++
++#include <asm/io.h>
++#include <asm/hardware.h>
++
++#include <asm/arch-s3c2410/S3C2410-gpio.h>
++#include <asm/arch-s3c2410/S3C2410-watchdog.h>
++
++/* our watchdog has a 16bit counter, and a pre-scaler that can take
++ * the pclk and feed it to the counter
++ *
++ * we try to get the divisor so that at least 8bits of the counter
++ * are available for the seconds counter.... ie, pclk/div < 256
++ */
++
++
++#define TIMER_MARGIN 20 /* (secs) Default is 20secs */
++
++static int s3c2410_margin = TIMER_MARGIN; /* in seconds */
++
++static int s3c2410_wdt_users;
++
++static int s3c2410_wtclk = 0; /* clock rate supplied to counter */
++
++#ifdef MODULE
++MODULE_PARM(s3c2410_margin,"i");
++#endif
++
++static void s3c2410dog_load(void)
++{
++ unsigned long tmp;
++
++ tmp = s3c2410_wtclk * s3c2410_margin;
++
++ if (tmp >= (1<<16)) {
++ printk(KERN_ERR ": watchdog: count to high (%08lx)\n", tmp);
++ tmp = (1<<16)-1;
++ }
++
++ __raw_writel(tmp, S3C2410_WTCNT);
++ __raw_writel(tmp, S3C2410_WTDAT);
++}
++
++/*
++ * Allow only one person to hold it open
++ */
++
++static int s3c2410dog_open(struct inode *inode, struct file *file)
++{
++ unsigned long tmp;
++
++ if(test_and_set_bit(1,&s3c2410_wdt_users))
++ return -EBUSY;
++
++ MOD_INC_USE_COUNT;
++
++ /* activate the watchdog timer */
++
++ s3c2410dog_load();
++
++ tmp = __raw_readl(S3C2410_WTCON);
++ tmp |= S3C2410_WTCON_RSTEN|S3C2410_WTCON_ENABLE;
++ __raw_writel(tmp, S3C2410_WTCON);
++
++ /* here we go! */
++
++ return 0;
++}
++
++static int s3c2410dog_release(struct inode *inode, struct file *file)
++{
++ unsigned long tmp;
++
++ /*
++ * Shut off the timer.
++ * Lock it in if it's a module and we defined ...NOWAYOUT
++ */
++
++ tmp = __raw_readl(S3C2410_WTCON);
++ tmp &= ~(S3C2410_WTCON_RSTEN|S3C2410_WTCON_ENABLE);
++ __raw_writel(tmp, S3C2410_WTCON);
++
++ s3c2410_wdt_users = 0;
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static ssize_t s3c2410dog_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 the watchdog timer */
++ if(len) {
++ s3c2410dog_load();
++ return 1;
++ }
++ return 0;
++}
++
++static struct watchdog_info ident = {
++ identity: "S3C2410 Watchdog",
++};
++
++static int s3c2410dog_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ unsigned long tmp;
++
++ 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:
++ tmp = __raw_readl(S3C2410_GSTATUS2);
++ return put_user((tmp & S3C2410_GSTATUS2_WTRESET ) ? WDIOF_CARDRESET : 0, (int *)arg);
++ case WDIOC_KEEPALIVE:
++ s3c2410dog_load();
++ return 0;
++ }
++}
++
++static struct file_operations s3c2410dog_fops=
++{
++ owner: THIS_MODULE,
++ write: s3c2410dog_write,
++ ioctl: s3c2410dog_ioctl,
++ open: s3c2410dog_open,
++ release: s3c2410dog_release,
++};
++
++static struct miscdevice s3c2410dog_miscdev=
++{
++ WATCHDOG_MINOR,
++ "S3C2410 watchdog",
++ &s3c2410dog_fops
++};
++
++static int __init s3c2410dog_init(void)
++{
++ int ret;
++ int wtcon = 0;
++ int div;
++
++ s3c2410_wtclk = s3c2410_pclk; /* start with clk = pclk */
++
++ /* sort out the first fixed divide of ps3c2410_wtclk */
++ if (s3c2410_wtclk > (256*64*128)) {
++ wtcon |= S3C2410_WTCON_DIV128;
++ s3c2410_wtclk /= 128;
++ } else if (s3c2410_wtclk > (256*64*64)) {
++ wtcon |= S3C2410_WTCON_DIV64;
++ s3c2410_wtclk /= 64;
++ } else if (s3c2410_wtclk > (256*64*32)) {
++ wtcon |= S3C2410_WTCON_DIV32;
++ s3c2410_wtclk /= 32;
++ } else {
++ wtcon |= S3C2410_WTCON_DIV16;
++ s3c2410_wtclk /= 16;
++ }
++
++ printk(KERN_DEBUG __FUNCTION__ "wtcon=%08x, s3c2410_wtclk=%d\n",
++ wtcon, s3c2410_wtclk);
++
++ /* now we sort out the value of our pre-scaler...
++ * _wtclk / div = output, wtclk = div * output, wtclk / output = div */
++
++ div = s3c2410_wtclk / 256; /* work out a reasonable divisor... */
++ div--;
++
++ if (div < 0)
++ div = 0;
++ else if (div > 255)
++ div = 255;
++
++ wtcon |= S3C2410_WTCON_PRESCALE(div);
++ s3c2410_wtclk /= div + 1;
++
++ printk(KERN_DEBUG __FUNCTION__ "wtcon=%08x, wtclk=%d, div=%d\n",
++ wtcon, s3c2410_wtclk, div);
++
++ __raw_writel(wtcon, S3C2410_WTCON);
++
++ ret = misc_register(&s3c2410dog_miscdev);
++
++ if (ret)
++ return ret;
++
++ printk("S3C2410 Watchdog Timer: timer margin %d sec\n", s3c2410_margin);
++
++ return 0;
++}
++
++static void __exit s3c2410dog_exit(void)
++{
++ misc_deregister(&s3c2410dog_miscdev);
++}
++
++module_init(s3c2410dog_init);
++module_exit(s3c2410dog_exit);
+diff -urN kernel-source-2.4.27-8/drivers/char/sa1100-rtc.c kernel-source-2.4.27-8-arm-1/drivers/char/sa1100-rtc.c
+--- kernel-source-2.4.27-8/drivers/char/sa1100-rtc.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/sa1100-rtc.c 2005-02-18 17:48:35.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 <nico at cam.org>
++ * - 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 <cih at coventive.com>
++ * - Modify the bug setups RTC clock.
++ *
++ * 0.02 2001-02-27 Nils Faerber <nils@@kernelconcepts.de>
++ * - removed mktime(), added alarm irq clear
++ *
++ * 0.01 2000-10-01 Nils Faerber <nils@@kernelconcepts.de>
++ * - initial release
++ */
++
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++#include <asm/bitops.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <linux/rtc.h>
++
++#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 <nils@@kernelconcepts.de>");
++MODULE_DESCRIPTION("SA1100 Realtime Clock Driver (RTC)");
++EXPORT_NO_SYMBOLS;
+diff -urN kernel-source-2.4.27-8/drivers/char/sa1100_wdt.c kernel-source-2.4.27-8-arm-1/drivers/char/sa1100_wdt.c
+--- kernel-source-2.4.27-8/drivers/char/sa1100_wdt.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/sa1100_wdt.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,150 @@
++/*
++ * Watchdog driver for the SA11x0
++ *
++ * (c) Copyright 2000 Oleg Drokin <green at crimea.edu>
++ * Based on SoftDog driver by Alan Cox <alan at redhat.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.
++ *
++ * 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 <green at crimea.edu>
++ *
++ * 27/11/2000 Initial release
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/reboot.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/bitops.h>
++
++#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 kernel-source-2.4.27-8/drivers/char/sa1111_keyb.c kernel-source-2.4.27-8-arm-1/drivers/char/sa1111_keyb.c
+--- kernel-source-2.4.27-8/drivers/char/sa1111_keyb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/sa1111_keyb.c 2005-02-18 17:48:35.000000000 +0000
+@@ -0,0 +1,1153 @@
++/*
++ * SA1111 PS/2 keyboard/mouse driver
++ *
++ * 2000 by VASARA RESEARCH INC.
++ *
++ * Changelog:
++ * Jun.xx,2000: Kunihiko IMAI <imai at vasara.co.jp>
++ * Port to 2.4.0test1-ac19-rmk1-np1
++ * Apr.17,2000: Takafumi Kawana <kawana at pro.or.jp>
++ * 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 <jem at iki.fi> 1998-10-08.
++ *
++ * Code fixes to handle mouse ACKs properly.
++ * C. Scott Ananian <cananian at alumni.princeton.edu> 1999-01-29.
++ *
++ */
++#include <linux/config.h>
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/mm.h>
++#include <linux/signal.h>
++#include <linux/init.h>
++#include <linux/kbd_ll.h>
++#include <linux/delay.h>
++#include <linux/random.h>
++#include <linux/poll.h>
++#include <linux/miscdevice.h>
++#include <linux/slab.h>
++#include <linux/kbd_kern.h>
++#include <linux/ioport.h>
++
++#include <asm/hardware.h>
++#include <asm/bitops.h>
++#include <asm/uaccess.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++
++#include <asm/io.h>
++
++/* Some configuration switches are present in the include file... */
++
++#include <linux/pc_keyb.h>
++#include <asm/keyboard.h>
++#include <asm/hardware/sa1111.h>
++
++#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 at 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 at 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 at 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 kernel-source-2.4.27-8/drivers/char/serial.c kernel-source-2.4.27-8-arm-1/drivers/char/serial.c
+--- kernel-source-2.4.27-8/drivers/char/serial.c 2005-01-19 09:57:54.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/serial.c 2005-02-18 17:48:35.000000000 +0000
+@@ -4521,6 +4521,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 kernel-source-2.4.27-8/drivers/char/serial.c.orig kernel-source-2.4.27-8-arm-1/drivers/char/serial.c.orig
+--- kernel-source-2.4.27-8/drivers/char/serial.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/serial.c.orig 2005-01-19 09:57:54.000000000 +0000
+@@ -0,0 +1,6031 @@
++/*
++ * linux/drivers/char/serial.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997,
++ * 1998, 1999 Theodore Ts'o
++ *
++ * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now
++ * much more extensible to support other serial cards based on the
++ * 16450/16550A UART's. Added support for the AST FourPort and the
++ * Accent Async board.
++ *
++ * set_serial_info fixed to set the flags, custom divisor, and uart
++ * type fields. Fix suggested by Michael K. Johnson 12/12/92.
++ *
++ * 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah at doc.ic.ac.uk>
++ *
++ * 03/96: Modularised by Angelo Haritsis <ah at doc.ic.ac.uk>
++ *
++ * rs_set_termios fixed to look also for changes of the input
++ * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
++ * Bernd Anhäupl 05/17/96.
++ *
++ * 1/97: Extended dumb serial ports are a config option now.
++ * Saves 4k. Michael A. Griffith <grif at acm.org>
++ *
++ * 8/97: Fix bug in rs_set_termios with RTS
++ * Stanislav V. Voronyi <stas at uanet.kharkov.ua>
++ *
++ * 3/98: Change the IRQ detection, use of probe_irq_o*(),
++ * suppress TIOCSERGWILD and TIOCSERSWILD
++ * Etienne Lorrain <etienne.lorrain at ibm.net>
++ *
++ * 4/98: Added changes to support the ARM architecture proposed by
++ * Russell King
++ *
++ * 5/99: Updated to include support for the XR16C850 and ST16C654
++ * uarts. Stuart MacDonald <stuartm at connecttech.com>
++ *
++ * 8/99: Generalized PCI support added. Theodore Ts'o
++ *
++ * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a
++ * few races on freeing buffers too.
++ * Alan Modra <alan at linuxcare.com>
++ *
++ * 5/00: Support for the RSA-DV II/S card added.
++ * Kiyokazu SUTO <suto at ks-and-ks.ne.jp>
++ *
++ * 6/00: Remove old-style timer, use timer_list
++ * Andrew Morton <andrewm at uow.edu.au>
++ *
++ * 7/00: Support Timedia/Sunix/Exsys PCI cards
++ *
++ * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT.
++ * Arnaldo Carvalho de Melo <acme at conectiva.com.br>
++ *
++ * 10/00: add in optional software flow control for serial console.
++ * Kanoj Sarcar <kanoj at sgi.com> (Modified by Theodore Ts'o)
++ *
++ * 02/02: Fix for AMD Elan bug in transmit irq routine, by
++ * Christer Weinigel <wingel at hog.ctrl-c.liu.se>,
++ * Robert Schwebel <robert at schwebel.de>,
++ * Juergen Beisert <jbeisert at eurodsn.de>,
++ * Theodore Ts'o <tytso at mit.edu>
++ */
++
++static char *serial_version = "5.05c";
++static char *serial_revdate = "2001-07-08";
++
++/*
++ * Serial driver configuration section. Here are the various options:
++ *
++ * CONFIG_HUB6
++ * Enables support for the venerable Bell Technologies
++ * HUB6 card.
++ *
++ * CONFIG_SERIAL_MANY_PORTS
++ * Enables support for ports beyond the standard, stupid
++ * COM 1/2/3/4.
++ *
++ * CONFIG_SERIAL_MULTIPORT
++ * Enables support for special multiport board support.
++ *
++ * CONFIG_SERIAL_SHARE_IRQ
++ * Enables support for multiple serial ports on one IRQ
++ *
++ * CONFIG_SERIAL_DETECT_IRQ
++ * Enable the autodetection of IRQ on standart ports
++ *
++ * SERIAL_PARANOIA_CHECK
++ * Check the magic number for the async_structure where
++ * ever possible.
++ *
++ * CONFIG_SERIAL_ACPI
++ * Enable support for serial console port and serial
++ * debug port as defined by the SPCR and DBGP tables in
++ * ACPI 2.0.
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++
++#undef SERIAL_PARANOIA_CHECK
++#define CONFIG_SERIAL_NOPAUSE_IO
++#define SERIAL_DO_RESTART
++
++#if 0
++/* These defines are normally controlled by the autoconf.h */
++#define CONFIG_SERIAL_MANY_PORTS
++#define CONFIG_SERIAL_SHARE_IRQ
++#define CONFIG_SERIAL_DETECT_IRQ
++#define CONFIG_SERIAL_MULTIPORT
++#define CONFIG_HUB6
++#endif
++
++#ifdef CONFIG_PCI
++#define ENABLE_SERIAL_PCI
++#ifndef CONFIG_SERIAL_SHARE_IRQ
++#define CONFIG_SERIAL_SHARE_IRQ
++#endif
++#ifndef CONFIG_SERIAL_MANY_PORTS
++#define CONFIG_SERIAL_MANY_PORTS
++#endif
++#endif
++
++#ifdef CONFIG_SERIAL_ACPI
++#define ENABLE_SERIAL_ACPI
++#endif
++
++#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE))
++#ifndef ENABLE_SERIAL_PNP
++#define ENABLE_SERIAL_PNP
++#endif
++#endif
++
++/* Set of debugging defines */
++
++#undef SERIAL_DEBUG_INTR
++#undef SERIAL_DEBUG_OPEN
++#undef SERIAL_DEBUG_FLOW
++#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
++#undef SERIAL_DEBUG_PCI
++#undef SERIAL_DEBUG_AUTOCONF
++
++/* Sanity checks */
++
++#ifdef CONFIG_SERIAL_MULTIPORT
++#ifndef CONFIG_SERIAL_SHARE_IRQ
++#define CONFIG_SERIAL_SHARE_IRQ
++#endif
++#endif
++
++#ifdef CONFIG_HUB6
++#ifndef CONFIG_SERIAL_MANY_PORTS
++#define CONFIG_SERIAL_MANY_PORTS
++#endif
++#ifndef CONFIG_SERIAL_SHARE_IRQ
++#define CONFIG_SERIAL_SHARE_IRQ
++#endif
++#endif
++
++#ifdef MODULE
++#undef CONFIG_SERIAL_CONSOLE
++#endif
++
++#define CONFIG_SERIAL_RSA
++
++#define RS_STROBE_TIME (10*HZ)
++#define RS_ISR_PASS_LIMIT 256
++
++#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
++#define SERIAL_INLINE
++#endif
++
++/*
++ * End of serial driver configuration section.
++ */
++
++#include <linux/module.h>
++
++#include <linux/types.h>
++#ifdef LOCAL_HEADERS
++#include "serial_local.h"
++#else
++#include <linux/serial.h>
++#include <linux/serialP.h>
++#include <linux/serial_reg.h>
++#include <asm/serial.h>
++#define LOCAL_VERSTRING ""
++#endif
++
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#if (LINUX_VERSION_CODE >= 131343)
++#include <linux/init.h>
++#endif
++#if (LINUX_VERSION_CODE >= 131336)
++#include <asm/uaccess.h>
++#endif
++#include <linux/delay.h>
++#ifdef CONFIG_SERIAL_CONSOLE
++#include <linux/console.h>
++#endif
++#ifdef ENABLE_SERIAL_PCI
++#include <linux/pci.h>
++#endif
++#ifdef ENABLE_SERIAL_PNP
++#include <linux/isapnp.h>
++#endif
++#ifdef CONFIG_MAGIC_SYSRQ
++#include <linux/sysrq.h>
++#endif
++
++/*
++ * All of the compatibilty code so we can compile serial.c against
++ * older kernels is hidden in serial_compat.h
++ */
++#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */
++#include "serial_compat.h"
++#endif
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/bitops.h>
++
++#if defined(CONFIG_MAC_SERIAL)
++#define SERIAL_DEV_OFFSET ((_machine == _MACH_prep || _machine == _MACH_chrp) ? 0 : 2)
++#else
++#define SERIAL_DEV_OFFSET 0
++#endif
++
++#ifdef SERIAL_INLINE
++#define _INLINE_ inline
++#else
++#define _INLINE_
++#endif
++
++static char *serial_name = "Serial driver";
++
++static DECLARE_TASK_QUEUE(tq_serial);
++
++static struct tty_driver serial_driver, callout_driver;
++static int serial_refcount;
++
++static struct timer_list serial_timer;
++
++/* serial subtype definitions */
++#ifndef SERIAL_TYPE_NORMAL
++#define SERIAL_TYPE_NORMAL 1
++#define SERIAL_TYPE_CALLOUT 2
++#endif
++
++/* number of characters left in xmit buffer before we ask for more */
++#define WAKEUP_CHARS 256
++
++/*
++ * IRQ_timeout - How long the timeout should be for each IRQ
++ * should be after the IRQ has been active.
++ */
++
++static struct async_struct *IRQ_ports[NR_IRQS];
++#ifdef CONFIG_SERIAL_MULTIPORT
++static struct rs_multiport_struct rs_multiport[NR_IRQS];
++#endif
++static int IRQ_timeout[NR_IRQS];
++#ifdef CONFIG_SERIAL_CONSOLE
++static struct console sercons;
++static int lsr_break_flag;
++#endif
++#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++static unsigned long break_pressed; /* break, really ... */
++#endif
++
++static unsigned detect_uart_irq (struct serial_state * state);
++static void autoconfig(struct serial_state * state);
++static void change_speed(struct async_struct *info, struct termios *old);
++static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
++
++/*
++ * Here we define the default xmit fifo size used for each type of
++ * UART
++ */
++static struct serial_uart_config uart_config[] = {
++ { "unknown", 1, 0 },
++ { "8250", 1, 0 },
++ { "16450", 1, 0 },
++ { "16550", 1, 0 },
++ { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
++ { "cirrus", 1, 0 }, /* usurped by cyclades.c */
++ { "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}, /* usurped by cyclades.c */
++ { "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 },
++ { 0, 0}
++};
++
++#if defined(CONFIG_SERIAL_RSA) && defined(MODULE)
++
++#define PORT_RSA_MAX 4
++static int probe_rsa[PORT_RSA_MAX];
++static int force_rsa[PORT_RSA_MAX];
++
++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_RSA */
++
++struct serial_state rs_table[RS_TABLE_SIZE] = {
++ SERIAL_PORT_DFNS /* Defined in serial.h */
++};
++
++#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))
++int serial_nr_ports = NR_PORTS;
++
++#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP))
++#define NR_PCI_BOARDS 8
++
++static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS];
++
++#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
++#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */
++
++#ifndef PREPARE_FUNC
++#define PREPARE_FUNC(dev) (dev->prepare)
++#define ACTIVATE_FUNC(dev) (dev->activate)
++#define DEACTIVATE_FUNC(dev) (dev->deactivate)
++#endif
++
++#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
++
++static struct tty_struct *serial_table[NR_PORTS];
++static struct termios *serial_termios[NR_PORTS];
++static struct termios *serial_termios_locked[NR_PORTS];
++
++
++#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
++#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
++ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
++#else
++#define DBG_CNT(s)
++#endif
++
++/*
++ * 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 unsigned char *tmp_buf;
++#ifdef DECLARE_MUTEX
++static DECLARE_MUTEX(tmp_buf_sem);
++#else
++static struct semaphore tmp_buf_sem = MUTEX;
++#endif
++
++
++static inline int serial_paranoia_check(struct async_struct *info,
++ kdev_t device, const char *routine)
++{
++#ifdef SERIAL_PARANOIA_CHECK
++ static const char *badmagic =
++ "Warning: bad magic number for serial struct (%s) in %s\n";
++ static const char *badinfo =
++ "Warning: null async_struct for (%s) in %s\n";
++
++ if (!info) {
++ printk(badinfo, kdevname(device), routine);
++ return 1;
++ }
++ if (info->magic != SERIAL_MAGIC) {
++ printk(badmagic, kdevname(device), routine);
++ return 1;
++ }
++#endif
++ return 0;
++}
++
++static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset)
++{
++ switch (info->io_type) {
++#ifdef CONFIG_HUB6
++ case SERIAL_IO_HUB6:
++ outb(info->hub6 - 1 + offset, info->port);
++ return inb(info->port+1);
++#endif
++ case SERIAL_IO_MEM:
++ return readb((unsigned long) info->iomem_base +
++ (offset<<info->iomem_reg_shift));
++ default:
++ return inb(info->port + offset);
++ }
++}
++
++static _INLINE_ void serial_out(struct async_struct *info, int offset,
++ int value)
++{
++ switch (info->io_type) {
++#ifdef CONFIG_HUB6
++ case SERIAL_IO_HUB6:
++ outb(info->hub6 - 1 + offset, info->port);
++ outb(value, info->port+1);
++ break;
++#endif
++ case SERIAL_IO_MEM:
++ writeb(value, (unsigned long) info->iomem_base +
++ (offset<<info->iomem_reg_shift));
++ break;
++ default:
++ outb(value, info->port+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(info, offset) serial_in(info, offset)
++#define serial_outp(info, offset, value) serial_out(info, offset, value)
++
++
++/*
++ * For the 16C950
++ */
++void serial_icr_write(struct async_struct *info, int offset, int value)
++{
++ serial_out(info, UART_SCR, offset);
++ serial_out(info, UART_ICR, value);
++}
++
++unsigned int serial_icr_read(struct async_struct *info, int offset)
++{
++ int value;
++
++ serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD);
++ serial_out(info, UART_SCR, offset);
++ value = serial_in(info, UART_ICR);
++ serial_icr_write(info, UART_ACR, info->ACR);
++ return value;
++}
++
++/*
++ * ------------------------------------------------------------
++ * rs_stop() and rs_start()
++ *
++ * This routines are called before setting or resetting tty->stopped.
++ * They enable or disable transmitter interrupts, as necessary.
++ * ------------------------------------------------------------
++ */
++static void rs_stop(struct tty_struct *tty)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++
++ if (serial_paranoia_check(info, tty->device, "rs_stop"))
++ return;
++
++ save_flags(flags); cli();
++ if (info->IER & UART_IER_THRI) {
++ info->IER &= ~UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ }
++ if (info->state->type == PORT_16C950) {
++ info->ACR |= UART_ACR_TXDIS;
++ serial_icr_write(info, UART_ACR, info->ACR);
++ }
++ restore_flags(flags);
++}
++
++static void rs_start(struct tty_struct *tty)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++
++ if (serial_paranoia_check(info, tty->device, "rs_start"))
++ return;
++
++ save_flags(flags); cli();
++ if (info->xmit.head != info->xmit.tail
++ && info->xmit.buf
++ && !(info->IER & UART_IER_THRI)) {
++ info->IER |= UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ }
++ if (info->state->type == PORT_16C950) {
++ info->ACR &= ~UART_ACR_TXDIS;
++ serial_icr_write(info, UART_ACR, info->ACR);
++ }
++ restore_flags(flags);
++}
++
++/*
++ * ----------------------------------------------------------------------
++ *
++ * Here starts the interrupt handling routines. All of the following
++ * subroutines are declared as inline and are folded into
++ * rs_interrupt(). They were separated out for readability's sake.
++ *
++ * Note: rs_interrupt() is a "fast" interrupt, which means that it
++ * runs with interrupts turned off. People who may want to modify
++ * rs_interrupt() should try to keep the interrupt handler as fast as
++ * possible. After you are done making modifications, it is not a bad
++ * idea to do:
++ *
++ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
++ *
++ * and look at the resulting assemble code in serial.s.
++ *
++ * - Ted Ts'o (tytso at mit.edu), 7-Mar-93
++ * -----------------------------------------------------------------------
++ */
++
++/*
++ * This routine is used by the interrupt handler to schedule
++ * processing in the software interrupt portion of the driver.
++ */
++static _INLINE_ void rs_sched_event(struct async_struct *info,
++ int event)
++{
++ info->event |= 1 << event;
++ queue_task(&info->tqueue, &tq_serial);
++ mark_bh(SERIAL_BH);
++}
++
++static _INLINE_ void receive_chars(struct async_struct *info,
++ int *status, struct pt_regs * regs)
++{
++ struct tty_struct *tty = info->tty;
++ unsigned char ch;
++ struct async_icount *icount;
++ int max_count = 256;
++
++ icount = &info->state->icount;
++ do {
++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
++ tty->flip.tqueue.routine((void *) tty);
++ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++ return; // if TTY_DONT_FLIP is set
++ }
++ ch = serial_inp(info, UART_RX);
++ *tty->flip.char_buf_ptr = ch;
++ icount->rx++;
++
++#ifdef SERIAL_DEBUG_INTR
++ printk("DR%02x:%02x...", ch, *status);
++#endif
++ *tty->flip.flag_buf_ptr = 0;
++ 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);
++ 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 defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++ if (info->line == sercons.index) {
++ if (!break_pressed) {
++ break_pressed = jiffies;
++ goto ignore_char;
++ }
++ break_pressed = 0;
++ }
++#endif
++ if (info->flags & ASYNC_SAK)
++ do_SAK(tty);
++ } else if (*status & UART_LSR_PE)
++ icount->parity++;
++ else if (*status & UART_LSR_FE)
++ icount->frame++;
++ if (*status & UART_LSR_OE)
++ icount->overrun++;
++
++ /*
++ * Mask off conditions which should be ignored.
++ */
++ *status &= info->read_status_mask;
++
++#ifdef CONFIG_SERIAL_CONSOLE
++ if (info->line == sercons.index) {
++ /* Recover the break flag from console xmit */
++ *status |= lsr_break_flag;
++ lsr_break_flag = 0;
++ }
++#endif
++ if (*status & (UART_LSR_BI)) {
++#ifdef SERIAL_DEBUG_INTR
++ printk("handling break....");
++#endif
++ *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 defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++ if (break_pressed && info->line == sercons.index) {
++ if (ch != 0 &&
++ time_before(jiffies, break_pressed + HZ*5)) {
++ handle_sysrq(ch, regs, NULL, NULL);
++ break_pressed = 0;
++ goto ignore_char;
++ }
++ break_pressed = 0;
++ }
++#endif
++ if ((*status & info->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.count++;
++ tty->flip.flag_buf_ptr++;
++ tty->flip.char_buf_ptr++;
++ }
++#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++ ignore_char:
++#endif
++ *status = serial_inp(info, UART_LSR);
++ } while ((*status & UART_LSR_DR) && (max_count-- > 0));
++#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */
++ tty_flip_buffer_push(tty);
++#else
++ queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
++#endif
++}
++
++static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
++{
++ int count;
++
++ if (info->x_char) {
++ serial_outp(info, UART_TX, info->x_char);
++ info->state->icount.tx++;
++ info->x_char = 0;
++ if (intr_done)
++ *intr_done = 0;
++ return;
++ }
++ if (info->xmit.head == info->xmit.tail
++ || info->tty->stopped
++ || info->tty->hw_stopped) {
++ info->IER &= ~UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ return;
++ }
++
++ count = info->xmit_fifo_size;
++ do {
++ serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]);
++ info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_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,
++ SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
++ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
++
++#ifdef SERIAL_DEBUG_INTR
++ printk("THRE...");
++#endif
++ if (intr_done)
++ *intr_done = 0;
++
++ if (info->xmit.head == info->xmit.tail) {
++ info->IER &= ~UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ }
++}
++
++static _INLINE_ void check_modem_status(struct async_struct *info)
++{
++ int status;
++ struct async_icount *icount;
++
++ status = serial_in(info, UART_MSR);
++
++ if (status & UART_MSR_ANY_DELTA) {
++ icount = &info->state->icount;
++ /* update input line counters */
++ if (status & UART_MSR_TERI)
++ icount->rng++;
++ if (status & UART_MSR_DDSR)
++ icount->dsr++;
++ if (status & UART_MSR_DDCD) {
++ icount->dcd++;
++#ifdef CONFIG_HARD_PPS
++ if ((info->flags & ASYNC_HARDPPS_CD) &&
++ (status & UART_MSR_DCD))
++ hardpps();
++#endif
++ }
++ if (status & UART_MSR_DCTS)
++ icount->cts++;
++ wake_up_interruptible(&info->delta_msr_wait);
++ }
++
++ if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
++#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
++ printk("ttys%d CD now %s...", info->line,
++ (status & UART_MSR_DCD) ? "on" : "off");
++#endif
++ if (status & UART_MSR_DCD)
++ wake_up_interruptible(&info->open_wait);
++ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
++ (info->flags & ASYNC_CALLOUT_NOHUP))) {
++#ifdef SERIAL_DEBUG_OPEN
++ printk("doing serial hangup...");
++#endif
++ if (info->tty)
++ tty_hangup(info->tty);
++ }
++ }
++ if (info->flags & ASYNC_CTS_FLOW) {
++ if (info->tty->hw_stopped) {
++ if (status & UART_MSR_CTS) {
++#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
++ printk("CTS tx start...");
++#endif
++ info->tty->hw_stopped = 0;
++ info->IER |= UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
++ return;
++ }
++ } else {
++ if (!(status & UART_MSR_CTS)) {
++#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
++ printk("CTS tx stop...");
++#endif
++ info->tty->hw_stopped = 1;
++ info->IER &= ~UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ }
++ }
++ }
++}
++
++#ifdef CONFIG_SERIAL_SHARE_IRQ
++/*
++ * This is the serial driver's generic interrupt routine
++ */
++static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
++{
++ int status, iir;
++ struct async_struct * info;
++ int pass_counter = 0;
++ struct async_struct *end_mark = 0;
++#ifdef CONFIG_SERIAL_MULTIPORT
++ int first_multi = 0;
++ struct rs_multiport_struct *multi;
++#endif
++
++#ifdef SERIAL_DEBUG_INTR
++ printk("rs_interrupt(%d)...", irq);
++#endif
++
++ info = IRQ_ports[irq];
++ if (!info)
++ return;
++
++#ifdef CONFIG_SERIAL_MULTIPORT
++ multi = &rs_multiport[irq];
++ if (multi->port_monitor)
++ first_multi = inb(multi->port_monitor);
++#endif
++
++ do {
++ if (!info->tty ||
++ ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT)) {
++ if (!end_mark)
++ end_mark = info;
++ goto next;
++ }
++#ifdef SERIAL_DEBUG_INTR
++ printk("IIR = %x...", serial_in(info, UART_IIR));
++#endif
++ end_mark = 0;
++
++ info->last_active = jiffies;
++
++ status = serial_inp(info, UART_LSR);
++#ifdef SERIAL_DEBUG_INTR
++ printk("status = %x...", status);
++#endif
++ if (status & UART_LSR_DR)
++ receive_chars(info, &status, regs);
++ check_modem_status(info);
++#ifdef CONFIG_MELAN
++ if ((status & UART_LSR_THRE) ||
++ /* for buggy ELAN processors */
++ ((iir & UART_IIR_ID) == UART_IIR_THRI))
++ transmit_chars(info, 0);
++#else
++ if (status & UART_LSR_THRE)
++ transmit_chars(info, 0);
++#endif
++
++ next:
++ info = info->next_port;
++ if (!info) {
++ info = IRQ_ports[irq];
++ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
++#if 0
++ printk("rs loop break\n");
++#endif
++ break; /* Prevent infinite loops */
++ }
++ continue;
++ }
++ } while (end_mark != info);
++#ifdef CONFIG_SERIAL_MULTIPORT
++ if (multi->port_monitor)
++ printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
++ info->state->irq, first_multi,
++ inb(multi->port_monitor));
++#endif
++#ifdef SERIAL_DEBUG_INTR
++ printk("end.\n");
++#endif
++}
++#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */
++
++
++/*
++ * This is the serial driver's interrupt routine for a single port
++ */
++static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
++{
++ int status, iir;
++ int pass_counter = 0;
++ struct async_struct * info;
++#ifdef CONFIG_SERIAL_MULTIPORT
++ int first_multi = 0;
++ struct rs_multiport_struct *multi;
++#endif
++
++#ifdef SERIAL_DEBUG_INTR
++ printk("rs_interrupt_single(%d)...", irq);
++#endif
++
++ info = IRQ_ports[irq];
++ if (!info || !info->tty)
++ return;
++
++#ifdef CONFIG_SERIAL_MULTIPORT
++ multi = &rs_multiport[irq];
++ if (multi->port_monitor)
++ first_multi = inb(multi->port_monitor);
++#endif
++
++ iir = serial_in(info, UART_IIR);
++ do {
++ status = serial_inp(info, UART_LSR);
++#ifdef SERIAL_DEBUG_INTR
++ printk("status = %x...", status);
++#endif
++ if (status & UART_LSR_DR)
++ receive_chars(info, &status, regs);
++ check_modem_status(info);
++#ifdef CONFIG_MELAN
++ if ((status & UART_LSR_THRE) ||
++ /* For buggy ELAN processors */
++ ((iir & UART_IIR_ID) == UART_IIR_THRI))
++ transmit_chars(info, 0);
++#else
++ if (status & UART_LSR_THRE)
++ transmit_chars(info, 0);
++#endif
++ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
++#if SERIAL_DEBUG_INTR
++ printk("rs_single loop break.\n");
++#endif
++ break;
++ }
++ iir = serial_in(info, UART_IIR);
++#ifdef SERIAL_DEBUG_INTR
++ printk("IIR = %x...", iir);
++#endif
++ } while ((iir & UART_IIR_NO_INT) == 0);
++ info->last_active = jiffies;
++#ifdef CONFIG_SERIAL_MULTIPORT
++ if (multi->port_monitor)
++ printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
++ info->state->irq, first_multi,
++ inb(multi->port_monitor));
++#endif
++#ifdef SERIAL_DEBUG_INTR
++ printk("end.\n");
++#endif
++}
++
++#ifdef CONFIG_SERIAL_MULTIPORT
++/*
++ * This is the serial driver's for multiport boards
++ */
++static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs)
++{
++ int status;
++ struct async_struct * info;
++ int pass_counter = 0;
++ int first_multi= 0;
++ struct rs_multiport_struct *multi;
++
++#ifdef SERIAL_DEBUG_INTR
++ printk("rs_interrupt_multi(%d)...", irq);
++#endif
++
++ info = IRQ_ports[irq];
++ if (!info)
++ return;
++ multi = &rs_multiport[irq];
++ if (!multi->port1) {
++ /* Should never happen */
++ printk("rs_interrupt_multi: NULL port1!\n");
++ return;
++ }
++ if (multi->port_monitor)
++ first_multi = inb(multi->port_monitor);
++
++ while (1) {
++ if (!info->tty ||
++ (serial_in(info, UART_IIR) & UART_IIR_NO_INT))
++ goto next;
++
++ info->last_active = jiffies;
++
++ status = serial_inp(info, UART_LSR);
++#ifdef SERIAL_DEBUG_INTR
++ printk("status = %x...", status);
++#endif
++ if (status & UART_LSR_DR)
++ receive_chars(info, &status, regs);
++ check_modem_status(info);
++ if (status & UART_LSR_THRE)
++ transmit_chars(info, 0);
++
++ next:
++ info = info->next_port;
++ if (info)
++ continue;
++
++ info = IRQ_ports[irq];
++ /*
++ * The user was a bonehead, and misconfigured their
++ * multiport info. Rather than lock up the kernel
++ * in an infinite loop, if we loop too many times,
++ * print a message and break out of the loop.
++ */
++ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
++ printk("Misconfigured multiport serial info "
++ "for irq %d. Breaking out irq loop\n", irq);
++ break;
++ }
++ if (multi->port_monitor)
++ printk("rs port monitor irq %d: 0x%x, 0x%x\n",
++ info->state->irq, first_multi,
++ inb(multi->port_monitor));
++ if ((inb(multi->port1) & multi->mask1) != multi->match1)
++ continue;
++ if (!multi->port2)
++ break;
++ if ((inb(multi->port2) & multi->mask2) != multi->match2)
++ continue;
++ if (!multi->port3)
++ break;
++ if ((inb(multi->port3) & multi->mask3) != multi->match3)
++ continue;
++ if (!multi->port4)
++ break;
++ if ((inb(multi->port4) & multi->mask4) != multi->match4)
++ continue;
++ break;
++ }
++#ifdef SERIAL_DEBUG_INTR
++ printk("end.\n");
++#endif
++}
++#endif
++
++/*
++ * -------------------------------------------------------------------
++ * Here ends the serial interrupt routines.
++ * -------------------------------------------------------------------
++ */
++
++/*
++ * This routine is used to handle the "bottom half" processing for the
++ * serial driver, known also the "software interrupt" processing.
++ * This processing is done at the kernel interrupt level, after the
++ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
++ * is where time-consuming activities which can not be done in the
++ * interrupt driver proper are done; the interrupt driver schedules
++ * them using rs_sched_event(), and they get done here.
++ */
++static void do_serial_bh(void)
++{
++ run_task_queue(&tq_serial);
++}
++
++static void do_softint(void *private_)
++{
++ struct async_struct *info = (struct async_struct *) private_;
++ struct tty_struct *tty;
++
++ tty = info->tty;
++ if (!tty)
++ return;
++
++ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
++ tty_wakeup(tty);
++
++#ifdef SERIAL_HAVE_POLL_WAIT
++ wake_up_interruptible(&tty->poll_wait);
++#endif
++ }
++}
++
++/*
++ * This subroutine is called when the RS_TIMER goes off. It is used
++ * by the serial driver to handle ports that do not have an interrupt
++ * (irq=0). 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 rs_timer(unsigned long dummy)
++{
++ static unsigned long last_strobe;
++ struct async_struct *info;
++ unsigned int i;
++ unsigned long flags;
++
++ if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
++ for (i=0; i < NR_IRQS; i++) {
++ info = IRQ_ports[i];
++ if (!info)
++ continue;
++ save_flags(flags); cli();
++#ifdef CONFIG_SERIAL_SHARE_IRQ
++ if (info->next_port) {
++ do {
++ serial_out(info, UART_IER, 0);
++ info->IER |= UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ info = info->next_port;
++ } while (info);
++#ifdef CONFIG_SERIAL_MULTIPORT
++ if (rs_multiport[i].port1)
++ rs_interrupt_multi(i, NULL, NULL);
++ else
++#endif
++ rs_interrupt(i, NULL, NULL);
++ } else
++#endif /* CONFIG_SERIAL_SHARE_IRQ */
++ rs_interrupt_single(i, NULL, NULL);
++ restore_flags(flags);
++ }
++ }
++ last_strobe = jiffies;
++ mod_timer(&serial_timer, jiffies + RS_STROBE_TIME);
++
++ if (IRQ_ports[0]) {
++ save_flags(flags); cli();
++#ifdef CONFIG_SERIAL_SHARE_IRQ
++ rs_interrupt(0, NULL, NULL);
++#else
++ rs_interrupt_single(0, NULL, NULL);
++#endif
++ restore_flags(flags);
++
++ mod_timer(&serial_timer, jiffies + IRQ_timeout[0]);
++ }
++}
++
++/*
++ * ---------------------------------------------------------------
++ * Low level utility subroutines for the serial driver: routines to
++ * figure out the appropriate timeout for an interrupt chain, routines
++ * to initialize and startup a serial port, and routines to shutdown a
++ * serial port. Useful stuff like that.
++ * ---------------------------------------------------------------
++ */
++
++/*
++ * This routine figures out the correct timeout for a particular IRQ.
++ * It uses the smallest timeout of all of the serial ports in a
++ * particular interrupt chain. Now only used for IRQ 0....
++ */
++static void figure_IRQ_timeout(int irq)
++{
++ struct async_struct *info;
++ int timeout = 60*HZ; /* 60 seconds === a long time :-) */
++
++ info = IRQ_ports[irq];
++ if (!info) {
++ IRQ_timeout[irq] = 60*HZ;
++ return;
++ }
++ while (info) {
++ if (info->timeout < timeout)
++ timeout = info->timeout;
++ info = info->next_port;
++ }
++ if (!irq)
++ timeout = timeout / 2;
++ IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1;
++}
++
++#ifdef CONFIG_SERIAL_RSA
++/* Attempts to turn on the RSA FIFO. Returns zero on failure */
++static int enable_rsa(struct async_struct *info)
++{
++ unsigned char mode;
++ int result;
++ unsigned long flags;
++
++ save_flags(flags); cli();
++ mode = serial_inp(info, UART_RSA_MSR);
++ result = mode & UART_RSA_MSR_FIFO;
++
++ if (!result) {
++ serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
++ mode = serial_inp(info, UART_RSA_MSR);
++ result = mode & UART_RSA_MSR_FIFO;
++ }
++
++ restore_flags(flags);
++ return result;
++}
++
++/* Attempts to turn off the RSA FIFO. Returns zero on failure */
++static int disable_rsa(struct async_struct *info)
++{
++ unsigned char mode;
++ int result;
++ unsigned long flags;
++
++ save_flags(flags); cli();
++ mode = serial_inp(info, UART_RSA_MSR);
++ result = !(mode & UART_RSA_MSR_FIFO);
++
++ if (!result) {
++ serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
++ mode = serial_inp(info, UART_RSA_MSR);
++ result = !(mode & UART_RSA_MSR_FIFO);
++ }
++
++ restore_flags(flags);
++ return result;
++}
++#endif /* CONFIG_SERIAL_RSA */
++
++static int startup(struct async_struct * info)
++{
++ unsigned long flags;
++ int retval=0;
++ void (*handler)(int, void *, struct pt_regs *);
++ struct serial_state *state= info->state;
++ unsigned long page;
++#ifdef CONFIG_SERIAL_MANY_PORTS
++ unsigned short ICP;
++#endif
++
++ 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 (!CONFIGURED_SERIAL_PORT(state) || !state->type) {
++ if (info->tty)
++ set_bit(TTY_IO_ERROR, &info->tty->flags);
++ free_page(page);
++ goto errout;
++ }
++ if (info->xmit.buf)
++ free_page(page);
++ else
++ info->xmit.buf = (unsigned char *) page;
++
++#ifdef SERIAL_DEBUG_OPEN
++ printk("starting up ttys%d (irq %d)...", info->line, state->irq);
++#endif
++
++ if (uart_config[state->type].flags & UART_STARTECH) {
++ /* Wake up UART */
++ serial_outp(info, UART_LCR, 0xBF);
++ serial_outp(info, UART_EFR, UART_EFR_ECB);
++ /*
++ * Turn off LCR == 0xBF so we actually set the IER
++ * register on the XR16C850
++ */
++ serial_outp(info, UART_LCR, 0);
++ serial_outp(info, UART_IER, 0);
++ /*
++ * Now reset LCR so we can turn off the ECB bit
++ */
++ serial_outp(info, UART_LCR, 0xBF);
++ serial_outp(info, UART_EFR, 0);
++ /*
++ * For a XR16C850, we need to set the trigger levels
++ */
++ if (state->type == PORT_16850) {
++ serial_outp(info, UART_FCTR, UART_FCTR_TRGD |
++ UART_FCTR_RX);
++ serial_outp(info, UART_TRG, UART_TRG_96);
++ serial_outp(info, UART_FCTR, UART_FCTR_TRGD |
++ UART_FCTR_TX);
++ serial_outp(info, UART_TRG, UART_TRG_96);
++ }
++ serial_outp(info, UART_LCR, 0);
++ }
++
++ if (state->type == PORT_16750) {
++ /* Wake up UART */
++ serial_outp(info, UART_IER, 0);
++ }
++
++ if (state->type == PORT_16C950) {
++ /* Wake up and initialize UART */
++ info->ACR = 0;
++ serial_outp(info, UART_LCR, 0xBF);
++ serial_outp(info, UART_EFR, UART_EFR_ECB);
++ serial_outp(info, UART_IER, 0);
++ serial_outp(info, UART_LCR, 0);
++ serial_icr_write(info, UART_CSR, 0); /* Reset the UART */
++ serial_outp(info, UART_LCR, 0xBF);
++ serial_outp(info, UART_EFR, UART_EFR_ECB);
++ serial_outp(info, UART_LCR, 0);
++ }
++
++#ifdef CONFIG_SERIAL_RSA
++ /*
++ * If this is an RSA port, see if we can kick it up to the
++ * higher speed clock.
++ */
++ if (state->type == PORT_RSA) {
++ if (state->baud_base != SERIAL_RSA_BAUD_BASE &&
++ enable_rsa(info))
++ state->baud_base = SERIAL_RSA_BAUD_BASE;
++ if (state->baud_base == SERIAL_RSA_BAUD_BASE)
++ serial_outp(info, UART_RSA_FRR, 0);
++ }
++#endif
++
++ /*
++ * Clear the FIFO buffers and disable them
++ * (they will be reenabled in change_speed())
++ */
++ if (uart_config[state->type].flags & UART_CLEAR_FIFO) {
++ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
++ serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO |
++ UART_FCR_CLEAR_RCVR |
++ UART_FCR_CLEAR_XMIT));
++ serial_outp(info, UART_FCR, 0);
++ }
++
++ /*
++ * Clear the interrupt registers.
++ */
++ (void) serial_inp(info, UART_LSR);
++ (void) serial_inp(info, UART_RX);
++ (void) serial_inp(info, UART_IIR);
++ (void) serial_inp(info, 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 (!(info->flags & ASYNC_BUGGY_UART) &&
++ (serial_inp(info, UART_LSR) == 0xff)) {
++ printk("ttyS%d: LSR safety check engaged!\n", state->line);
++ if (capable(CAP_SYS_ADMIN)) {
++ if (info->tty)
++ set_bit(TTY_IO_ERROR, &info->tty->flags);
++ } else
++ retval = -ENODEV;
++ goto errout;
++ }
++
++ /*
++ * Allocate the IRQ if necessary
++ */
++ if (state->irq && (!IRQ_ports[state->irq] ||
++ !IRQ_ports[state->irq]->next_port)) {
++ if (IRQ_ports[state->irq]) {
++#ifdef CONFIG_SERIAL_SHARE_IRQ
++ free_irq(state->irq, &IRQ_ports[state->irq]);
++#ifdef CONFIG_SERIAL_MULTIPORT
++ if (rs_multiport[state->irq].port1)
++ handler = rs_interrupt_multi;
++ else
++#endif
++ handler = rs_interrupt;
++#else
++ retval = -EBUSY;
++ goto errout;
++#endif /* CONFIG_SERIAL_SHARE_IRQ */
++ } else
++ handler = rs_interrupt_single;
++
++ retval = request_irq(state->irq, handler, SA_SHIRQ,
++ "serial", &IRQ_ports[state->irq]);
++ if (retval) {
++ if (capable(CAP_SYS_ADMIN)) {
++ if (info->tty)
++ set_bit(TTY_IO_ERROR,
++ &info->tty->flags);
++ retval = 0;
++ }
++ goto errout;
++ }
++ }
++
++ /*
++ * Insert serial port into IRQ chain.
++ */
++ info->prev_port = 0;
++ info->next_port = IRQ_ports[state->irq];
++ if (info->next_port)
++ info->next_port->prev_port = info;
++ IRQ_ports[state->irq] = info;
++ figure_IRQ_timeout(state->irq);
++
++ /*
++ * Now, initialize the UART
++ */
++ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
++
++ info->MCR = 0;
++ if (info->tty->termios->c_cflag & CBAUD)
++ info->MCR = UART_MCR_DTR | UART_MCR_RTS;
++#ifdef CONFIG_SERIAL_MANY_PORTS
++ if (info->flags & ASYNC_FOURPORT) {
++ if (state->irq == 0)
++ info->MCR |= UART_MCR_OUT1;
++ } else
++#endif
++ {
++ if (state->irq != 0)
++ info->MCR |= UART_MCR_OUT2;
++ }
++ info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */
++ serial_outp(info, UART_MCR, info->MCR);
++
++ /*
++ * Finally, enable interrupts
++ */
++ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
++ serial_outp(info, UART_IER, info->IER); /* enable interrupts */
++
++#ifdef CONFIG_SERIAL_MANY_PORTS
++ if (info->flags & ASYNC_FOURPORT) {
++ /* Enable interrupts on the AST Fourport board */
++ ICP = (info->port & 0xFE0) | 0x01F;
++ outb_p(0x80, ICP);
++ (void) inb_p(ICP);
++ }
++#endif
++
++ /*
++ * And clear the interrupt registers again for luck.
++ */
++ (void)serial_inp(info, UART_LSR);
++ (void)serial_inp(info, UART_RX);
++ (void)serial_inp(info, UART_IIR);
++ (void)serial_inp(info, UART_MSR);
++
++ if (info->tty)
++ clear_bit(TTY_IO_ERROR, &info->tty->flags);
++ info->xmit.head = info->xmit.tail = 0;
++
++ /*
++ * Set up serial timers...
++ */
++ mod_timer(&serial_timer, jiffies + 2*HZ/100);
++
++ /*
++ * Set up the tty->alt_speed kludge
++ */
++#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */
++ 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;
++ }
++#endif
++
++ /*
++ * and set the speed of the serial port
++ */
++ 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 shutdown(struct async_struct * info)
++{
++ unsigned long flags;
++ struct serial_state *state;
++ int retval;
++
++ if (!(info->flags & ASYNC_INITIALIZED))
++ return;
++
++ state = info->state;
++
++#ifdef SERIAL_DEBUG_OPEN
++ printk("Shutting down serial port %d (irq %d)....", info->line,
++ state->irq);
++#endif
++
++ 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 waken up
++ */
++ wake_up_interruptible(&info->delta_msr_wait);
++
++ /*
++ * First unlink the serial port from the IRQ chain...
++ */
++ if (info->next_port)
++ info->next_port->prev_port = info->prev_port;
++ if (info->prev_port)
++ info->prev_port->next_port = info->next_port;
++ else
++ IRQ_ports[state->irq] = info->next_port;
++ figure_IRQ_timeout(state->irq);
++
++ /*
++ * Free the IRQ, if necessary
++ */
++ if (state->irq && (!IRQ_ports[state->irq] ||
++ !IRQ_ports[state->irq]->next_port)) {
++ if (IRQ_ports[state->irq]) {
++ free_irq(state->irq, &IRQ_ports[state->irq]);
++ retval = request_irq(state->irq, rs_interrupt_single,
++ SA_SHIRQ, "serial",
++ &IRQ_ports[state->irq]);
++
++ if (retval)
++ printk("serial shutdown: request_irq: error %d"
++ " Couldn't reacquire IRQ.\n", retval);
++ } else
++ free_irq(state->irq, &IRQ_ports[state->irq]);
++ }
++
++ if (info->xmit.buf) {
++ unsigned long pg = (unsigned long) info->xmit.buf;
++ info->xmit.buf = 0;
++ free_page(pg);
++ }
++
++ info->IER = 0;
++ serial_outp(info, UART_IER, 0x00); /* disable all intrs */
++#ifdef CONFIG_SERIAL_MANY_PORTS
++ if (info->flags & ASYNC_FOURPORT) {
++ /* reset interrupts on the AST Fourport board */
++ (void) inb((info->port & 0xFE0) | 0x01F);
++ info->MCR |= UART_MCR_OUT1;
++ } else
++#endif
++ info->MCR &= ~UART_MCR_OUT2;
++ info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */
++
++ /* disable break condition */
++ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
++
++ if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
++ info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
++ serial_outp(info, UART_MCR, info->MCR);
++
++ /* disable FIFO's */
++ serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO |
++ UART_FCR_CLEAR_RCVR |
++ UART_FCR_CLEAR_XMIT));
++ serial_outp(info, UART_FCR, 0);
++
++#ifdef CONFIG_SERIAL_RSA
++ /*
++ * Reset the RSA board back to 115kbps compat mode.
++ */
++ if ((state->type == PORT_RSA) &&
++ (state->baud_base == SERIAL_RSA_BAUD_BASE &&
++ disable_rsa(info)))
++ state->baud_base = SERIAL_RSA_BAUD_BASE_LO;
++#endif
++
++
++ (void)serial_in(info, UART_RX); /* read data port to reset things */
++
++ if (info->tty)
++ set_bit(TTY_IO_ERROR, &info->tty->flags);
++
++ if (uart_config[info->state->type].flags & UART_STARTECH) {
++ /* Arrange to enter sleep mode */
++ serial_outp(info, UART_LCR, 0xBF);
++ serial_outp(info, UART_EFR, UART_EFR_ECB);
++ serial_outp(info, UART_LCR, 0);
++ serial_outp(info, UART_IER, UART_IERX_SLEEP);
++ serial_outp(info, UART_LCR, 0xBF);
++ serial_outp(info, UART_EFR, 0);
++ serial_outp(info, UART_LCR, 0);
++ }
++ if (info->state->type == PORT_16750) {
++ /* Arrange to enter sleep mode */
++ serial_outp(info, UART_IER, UART_IERX_SLEEP);
++ }
++ info->flags &= ~ASYNC_INITIALIZED;
++ restore_flags(flags);
++}
++
++#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
++static int baud_table[] = {
++ 0, 50, 75, 110, 134, 150, 200, 300,
++ 600, 1200, 1800, 2400, 4800, 9600, 19200,
++ 38400, 57600, 115200, 230400, 460800, 0 };
++
++static int tty_get_baud_rate(struct tty_struct *tty)
++{
++ struct async_struct * info = (struct async_struct *)tty->driver_data;
++ unsigned int cflag, i;
++
++ cflag = tty->termios->c_cflag;
++
++ i = cflag & CBAUD;
++ if (i & CBAUDEX) {
++ i &= ~CBAUDEX;
++ if (i < 1 || i > 2)
++ tty->termios->c_cflag &= ~CBAUDEX;
++ else
++ i += 15;
++ }
++ if (i == 15) {
++ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
++ i += 1;
++ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
++ i += 2;
++ }
++ return baud_table[i];
++}
++#endif
++
++/*
++ * This routine is called to set the UART divisor registers to match
++ * the specified baud rate for a serial port.
++ */
++static void change_speed(struct async_struct *info,
++ struct termios *old_termios)
++{
++ int quot = 0, baud_base, baud;
++ unsigned cflag, cval, fcr = 0;
++ int bits;
++ unsigned long flags;
++
++ if (!info->tty || !info->tty->termios)
++ return;
++ cflag = info->tty->termios->c_cflag;
++ if (!CONFIGURED_SERIAL_PORT(info))
++ return;
++
++ /* byte size and parity */
++ switch (cflag & CSIZE) {
++ case CS5: cval = 0x00; bits = 7; break;
++ case CS6: cval = 0x01; bits = 8; break;
++ case CS7: cval = 0x02; bits = 9; break;
++ case CS8: cval = 0x03; bits = 10; break;
++ /* Never happens, but GCC is too dumb to figure it out */
++ default: cval = 0x00; bits = 7; break;
++ }
++ if (cflag & CSTOPB) {
++ cval |= 0x04;
++ bits++;
++ }
++ if (cflag & PARENB) {
++ cval |= UART_LCR_PARITY;
++ bits++;
++ }
++ if (!(cflag & PARODD))
++ cval |= UART_LCR_EPAR;
++#ifdef CMSPAR
++ if (cflag & CMSPAR)
++ cval |= UART_LCR_SPAR;
++#endif
++
++ /* Determine divisor based on baud rate */
++ baud = tty_get_baud_rate(info->tty);
++ if (!baud)
++ baud = 9600; /* B0 transition handled in rs_set_termios */
++#ifdef CONFIG_SERIAL_RSA
++ if ((info->state->type == PORT_RSA) &&
++ (info->state->baud_base != SERIAL_RSA_BAUD_BASE) &&
++ enable_rsa(info))
++ info->state->baud_base = SERIAL_RSA_BAUD_BASE;
++#endif
++ baud_base = info->state->baud_base;
++ if (info->state->type == PORT_16C950) {
++ if (baud <= baud_base)
++ serial_icr_write(info, UART_TCR, 0);
++ else if (baud <= 2*baud_base) {
++ serial_icr_write(info, UART_TCR, 0x8);
++ baud_base = baud_base * 2;
++ } else if (baud <= 4*baud_base) {
++ serial_icr_write(info, UART_TCR, 0x4);
++ baud_base = baud_base * 4;
++ } else
++ serial_icr_write(info, UART_TCR, 0);
++ }
++ if (baud == 38400 &&
++ ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
++ quot = info->state->custom_divisor;
++ else {
++ if (baud == 134)
++ /* Special case since 134 is really 134.5 */
++ quot = (2*baud_base / 269);
++ else if (baud)
++ quot = baud_base / baud;
++ }
++ /* If the quotient is zero refuse the change */
++ if (!quot && old_termios) {
++ info->tty->termios->c_cflag &= ~CBAUD;
++ info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
++ 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 {
++ if (baud == 134)
++ /* Special case since 134 is really 134.5 */
++ quot = (2*baud_base / 269);
++ else if (baud)
++ quot = baud_base / baud;
++ }
++ }
++ /* As a last resort, if the quotient is zero, default to 9600 bps */
++ if (!quot)
++ quot = baud_base / 9600;
++ /*
++ * 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) && (info->state->type == PORT_16C950) &&
++ (info->state->revision == 0x5201))
++ quot++;
++
++ info->quot = quot;
++ info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
++ info->timeout += HZ/50; /* Add .02 seconds of slop */
++
++ /* Set up FIFO's */
++ if (uart_config[info->state->type].flags & UART_USE_FIFO) {
++ if ((info->state->baud_base / quot) < 2400)
++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
++#ifdef CONFIG_SERIAL_RSA
++ else if (info->state->type == PORT_RSA)
++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
++#endif
++ else
++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
++ }
++ if (info->state->type == PORT_16750)
++ fcr |= UART_FCR7_64BYTE;
++
++ /* CTS flow control flag and modem status interrupts */
++ info->IER &= ~UART_IER_MSI;
++ if (info->flags & ASYNC_HARDPPS_CD)
++ info->IER |= UART_IER_MSI;
++ if (cflag & CRTSCTS) {
++ info->flags |= ASYNC_CTS_FLOW;
++ info->IER |= UART_IER_MSI;
++ } else
++ info->flags &= ~ASYNC_CTS_FLOW;
++ if (cflag & CLOCAL)
++ info->flags &= ~ASYNC_CHECK_CD;
++ else {
++ info->flags |= ASYNC_CHECK_CD;
++ info->IER |= UART_IER_MSI;
++ }
++ serial_out(info, UART_IER, info->IER);
++
++ /*
++ * Set up parity check flag
++ */
++#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
++
++ info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
++ if (I_INPCK(info->tty))
++ info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
++ if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
++ info->read_status_mask |= UART_LSR_BI;
++
++ /*
++ * Characters to ignore
++ */
++ info->ignore_status_mask = 0;
++ if (I_IGNPAR(info->tty))
++ info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
++ if (I_IGNBRK(info->tty)) {
++ info->ignore_status_mask |= UART_LSR_BI;
++ /*
++ * If we're ignore parity and break indicators, ignore
++ * overruns too. (For real raw support).
++ */
++ if (I_IGNPAR(info->tty))
++ info->ignore_status_mask |= UART_LSR_OE;
++ }
++ /*
++ * !!! ignore all characters if CREAD is not set
++ */
++ if ((cflag & CREAD) == 0)
++ info->ignore_status_mask |= UART_LSR_DR;
++ save_flags(flags); cli();
++ if (uart_config[info->state->type].flags & UART_STARTECH) {
++ serial_outp(info, UART_LCR, 0xBF);
++ serial_outp(info, UART_EFR,
++ (cflag & CRTSCTS) ? UART_EFR_CTS : 0);
++ }
++ serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
++ serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
++ serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
++ if (info->state->type == PORT_16750)
++ serial_outp(info, UART_FCR, fcr); /* set fcr */
++ serial_outp(info, UART_LCR, cval); /* reset DLAB */
++ info->LCR = cval; /* Save LCR */
++ if (info->state->type != PORT_16750) {
++ if (fcr & UART_FCR_ENABLE_FIFO) {
++ /* emulated UARTs (Lucent Venus 167x) need two steps */
++ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
++ }
++ serial_outp(info, UART_FCR, fcr); /* set fcr */
++ }
++ restore_flags(flags);
++}
++
++static void rs_put_char(struct tty_struct *tty, unsigned char ch)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++
++ if (serial_paranoia_check(info, tty->device, "rs_put_char"))
++ return;
++
++ if (!tty || !info->xmit.buf)
++ return;
++
++ save_flags(flags); cli();
++ if (CIRC_SPACE(info->xmit.head,
++ info->xmit.tail,
++ SERIAL_XMIT_SIZE) == 0) {
++ restore_flags(flags);
++ return;
++ }
++
++ info->xmit.buf[info->xmit.head] = ch;
++ info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
++ restore_flags(flags);
++}
++
++static void rs_flush_chars(struct tty_struct *tty)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++
++ if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
++ return;
++
++ if (info->xmit.head == info->xmit.tail
++ || tty->stopped
++ || tty->hw_stopped
++ || !info->xmit.buf)
++ return;
++
++ save_flags(flags); cli();
++ info->IER |= UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ restore_flags(flags);
++}
++
++static int rs_write(struct tty_struct * tty, int from_user,
++ const unsigned char *buf, int count)
++{
++ int c, ret = 0;
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++
++ if (serial_paranoia_check(info, tty->device, "rs_write"))
++ return 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,
++ SERIAL_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,
++ SERIAL_XMIT_SIZE);
++ if (c1 < c)
++ c = c1;
++ memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
++ info->xmit.head = ((info->xmit.head + c) &
++ (SERIAL_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,
++ SERIAL_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) &
++ (SERIAL_XMIT_SIZE-1));
++ buf += c;
++ count -= c;
++ ret += c;
++ }
++ restore_flags(flags);
++ }
++ if (info->xmit.head != info->xmit.tail
++ && !tty->stopped
++ && !tty->hw_stopped
++ && !(info->IER & UART_IER_THRI)) {
++ info->IER |= UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ }
++ return ret;
++}
++
++static int rs_write_room(struct tty_struct *tty)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++
++ if (serial_paranoia_check(info, tty->device, "rs_write_room"))
++ return 0;
++ return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
++}
++
++static int rs_chars_in_buffer(struct tty_struct *tty)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++
++ if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
++ return 0;
++ return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
++}
++
++static void rs_flush_buffer(struct tty_struct *tty)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++
++ if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
++ return;
++ save_flags(flags); cli();
++ info->xmit.head = info->xmit.tail = 0;
++ restore_flags(flags);
++#ifdef SERIAL_HAVE_POLL_WAIT
++ wake_up_interruptible(&tty->poll_wait);
++#endif
++ tty_wakeup(tty);
++}
++
++/*
++ * This function is used to send a high-priority XON/XOFF character to
++ * the device
++ */
++static void rs_send_xchar(struct tty_struct *tty, char ch)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++
++ if (serial_paranoia_check(info, tty->device, "rs_send_char"))
++ return;
++
++ info->x_char = ch;
++ if (ch) {
++ /* Make sure transmit interrupts are on */
++ info->IER |= UART_IER_THRI;
++ serial_out(info, UART_IER, info->IER);
++ }
++}
++
++/*
++ * ------------------------------------------------------------
++ * rs_throttle()
++ *
++ * This routine is called by the upper-layer tty layer to signal that
++ * incoming characters should be throttled.
++ * ------------------------------------------------------------
++ */
++static void rs_throttle(struct tty_struct * tty)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++#ifdef SERIAL_DEBUG_THROTTLE
++ char buf[64];
++
++ printk("throttle %s: %d....\n", tty_name(tty, buf),
++ tty->ldisc.chars_in_buffer(tty));
++#endif
++
++ if (serial_paranoia_check(info, tty->device, "rs_throttle"))
++ return;
++
++ if (I_IXOFF(tty))
++ rs_send_xchar(tty, STOP_CHAR(tty));
++
++ if (tty->termios->c_cflag & CRTSCTS)
++ info->MCR &= ~UART_MCR_RTS;
++
++ save_flags(flags); cli();
++ serial_out(info, UART_MCR, info->MCR);
++ restore_flags(flags);
++}
++
++static void rs_unthrottle(struct tty_struct * tty)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++#ifdef SERIAL_DEBUG_THROTTLE
++ char buf[64];
++
++ printk("unthrottle %s: %d....\n", tty_name(tty, buf),
++ tty->ldisc.chars_in_buffer(tty));
++#endif
++
++ if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
++ return;
++
++ if (I_IXOFF(tty)) {
++ if (info->x_char)
++ info->x_char = 0;
++ else
++ rs_send_xchar(tty, START_CHAR(tty));
++ }
++ if (tty->termios->c_cflag & CRTSCTS)
++ info->MCR |= UART_MCR_RTS;
++ save_flags(flags); cli();
++ serial_out(info, UART_MCR, info->MCR);
++ restore_flags(flags);
++}
++
++/*
++ * ------------------------------------------------------------
++ * rs_ioctl() and friends
++ * ------------------------------------------------------------
++ */
++
++static int get_serial_info(struct async_struct * info,
++ struct serial_struct * retinfo)
++{
++ struct serial_struct tmp;
++ struct serial_state *state = info->state;
++
++ if (!retinfo)
++ return -EFAULT;
++ memset(&tmp, 0, sizeof(tmp));
++ tmp.type = state->type;
++ tmp.line = state->line;
++ tmp.port = state->port;
++ if (HIGH_BITS_OFFSET)
++ tmp.port_high = state->port >> HIGH_BITS_OFFSET;
++ else
++ tmp.port_high = 0;
++ tmp.irq = state->irq;
++ tmp.flags = state->flags;
++ tmp.xmit_fifo_size = state->xmit_fifo_size;
++ tmp.baud_base = state->baud_base;
++ tmp.close_delay = state->close_delay;
++ tmp.closing_wait = state->closing_wait;
++ tmp.custom_divisor = state->custom_divisor;
++ tmp.hub6 = state->hub6;
++ tmp.io_type = state->io_type;
++ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
++ return -EFAULT;
++ return 0;
++}
++
++static int set_serial_info(struct async_struct * info,
++ struct serial_struct * new_info)
++{
++ struct serial_struct new_serial;
++ struct serial_state old_state, *state;
++ unsigned int i,change_irq,change_port;
++ int retval = 0;
++ unsigned long new_port;
++
++ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
++ return -EFAULT;
++ state = info->state;
++ old_state = *state;
++
++ 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 != state->irq;
++ change_port = (new_port != ((int) state->port)) ||
++ (new_serial.hub6 != state->hub6);
++
++ if (!capable(CAP_SYS_ADMIN)) {
++ if (change_irq || change_port ||
++ (new_serial.baud_base != state->baud_base) ||
++ (new_serial.type != state->type) ||
++ (new_serial.close_delay != state->close_delay) ||
++ (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
++ ((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;
++ }
++
++ new_serial.irq = irq_cannonicalize(new_serial.irq);
++
++ if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) ||
++ (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) ||
++ (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) ||
++ (new_serial.type == PORT_STARTECH)) {
++ return -EINVAL;
++ }
++
++ if ((new_serial.type != state->type) ||
++ (new_serial.xmit_fifo_size <= 0))
++ new_serial.xmit_fifo_size =
++ uart_config[new_serial.type].dfl_xmit_fifo_size;
++
++ /* Make sure address is not already in use */
++ if (new_serial.type) {
++ for (i = 0 ; i < NR_PORTS; i++)
++ if ((state != &rs_table[i]) &&
++ (rs_table[i].io_type == SERIAL_IO_PORT) &&
++ (rs_table[i].port == new_port) &&
++ rs_table[i].type)
++ 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.....
++ */
++
++ state->baud_base = new_serial.baud_base;
++ 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;
++#if (LINUX_VERSION_CODE > 0x20100)
++ info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
++#endif
++ info->xmit_fifo_size = state->xmit_fifo_size =
++ new_serial.xmit_fifo_size;
++
++ if ((state->type != PORT_UNKNOWN) && state->port) {
++#ifdef CONFIG_SERIAL_RSA
++ if (old_state.type == PORT_RSA)
++ release_region(state->port + UART_RSA_BASE, 16);
++ else
++#endif
++ release_region(state->port,8);
++ }
++ state->type = new_serial.type;
++ if (change_port || change_irq) {
++ /*
++ * We need to shutdown the serial port at the old
++ * port/irq combination.
++ */
++ shutdown(info);
++ state->irq = new_serial.irq;
++ info->port = state->port = new_port;
++ info->hub6 = state->hub6 = new_serial.hub6;
++ if (info->hub6)
++ info->io_type = state->io_type = SERIAL_IO_HUB6;
++ else if (info->io_type == SERIAL_IO_HUB6)
++ info->io_type = state->io_type = SERIAL_IO_PORT;
++ }
++ if ((state->type != PORT_UNKNOWN) && state->port) {
++#ifdef CONFIG_SERIAL_RSA
++ if (state->type == PORT_RSA)
++ request_region(state->port + UART_RSA_BASE,
++ 16, "serial_rsa(set)");
++ else
++#endif
++ request_region(state->port,8,"serial(set)");
++ }
++
++
++check_and_exit:
++ if ((!state->port && !state->iomem_base) || !state->type)
++ 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 (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */
++ 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;
++#endif
++ change_speed(info, 0);
++ }
++ } else
++ retval = startup(info);
++ return retval;
++}
++
++
++/*
++ * get_lsr_info - get line status register info
++ *
++ * Purpose: Let user call ioctl() to get info when the UART physically
++ * is emptied. On bus types like RS485, the transmitter must
++ * release the bus after transmitting. This must be done when
++ * the transmit shift register is empty, not be done when the
++ * transmit holding register is empty. This functionality
++ * allows an RS485 driver to be written in user space.
++ */
++static int get_lsr_info(struct async_struct * info, unsigned int *value)
++{
++ unsigned char status;
++ unsigned int result;
++ unsigned long flags;
++
++ save_flags(flags); cli();
++ status = serial_in(info, UART_LSR);
++ restore_flags(flags);
++ result = ((status & UART_LSR_TEMT) ? 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,
++ SERIAL_XMIT_SIZE) > 0) &&
++ !info->tty->stopped && !info->tty->hw_stopped))
++ result &= ~TIOCSER_TEMT;
++
++ if (copy_to_user(value, &result, sizeof(int)))
++ return -EFAULT;
++ return 0;
++}
++
++
++static int get_modem_info(struct async_struct * info, unsigned int *value)
++{
++ unsigned char control, status;
++ unsigned int result;
++ unsigned long flags;
++
++ control = info->MCR;
++ save_flags(flags); cli();
++ status = serial_in(info, UART_MSR);
++ restore_flags(flags);
++ result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
++ | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
++#ifdef TIOCM_OUT1
++ | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0)
++ | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)
++#endif
++ | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
++ | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
++ | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
++ | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
++
++ if (copy_to_user(value, &result, sizeof(int)))
++ return -EFAULT;
++ return 0;
++}
++
++static int set_modem_info(struct async_struct * info, unsigned int cmd,
++ unsigned int *value)
++{
++ unsigned int arg;
++ unsigned long flags;
++
++ if (copy_from_user(&arg, value, sizeof(int)))
++ return -EFAULT;
++
++ switch (cmd) {
++ case TIOCMBIS:
++ if (arg & TIOCM_RTS)
++ info->MCR |= UART_MCR_RTS;
++ if (arg & TIOCM_DTR)
++ info->MCR |= UART_MCR_DTR;
++#ifdef TIOCM_OUT1
++ if (arg & TIOCM_OUT1)
++ info->MCR |= UART_MCR_OUT1;
++ if (arg & TIOCM_OUT2)
++ info->MCR |= UART_MCR_OUT2;
++#endif
++ if (arg & TIOCM_LOOP)
++ info->MCR |= UART_MCR_LOOP;
++ break;
++ case TIOCMBIC:
++ if (arg & TIOCM_RTS)
++ info->MCR &= ~UART_MCR_RTS;
++ if (arg & TIOCM_DTR)
++ info->MCR &= ~UART_MCR_DTR;
++#ifdef TIOCM_OUT1
++ if (arg & TIOCM_OUT1)
++ info->MCR &= ~UART_MCR_OUT1;
++ if (arg & TIOCM_OUT2)
++ info->MCR &= ~UART_MCR_OUT2;
++#endif
++ if (arg & TIOCM_LOOP)
++ info->MCR &= ~UART_MCR_LOOP;
++ break;
++ case TIOCMSET:
++ info->MCR = ((info->MCR & ~(UART_MCR_RTS |
++#ifdef TIOCM_OUT1
++ UART_MCR_OUT1 |
++ UART_MCR_OUT2 |
++#endif
++ UART_MCR_LOOP |
++ UART_MCR_DTR))
++ | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
++#ifdef TIOCM_OUT1
++ | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0)
++ | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)
++#endif
++ | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0)
++ | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
++ break;
++ default:
++ return -EINVAL;
++ }
++ save_flags(flags); cli();
++ info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */
++ serial_out(info, UART_MCR, info->MCR);
++ restore_flags(flags);
++ return 0;
++}
++
++static int do_autoconfig(struct async_struct * info)
++{
++ int irq, retval;
++
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++
++ if (info->state->count > 1)
++ return -EBUSY;
++
++ shutdown(info);
++
++ autoconfig(info->state);
++ if ((info->state->flags & ASYNC_AUTO_IRQ) &&
++ (info->state->port != 0 || info->state->iomem_base != 0) &&
++ (info->state->type != PORT_UNKNOWN)) {
++ irq = detect_uart_irq(info->state);
++ if (irq > 0)
++ info->state->irq = irq;
++ }
++
++ retval = startup(info);
++ if (retval)
++ return retval;
++ return 0;
++}
++
++/*
++ * rs_break() --- routine which turns the break handling on or off
++ */
++#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
++static void send_break( struct async_struct * info, int duration)
++{
++ if (!CONFIGURED_SERIAL_PORT(info))
++ return;
++ current->state = TASK_INTERRUPTIBLE;
++ current->timeout = jiffies + duration;
++ cli();
++ info->LCR |= UART_LCR_SBC;
++ serial_out(info, UART_LCR, info->LCR);
++ schedule();
++ info->LCR &= ~UART_LCR_SBC;
++ serial_out(info, UART_LCR, info->LCR);
++ sti();
++}
++#else
++static void rs_break(struct tty_struct *tty, int break_state)
++{
++ struct async_struct * info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++
++ if (serial_paranoia_check(info, tty->device, "rs_break"))
++ return;
++
++ if (!CONFIGURED_SERIAL_PORT(info))
++ return;
++ save_flags(flags); cli();
++ if (break_state == -1)
++ info->LCR |= UART_LCR_SBC;
++ else
++ info->LCR &= ~UART_LCR_SBC;
++ serial_out(info, UART_LCR, info->LCR);
++ restore_flags(flags);
++}
++#endif
++
++#ifdef CONFIG_SERIAL_MULTIPORT
++static int get_multiport_struct(struct async_struct * info,
++ struct serial_multiport_struct *retinfo)
++{
++ struct serial_multiport_struct ret;
++ struct rs_multiport_struct *multi;
++
++ multi = &rs_multiport[info->state->irq];
++
++ ret.port_monitor = multi->port_monitor;
++
++ ret.port1 = multi->port1;
++ ret.mask1 = multi->mask1;
++ ret.match1 = multi->match1;
++
++ ret.port2 = multi->port2;
++ ret.mask2 = multi->mask2;
++ ret.match2 = multi->match2;
++
++ ret.port3 = multi->port3;
++ ret.mask3 = multi->mask3;
++ ret.match3 = multi->match3;
++
++ ret.port4 = multi->port4;
++ ret.mask4 = multi->mask4;
++ ret.match4 = multi->match4;
++
++ ret.irq = info->state->irq;
++
++ if (copy_to_user(retinfo,&ret,sizeof(*retinfo)))
++ return -EFAULT;
++ return 0;
++}
++
++static int set_multiport_struct(struct async_struct * info,
++ struct serial_multiport_struct *in_multi)
++{
++ struct serial_multiport_struct new_multi;
++ struct rs_multiport_struct *multi;
++ struct serial_state *state;
++ int was_multi, now_multi;
++ int retval;
++ void (*handler)(int, void *, struct pt_regs *);
++
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++ state = info->state;
++
++ if (copy_from_user(&new_multi, in_multi,
++ sizeof(struct serial_multiport_struct)))
++ return -EFAULT;
++
++ if (new_multi.irq != state->irq || state->irq == 0 ||
++ !IRQ_ports[state->irq])
++ return -EINVAL;
++
++ multi = &rs_multiport[state->irq];
++ was_multi = (multi->port1 != 0);
++
++ multi->port_monitor = new_multi.port_monitor;
++
++ if (multi->port1)
++ release_region(multi->port1,1);
++ multi->port1 = new_multi.port1;
++ multi->mask1 = new_multi.mask1;
++ multi->match1 = new_multi.match1;
++ if (multi->port1)
++ request_region(multi->port1,1,"serial(multiport1)");
++
++ if (multi->port2)
++ release_region(multi->port2,1);
++ multi->port2 = new_multi.port2;
++ multi->mask2 = new_multi.mask2;
++ multi->match2 = new_multi.match2;
++ if (multi->port2)
++ request_region(multi->port2,1,"serial(multiport2)");
++
++ if (multi->port3)
++ release_region(multi->port3,1);
++ multi->port3 = new_multi.port3;
++ multi->mask3 = new_multi.mask3;
++ multi->match3 = new_multi.match3;
++ if (multi->port3)
++ request_region(multi->port3,1,"serial(multiport3)");
++
++ if (multi->port4)
++ release_region(multi->port4,1);
++ multi->port4 = new_multi.port4;
++ multi->mask4 = new_multi.mask4;
++ multi->match4 = new_multi.match4;
++ if (multi->port4)
++ request_region(multi->port4,1,"serial(multiport4)");
++
++ now_multi = (multi->port1 != 0);
++
++ if (IRQ_ports[state->irq]->next_port &&
++ (was_multi != now_multi)) {
++ free_irq(state->irq, &IRQ_ports[state->irq]);
++ if (now_multi)
++ handler = rs_interrupt_multi;
++ else
++ handler = rs_interrupt;
++
++ retval = request_irq(state->irq, handler, SA_SHIRQ,
++ "serial", &IRQ_ports[state->irq]);
++ if (retval) {
++ printk("Couldn't reallocate serial interrupt "
++ "driver!!\n");
++ }
++ }
++ return 0;
++}
++#endif
++
++static int rs_ioctl(struct tty_struct *tty, struct file * file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct async_struct * info = (struct async_struct *)tty->driver_data;
++ struct async_icount cprev, cnow; /* kernel counter temps */
++ struct serial_icounter_struct icount;
++ unsigned long flags;
++#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
++ int retval, tmp;
++#endif
++
++ if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
++ return -ENODEV;
++
++ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
++ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
++ (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
++ if (tty->flags & (1 << TTY_IO_ERROR))
++ return -EIO;
++ }
++
++ switch (cmd) {
++#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
++ case TCSBRK: /* SVID version: non-zero arg --> no break */
++ retval = tty_check_change(tty);
++ if (retval)
++ return retval;
++ tty_wait_until_sent(tty, 0);
++ if (signal_pending(current))
++ return -EINTR;
++ if (!arg) {
++ send_break(info, HZ/4); /* 1/4 second */
++ if (signal_pending(current))
++ return -EINTR;
++ }
++ return 0;
++ case TCSBRKP: /* support for POSIX tcsendbreak() */
++ retval = tty_check_change(tty);
++ if (retval)
++ return retval;
++ tty_wait_until_sent(tty, 0);
++ if (signal_pending(current))
++ return -EINTR;
++ send_break(info, arg ? arg*(HZ/10) : HZ/4);
++ if (signal_pending(current))
++ return -EINTR;
++ return 0;
++ case TIOCGSOFTCAR:
++ tmp = C_CLOCAL(tty) ? 1 : 0;
++ if (copy_to_user((void *)arg, &tmp, sizeof(int)))
++ return -EFAULT;
++ return 0;
++ case TIOCSSOFTCAR:
++ if (copy_from_user(&tmp, (void *)arg, sizeof(int)))
++ return -EFAULT;
++
++ tty->termios->c_cflag =
++ ((tty->termios->c_cflag & ~CLOCAL) |
++ (tmp ? CLOCAL : 0));
++ return 0;
++#endif
++ 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 TIOCSERCONFIG:
++ return do_autoconfig(info);
++
++ case TIOCSERGETLSR: /* Get line status register */
++ return get_lsr_info(info, (unsigned int *) arg);
++
++ case TIOCSERGSTRUCT:
++ if (copy_to_user((struct async_struct *) arg,
++ info, sizeof(struct async_struct)))
++ return -EFAULT;
++ return 0;
++
++#ifdef CONFIG_SERIAL_MULTIPORT
++ case TIOCSERGETMULTI:
++ return get_multiport_struct(info,
++ (struct serial_multiport_struct *) arg);
++ case TIOCSERSETMULTI:
++ return set_multiport_struct(info,
++ (struct serial_multiport_struct *) arg);
++#endif
++
++ /*
++ * 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;
++ restore_flags(flags);
++ /* Force modem status interrupts on */
++ info->IER |= UART_IER_MSI;
++ serial_out(info, UART_IER, info->IER);
++ 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;
++
++ if (copy_to_user((void *)arg, &icount, sizeof(icount)))
++ return -EFAULT;
++ return 0;
++ case TIOCSERGWILD:
++ case TIOCSERSWILD:
++ /* "setserial -W" is called in Debian boot */
++ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
++ return 0;
++
++ default:
++ return -ENOIOCTLCMD;
++ }
++ return 0;
++}
++
++static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
++{
++ struct async_struct *info = (struct async_struct *)tty->driver_data;
++ unsigned long flags;
++ unsigned int cflag = tty->termios->c_cflag;
++
++ if ( (cflag == old_termios->c_cflag)
++ && ( RELEVANT_IFLAG(tty->termios->c_iflag)
++ == RELEVANT_IFLAG(old_termios->c_iflag)))
++ return;
++
++ change_speed(info, old_termios);
++
++ /* Handle transition to B0 status */
++ if ((old_termios->c_cflag & CBAUD) &&
++ !(cflag & CBAUD)) {
++ info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
++ save_flags(flags); cli();
++ serial_out(info, UART_MCR, info->MCR);
++ restore_flags(flags);
++ }
++
++ /* Handle transition away from B0 status */
++ if (!(old_termios->c_cflag & CBAUD) &&
++ (cflag & CBAUD)) {
++ info->MCR |= UART_MCR_DTR;
++ if (!(tty->termios->c_cflag & CRTSCTS) ||
++ !test_bit(TTY_THROTTLED, &tty->flags)) {
++ info->MCR |= UART_MCR_RTS;
++ }
++ save_flags(flags); cli();
++ serial_out(info, UART_MCR, info->MCR);
++ restore_flags(flags);
++ }
++
++ /* Handle turning off CRTSCTS */
++ if ((old_termios->c_cflag & CRTSCTS) &&
++ !(tty->termios->c_cflag & CRTSCTS)) {
++ tty->hw_stopped = 0;
++ rs_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
++}
++
++/*
++ * ------------------------------------------------------------
++ * rs_close()
++ *
++ * This routine is called when the serial port gets closed. First, we
++ * wait for the last remaining data to be sent. Then, we unlink its
++ * async structure from the interrupt chain if necessary, and we free
++ * that IRQ if nothing is left in the chain.
++ * ------------------------------------------------------------
++ */
++static void rs_close(struct tty_struct *tty, struct file * filp)
++{
++ struct async_struct * info = (struct async_struct *)tty->driver_data;
++ struct serial_state *state;
++ unsigned long flags;
++
++ if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
++ return;
++
++ state = info->state;
++
++ save_flags(flags); cli();
++
++ if (tty_hung_up_p(filp)) {
++ DBG_CNT("before DEC-hung");
++ MOD_DEC_USE_COUNT;
++ restore_flags(flags);
++ return;
++ }
++
++#ifdef SERIAL_DEBUG_OPEN
++ printk("rs_close ttys%d, count = %d\n", info->line, state->count);
++#endif
++ 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("rs_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 ttys%d: %d\n",
++ info->line, state->count);
++ state->count = 0;
++ }
++ if (state->count) {
++ DBG_CNT("before DEC-2");
++ 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->closing_wait != ASYNC_CLOSING_WAIT_NONE)
++ tty_wait_until_sent(tty, info->closing_wait);
++ /*
++ * At this point we stop accepting input. To do this, we
++ * disable the receive line status interrupts, and tell the
++ * interrupt driver to stop checking the data ready bit in the
++ * line status register.
++ */
++ info->IER &= ~UART_IER_RLSI;
++ info->read_status_mask &= ~UART_LSR_DR;
++ if (info->flags & ASYNC_INITIALIZED) {
++ serial_out(info, UART_IER, info->IER);
++ /*
++ * Before we drop DTR, make sure the UART transmitter
++ * has completely drained; this is especially
++ * important if there is a transmit FIFO!
++ */
++ rs_wait_until_sent(tty, info->timeout);
++ }
++ shutdown(info);
++ if (tty->driver.flush_buffer)
++ tty->driver.flush_buffer(tty);
++ tty_ldisc_flush(tty);
++ tty->closing = 0;
++ info->event = 0;
++ info->tty = 0;
++ if (info->blocked_open) {
++ if (info->close_delay) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(info->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;
++}
++
++/*
++ * rs_wait_until_sent() --- wait until the transmitter is empty
++ */
++static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
++{
++ struct async_struct * info = (struct async_struct *)tty->driver_data;
++ unsigned long orig_jiffies, char_time;
++ int lsr;
++
++ if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
++ return;
++
++ if (info->state->type == PORT_UNKNOWN)
++ return;
++
++ if (info->xmit_fifo_size == 0)
++ return; /* Just in case.... */
++
++ orig_jiffies = jiffies;
++ /*
++ * 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->xmit_fifo_size;
++ 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;
++#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
++ printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
++ printk("jiff=%lu...", jiffies);
++#endif
++ while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) {
++#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
++ printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
++#endif
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(char_time);
++ if (signal_pending(current))
++ break;
++ if (timeout && time_after(jiffies, orig_jiffies + timeout))
++ break;
++ }
++#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
++ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
++#endif
++}
++
++/*
++ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
++ */
++static void rs_hangup(struct tty_struct *tty)
++{
++ struct async_struct * info = (struct async_struct *)tty->driver_data;
++ struct serial_state *state = info->state;
++
++ if (serial_paranoia_check(info, tty->device, "rs_hangup"))
++ return;
++
++ state = info->state;
++
++ rs_flush_buffer(tty);
++ if (info->flags & ASYNC_CLOSING)
++ return;
++ shutdown(info);
++ info->event = 0;
++ state->count = 0;
++ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
++ info->tty = 0;
++ wake_up_interruptible(&info->open_wait);
++}
++
++/*
++ * ------------------------------------------------------------
++ * rs_open() and friends
++ * ------------------------------------------------------------
++ */
++static int block_til_ready(struct tty_struct *tty, struct file * filp,
++ struct async_struct *info)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct serial_state *state = info->state;
++ int retval;
++ int do_clocal = 0, extra_count = 0;
++ unsigned long flags;
++
++ /*
++ * 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);
++#ifdef SERIAL_DO_RESTART
++ return ((info->flags & ASYNC_HUP_NOTIFY) ?
++ -EAGAIN : -ERESTARTSYS);
++#else
++ return -EAGAIN;
++#endif
++ }
++
++ /*
++ * 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);
++#ifdef SERIAL_DEBUG_OPEN
++ printk("block_til_ready before block: ttys%d, count = %d\n",
++ state->line, state->count);
++#endif
++ 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))
++ serial_out(info, UART_MCR,
++ serial_inp(info, UART_MCR) |
++ (UART_MCR_DTR | UART_MCR_RTS));
++ restore_flags(flags);
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (tty_hung_up_p(filp) ||
++ !(info->flags & ASYNC_INITIALIZED)) {
++#ifdef SERIAL_DO_RESTART
++ if (info->flags & ASYNC_HUP_NOTIFY)
++ retval = -EAGAIN;
++ else
++ retval = -ERESTARTSYS;
++#else
++ retval = -EAGAIN;
++#endif
++ break;
++ }
++ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
++ !(info->flags & ASYNC_CLOSING) &&
++ (do_clocal || (serial_in(info, UART_MSR) &
++ UART_MSR_DCD)))
++ break;
++ if (signal_pending(current)) {
++ retval = -ERESTARTSYS;
++ break;
++ }
++#ifdef SERIAL_DEBUG_OPEN
++ printk("block_til_ready blocking: ttys%d, count = %d\n",
++ info->line, state->count);
++#endif
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&info->open_wait, &wait);
++ if (extra_count)
++ state->count++;
++ info->blocked_open--;
++#ifdef SERIAL_DEBUG_OPEN
++ printk("block_til_ready after blocking: ttys%d, count = %d\n",
++ info->line, state->count);
++#endif
++ if (retval)
++ return retval;
++ info->flags |= ASYNC_NORMAL_ACTIVE;
++ return 0;
++}
++
++static int get_async_struct(int line, struct async_struct **ret_info)
++{
++ struct async_struct *info;
++ struct serial_state *sstate;
++
++ sstate = rs_table + line;
++ sstate->count++;
++ if (sstate->info) {
++ *ret_info = sstate->info;
++ return 0;
++ }
++ info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
++ if (!info) {
++ sstate->count--;
++ return -ENOMEM;
++ }
++ memset(info, 0, sizeof(struct async_struct));
++ init_waitqueue_head(&info->open_wait);
++ init_waitqueue_head(&info->close_wait);
++ init_waitqueue_head(&info->delta_msr_wait);
++ info->magic = SERIAL_MAGIC;
++ info->port = sstate->port;
++ info->flags = sstate->flags;
++ info->io_type = sstate->io_type;
++ info->iomem_base = sstate->iomem_base;
++ info->iomem_reg_shift = sstate->iomem_reg_shift;
++ info->xmit_fifo_size = sstate->xmit_fifo_size;
++ info->line = line;
++ info->tqueue.routine = do_softint;
++ info->tqueue.data = info;
++ info->state = sstate;
++ if (sstate->info) {
++ kfree(info);
++ *ret_info = sstate->info;
++ return 0;
++ }
++ *ret_info = sstate->info = info;
++ return 0;
++}
++
++/*
++ * This routine is called whenever a serial port is opened. It
++ * enables interrupts for a serial port, linking in its async structure into
++ * the IRQ chain. It also performs the serial-specific
++ * initialization for the tty structure.
++ *
++ * Note that on failure, we don't decrement the module use count - the tty
++ * later will call rs_close, which will decrement it for us as long as
++ * tty->driver_data is set non-NULL. --rmk
++ */
++static int rs_open(struct tty_struct *tty, struct file * filp)
++{
++ struct async_struct *info;
++ int retval, line;
++ unsigned long page;
++
++ MOD_INC_USE_COUNT;
++ line = MINOR(tty->device) - tty->driver.minor_start;
++ if ((line < 0) || (line >= NR_PORTS)) {
++ MOD_DEC_USE_COUNT;
++ return -ENODEV;
++ }
++ retval = get_async_struct(line, &info);
++ if (retval) {
++ MOD_DEC_USE_COUNT;
++ return retval;
++ }
++ tty->driver_data = info;
++ info->tty = tty;
++ if (serial_paranoia_check(info, tty->device, "rs_open"))
++ return -ENODEV;
++
++#ifdef SERIAL_DEBUG_OPEN
++ printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
++ info->state->count);
++#endif
++#if (LINUX_VERSION_CODE > 0x20100)
++ info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
++#endif
++
++ /*
++ * This relies on lock_kernel() stuff so wants tidying for 2.5
++ */
++ if (!tmp_buf) {
++ page = get_zeroed_page(GFP_KERNEL);
++ if (!page)
++ return -ENOMEM;
++ if (tmp_buf)
++ free_page(page);
++ else
++ tmp_buf = (unsigned char *) page;
++ }
++
++ /*
++ * If the port is 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);
++#ifdef SERIAL_DO_RESTART
++ return ((info->flags & ASYNC_HUP_NOTIFY) ?
++ -EAGAIN : -ERESTARTSYS);
++#else
++ return -EAGAIN;
++#endif
++ }
++
++ /*
++ * Start up serial port
++ */
++ retval = startup(info);
++ if (retval)
++ return retval;
++
++ retval = block_til_ready(tty, filp, info);
++ if (retval) {
++#ifdef SERIAL_DEBUG_OPEN
++ printk("rs_open returning after block_til_ready with %d\n",
++ retval);
++#endif
++ 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;
++ change_speed(info, 0);
++ }
++#ifdef CONFIG_SERIAL_CONSOLE
++ if (sercons.cflag && sercons.index == line) {
++ tty->termios->c_cflag = sercons.cflag;
++ sercons.cflag = 0;
++ change_speed(info, 0);
++ }
++#endif
++ info->session = current->session;
++ info->pgrp = current->pgrp;
++
++#ifdef SERIAL_DEBUG_OPEN
++ printk("rs_open ttys%d successful...", info->line);
++#endif
++ return 0;
++}
++
++/*
++ * /proc fs routines....
++ */
++
++static inline int line_info(char *buf, struct serial_state *state)
++{
++ struct async_struct *info = state->info, scr_info;
++ char stat_buf[30], control, status;
++ int ret;
++ unsigned long flags;
++
++ /*
++ * Return zero characters for ports not claimed by driver.
++ */
++ if (state->type == PORT_UNKNOWN) {
++ return 0; /* ignore unused ports */
++ }
++
++ ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d",
++ state->line, uart_config[state->type].name,
++ (state->port ? state->port : (long)state->iomem_base),
++ state->irq);
++
++ /*
++ * Figure out the current RS-232 lines
++ */
++ if (!info) {
++ info = &scr_info; /* This is just for serial_{in,out} */
++
++ info->magic = SERIAL_MAGIC;
++ info->port = state->port;
++ info->flags = state->flags;
++ info->hub6 = state->hub6;
++ info->io_type = state->io_type;
++ info->iomem_base = state->iomem_base;
++ info->iomem_reg_shift = state->iomem_reg_shift;
++ info->quot = 0;
++ info->tty = 0;
++ }
++ save_flags(flags); cli();
++ status = serial_in(info, UART_MSR);
++ control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR);
++ restore_flags(flags);
++
++ stat_buf[0] = 0;
++ stat_buf[1] = 0;
++ if (control & UART_MCR_RTS)
++ strcat(stat_buf, "|RTS");
++ if (status & UART_MSR_CTS)
++ strcat(stat_buf, "|CTS");
++ if (control & UART_MCR_DTR)
++ strcat(stat_buf, "|DTR");
++ if (status & UART_MSR_DSR)
++ strcat(stat_buf, "|DSR");
++ if (status & UART_MSR_DCD)
++ strcat(stat_buf, "|CD");
++ if (status & UART_MSR_RI)
++ strcat(stat_buf, "|RI");
++
++ if (info->quot) {
++ ret += sprintf(buf+ret, " baud:%d",
++ state->baud_base / info->quot);
++ }
++
++ ret += sprintf(buf+ret, " tx:%d rx:%d",
++ state->icount.tx, state->icount.rx);
++
++ if (state->icount.frame)
++ ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
++
++ if (state->icount.parity)
++ ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
++
++ if (state->icount.brk)
++ ret += sprintf(buf+ret, " brk:%d", state->icount.brk);
++
++ if (state->icount.overrun)
++ ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
++
++ /*
++ * Last thing is the RS-232 status lines
++ */
++ ret += sprintf(buf+ret, " %s\n", stat_buf+1);
++ return ret;
++}
++
++static int rs_read_proc(char *page, char **start, off_t off, int count,
++ int *eof, void *data)
++{
++ int i, len = 0, l;
++ off_t begin = 0;
++
++ len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n",
++ serial_version, LOCAL_VERSTRING, serial_revdate);
++ for (i = 0; i < NR_PORTS && len < 4000; i++) {
++ l = line_info(page + len, &rs_table[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);
++}
++
++/*
++ * ---------------------------------------------------------------------
++ * rs_init() and friends
++ *
++ * rs_init() is called at boot-time to initialize the serial driver.
++ * ---------------------------------------------------------------------
++ */
++
++/*
++ * This routine prints out the appropriate serial driver version
++ * number, and identifies which options were configured into this
++ * driver.
++ */
++static char serial_options[] __initdata =
++#ifdef CONFIG_HUB6
++ " HUB-6"
++#define SERIAL_OPT
++#endif
++#ifdef CONFIG_SERIAL_MANY_PORTS
++ " MANY_PORTS"
++#define SERIAL_OPT
++#endif
++#ifdef CONFIG_SERIAL_MULTIPORT
++ " MULTIPORT"
++#define SERIAL_OPT
++#endif
++#ifdef CONFIG_SERIAL_SHARE_IRQ
++ " SHARE_IRQ"
++#define SERIAL_OPT
++#endif
++#ifdef CONFIG_SERIAL_DETECT_IRQ
++ " DETECT_IRQ"
++#define SERIAL_OPT
++#endif
++#ifdef ENABLE_SERIAL_PCI
++ " SERIAL_PCI"
++#define SERIAL_OPT
++#endif
++#ifdef ENABLE_SERIAL_PNP
++ " ISAPNP"
++#define SERIAL_OPT
++#endif
++#ifdef ENABLE_SERIAL_ACPI
++ " SERIAL_ACPI"
++#define SERIAL_OPT
++#endif
++#ifdef SERIAL_OPT
++ " enabled\n";
++#else
++ " no serial options enabled\n";
++#endif
++#undef SERIAL_OPT
++
++static _INLINE_ void show_serial_version(void)
++{
++ printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name,
++ serial_version, LOCAL_VERSTRING, serial_revdate,
++ serial_options);
++}
++
++/*
++ * This routine detect the IRQ of a serial port by clearing OUT2 when
++ * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at
++ * each time, as long as no other device permanently request the IRQ.
++ * If no IRQ is detected, or multiple IRQ appear, this function returns 0.
++ * The variable "state" and the field "state->port" should not be null.
++ */
++static unsigned detect_uart_irq (struct serial_state * state)
++{
++ int irq;
++ unsigned long irqs;
++ unsigned char save_mcr, save_ier;
++ struct async_struct scr_info; /* serial_{in,out} because HUB6 */
++
++#ifdef CONFIG_SERIAL_MANY_PORTS
++ unsigned char save_ICP=0; /* no warning */
++ unsigned short ICP=0;
++
++ if (state->flags & ASYNC_FOURPORT) {
++ ICP = (state->port & 0xFE0) | 0x01F;
++ save_ICP = inb_p(ICP);
++ outb_p(0x80, ICP);
++ (void) inb_p(ICP);
++ }
++#endif
++ scr_info.magic = SERIAL_MAGIC;
++ scr_info.state = state;
++ scr_info.port = state->port;
++ scr_info.flags = state->flags;
++#ifdef CONFIG_HUB6
++ scr_info.hub6 = state->hub6;
++#endif
++ scr_info.io_type = state->io_type;
++ scr_info.iomem_base = state->iomem_base;
++ scr_info.iomem_reg_shift = state->iomem_reg_shift;
++
++ /* forget possible initially masked and pending IRQ */
++ probe_irq_off(probe_irq_on());
++ save_mcr = serial_inp(&scr_info, UART_MCR);
++ save_ier = serial_inp(&scr_info, UART_IER);
++ serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
++
++ irqs = probe_irq_on();
++ serial_outp(&scr_info, UART_MCR, 0);
++ udelay (10);
++ if (state->flags & ASYNC_FOURPORT) {
++ serial_outp(&scr_info, UART_MCR,
++ UART_MCR_DTR | UART_MCR_RTS);
++ } else {
++ serial_outp(&scr_info, UART_MCR,
++ UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
++ }
++ serial_outp(&scr_info, UART_IER, 0x0f); /* enable all intrs */
++ (void)serial_inp(&scr_info, UART_LSR);
++ (void)serial_inp(&scr_info, UART_RX);
++ (void)serial_inp(&scr_info, UART_IIR);
++ (void)serial_inp(&scr_info, UART_MSR);
++ serial_outp(&scr_info, UART_TX, 0xFF);
++ udelay (20);
++ irq = probe_irq_off(irqs);
++
++ serial_outp(&scr_info, UART_MCR, save_mcr);
++ serial_outp(&scr_info, UART_IER, save_ier);
++#ifdef CONFIG_SERIAL_MANY_PORTS
++ if (state->flags & ASYNC_FOURPORT)
++ outb_p(save_ICP, ICP);
++#endif
++ return (irq > 0)? irq : 0;
++}
++
++/*
++ * 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 async_struct *info)
++{
++ unsigned char old_fcr, old_mcr, old_dll, old_dlm;
++ int count;
++
++ old_fcr = serial_inp(info, UART_FCR);
++ old_mcr = serial_inp(info, UART_MCR);
++ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO |
++ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
++ serial_outp(info, UART_MCR, UART_MCR_LOOP);
++ serial_outp(info, UART_LCR, UART_LCR_DLAB);
++ old_dll = serial_inp(info, UART_DLL);
++ old_dlm = serial_inp(info, UART_DLM);
++ serial_outp(info, UART_DLL, 0x01);
++ serial_outp(info, UART_DLM, 0x00);
++ serial_outp(info, UART_LCR, 0x03);
++ for (count = 0; count < 256; count++)
++ serial_outp(info, UART_TX, count);
++ mdelay(20);
++ for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) &&
++ (count < 256); count++)
++ serial_inp(info, UART_RX);
++ serial_outp(info, UART_FCR, old_fcr);
++ serial_outp(info, UART_MCR, old_mcr);
++ serial_outp(info, UART_LCR, UART_LCR_DLAB);
++ serial_outp(info, UART_DLL, old_dll);
++ serial_outp(info, 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_startech_uarts(struct async_struct *info,
++ struct serial_state *state,
++ unsigned long flags)
++{
++ unsigned char scratch, scratch2, scratch3, scratch4;
++
++ /*
++ * 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)
++ */
++ if (state->type == PORT_16550A) {
++ /*
++ * EFR [4] must be set else this test fails
++ *
++ * This shouldn't be necessary, but Mike Hudson
++ * (Exoray at isys.ca) claims that it's needed for 952
++ * dual UART's (which are not recommended for new designs).
++ */
++ info->ACR = 0;
++ serial_out(info, UART_LCR, 0xBF);
++ serial_out(info, UART_EFR, 0x10);
++ serial_out(info, UART_LCR, 0x00);
++ /* Check for Oxford Semiconductor 16C950 */
++ scratch = serial_icr_read(info, UART_ID1);
++ scratch2 = serial_icr_read(info, UART_ID2);
++ scratch3 = serial_icr_read(info, UART_ID3);
++
++ if (scratch == 0x16 && scratch2 == 0xC9 &&
++ (scratch3 == 0x50 || scratch3 == 0x52 ||
++ scratch3 == 0x54)) {
++ state->type = PORT_16C950;
++ state->revision = serial_icr_read(info, UART_REV) |
++ (scratch3 << 8);
++ return;
++ }
++ }
++
++ /*
++ * We check for a XR16C850 by setting DLL and DLM to 0, and
++ * then reading back DLL and DLM. If DLM reads back 0x10,
++ * then the UART is a XR16C850 and the DLL contains the chip
++ * revision. If DLM reads back 0x14, then the UART is a
++ * XR16C854.
++ *
++ */
++
++ /* Save the DLL and DLM */
++
++ serial_outp(info, UART_LCR, UART_LCR_DLAB);
++ scratch3 = serial_inp(info, UART_DLL);
++ scratch4 = serial_inp(info, UART_DLM);
++
++ serial_outp(info, UART_DLL, 0);
++ serial_outp(info, UART_DLM, 0);
++ scratch2 = serial_inp(info, UART_DLL);
++ scratch = serial_inp(info, UART_DLM);
++ serial_outp(info, UART_LCR, 0);
++
++ if (scratch == 0x10 || scratch == 0x14) {
++ if (scratch == 0x10)
++ state->revision = scratch2;
++ state->type = PORT_16850;
++ return;
++ }
++
++ /* Restore the DLL and DLM */
++
++ serial_outp(info, UART_LCR, UART_LCR_DLAB);
++ serial_outp(info, UART_DLL, scratch3);
++ serial_outp(info, UART_DLM, scratch4);
++ serial_outp(info, UART_LCR, 0);
++ /*
++ * 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(info) == 64)
++ state->type = PORT_16654;
++ else
++ state->type = PORT_16650V2;
++}
++
++/*
++ * 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 serial_state * state)
++{
++ unsigned char status1, status2, scratch, scratch2, scratch3;
++ unsigned char save_lcr, save_mcr;
++ struct async_struct *info, scr_info;
++ unsigned long flags;
++
++ state->type = PORT_UNKNOWN;
++
++#ifdef SERIAL_DEBUG_AUTOCONF
++ printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line,
++ state->port, (unsigned) state->iomem_base);
++#endif
++
++ if (!CONFIGURED_SERIAL_PORT(state))
++ return;
++
++ info = &scr_info; /* This is just for serial_{in,out} */
++
++ info->magic = SERIAL_MAGIC;
++ info->state = state;
++ info->port = state->port;
++ info->flags = state->flags;
++#ifdef CONFIG_HUB6
++ info->hub6 = state->hub6;
++#endif
++ info->io_type = state->io_type;
++ info->iomem_base = state->iomem_base;
++ info->iomem_reg_shift = state->iomem_reg_shift;
++
++ save_flags(flags); cli();
++
++ if (!(state->flags & ASYNC_BUGGY_UART) &&
++ !state->iomem_base) {
++ /*
++ * 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.
++ */
++ scratch = serial_inp(info, UART_IER);
++ serial_outp(info, UART_IER, 0);
++#ifdef __i386__
++ outb(0xff, 0x080);
++#endif
++ scratch2 = serial_inp(info, UART_IER);
++ serial_outp(info, UART_IER, 0x0F);
++#ifdef __i386__
++ outb(0, 0x080);
++#endif
++ scratch3 = serial_inp(info, UART_IER);
++ serial_outp(info, UART_IER, scratch);
++ if (scratch2 || scratch3 != 0x0F) {
++#ifdef SERIAL_DEBUG_AUTOCONF
++ printk("serial: ttyS%d: simple autoconfig failed "
++ "(%02x, %02x)\n", state->line,
++ scratch2, scratch3);
++#endif
++ restore_flags(flags);
++ return; /* We failed; there's nothing here */
++ }
++ }
++
++ save_mcr = serial_in(info, UART_MCR);
++ save_lcr = serial_in(info, 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 (!(state->flags & ASYNC_SKIP_TEST)) {
++ serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
++ status1 = serial_inp(info, UART_MSR) & 0xF0;
++ serial_outp(info, UART_MCR, save_mcr);
++ if (status1 != 0x90) {
++#ifdef SERIAL_DEBUG_AUTOCONF
++ printk("serial: ttyS%d: no UART loopback failed\n",
++ state->line);
++#endif
++ restore_flags(flags);
++ return;
++ }
++ }
++ serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */
++ serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */
++ serial_outp(info, UART_LCR, 0);
++ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
++ scratch = serial_in(info, UART_IIR) >> 6;
++ switch (scratch) {
++ case 0:
++ state->type = PORT_16450;
++ break;
++ case 1:
++ state->type = PORT_UNKNOWN;
++ break;
++ case 2:
++ state->type = PORT_16550;
++ break;
++ case 3:
++ state->type = PORT_16550A;
++ break;
++ }
++ if (state->type == PORT_16550A) {
++ /* Check for Startech UART's */
++ serial_outp(info, UART_LCR, UART_LCR_DLAB);
++ if (serial_in(info, UART_EFR) == 0) {
++ serial_outp(info, UART_EFR, 0xA8);
++ if (serial_in(info, UART_EFR) == 0) {
++ /* We are a NS16552D/Motorola
++ * 8xxx DUART, stop. */
++ goto out;
++ }
++ state->type = PORT_16650;
++ serial_outp(info, UART_EFR, 0);
++ } else {
++ serial_outp(info, UART_LCR, 0xBF);
++ if (serial_in(info, UART_EFR) == 0)
++ autoconfig_startech_uarts(info, state, flags);
++ }
++ }
++ if (state->type == PORT_16550A) {
++ /* Check for TI 16750 */
++ serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB);
++ serial_outp(info, UART_FCR,
++ UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
++ scratch = serial_in(info, UART_IIR) >> 5;
++ if (scratch == 7) {
++ /*
++ * If this is a 16750, and not a cheap UART
++ * clone, then it should only go into 64 byte
++ * mode if the UART_FCR7_64BYTE bit was set
++ * while UART_LCR_DLAB was latched.
++ */
++ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
++ serial_outp(info, UART_LCR, 0);
++ serial_outp(info, UART_FCR,
++ UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
++ scratch = serial_in(info, UART_IIR) >> 5;
++ if (scratch == 6)
++ state->type = PORT_16750;
++ }
++ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
++ }
++#if defined(CONFIG_SERIAL_RSA) && defined(MODULE)
++ if (state->type == PORT_16550A) {
++ int i;
++
++ for (i = 0 ; i < PORT_RSA_MAX ; ++i) {
++ if (!probe_rsa[i] && !force_rsa[i])
++ break;
++ if (((probe_rsa[i] != state->port) ||
++ check_region(state->port + UART_RSA_BASE, 16)) &&
++ (force_rsa[i] != state->port))
++ continue;
++ if (!enable_rsa(info))
++ continue;
++ state->type = PORT_RSA;
++ state->baud_base = SERIAL_RSA_BAUD_BASE;
++ break;
++ }
++ }
++#endif
++out:
++ serial_outp(info, UART_LCR, save_lcr);
++ if (state->type == PORT_16450) {
++ scratch = serial_in(info, UART_SCR);
++ serial_outp(info, UART_SCR, 0xa5);
++ status1 = serial_in(info, UART_SCR);
++ serial_outp(info, UART_SCR, 0x5a);
++ status2 = serial_in(info, UART_SCR);
++ serial_outp(info, UART_SCR, scratch);
++
++ if ((status1 != 0xa5) || (status2 != 0x5a))
++ state->type = PORT_8250;
++ }
++ state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size;
++
++ if (state->type == PORT_UNKNOWN) {
++ restore_flags(flags);
++ return;
++ }
++
++ if (info->port) {
++#ifdef CONFIG_SERIAL_RSA
++ if (state->type == PORT_RSA)
++ request_region(info->port + UART_RSA_BASE, 16,
++ "serial_rsa(auto)");
++ else
++#endif
++ request_region(info->port,8,"serial(auto)");
++ }
++
++ /*
++ * Reset the UART.
++ */
++#ifdef CONFIG_SERIAL_RSA
++ if (state->type == PORT_RSA)
++ serial_outp(info, UART_RSA_FRR, 0);
++#endif
++ serial_outp(info, UART_MCR, save_mcr);
++ serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO |
++ UART_FCR_CLEAR_RCVR |
++ UART_FCR_CLEAR_XMIT));
++ serial_outp(info, UART_FCR, 0);
++ (void)serial_in(info, UART_RX);
++ serial_outp(info, UART_IER, 0);
++
++ restore_flags(flags);
++}
++
++int register_serial(struct serial_struct *req);
++void unregister_serial(int line);
++
++#if (LINUX_VERSION_CODE > 0x20100)
++EXPORT_SYMBOL(register_serial);
++EXPORT_SYMBOL(unregister_serial);
++#else
++static struct symbol_table serial_syms = {
++#include <linux/symtab_begin.h>
++ X(register_serial),
++ X(unregister_serial),
++#include <linux/symtab_end.h>
++};
++#endif
++
++
++#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)
++
++static void __devinit printk_pnp_dev_id(unsigned short vendor,
++ unsigned short device)
++{
++ printk("%c%c%c%x%x%x%x",
++ 'A' + ((vendor >> 2) & 0x3f) - 1,
++ 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1,
++ 'A' + ((vendor >> 8) & 0x1f) - 1,
++ (device >> 4) & 0x0f,
++ device & 0x0f,
++ (device >> 12) & 0x0f,
++ (device >> 8) & 0x0f);
++}
++
++static _INLINE_ 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);
++ }
++
++ }
++
++ /* HP's Diva chip puts the 4th/5th serial port further out, and
++ * some serial ports are supposed to be hidden on certain models.
++ */
++ if (dev->vendor == PCI_VENDOR_ID_HP &&
++ dev->device == PCI_DEVICE_ID_HP_SAS) {
++ switch (dev->subsystem_device) {
++ case 0x104B: /* Maestro */
++ if (idx == 3) idx++;
++ break;
++ case 0x1282: /* Everest / Longs Peak */
++ if (idx > 0) idx++;
++ if (idx > 2) idx++;
++ break;
++ }
++ if (idx > 2) {
++ offset = 0x18;
++ }
++ }
++
++ 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);
++}
++
++/*
++ * Common enabler code shared by both PCI and ISAPNP probes
++ */
++static void __devinit start_pci_pnp_board(struct pci_dev *dev,
++ struct pci_board *board)
++{
++ int k, line;
++ struct serial_struct serial_req;
++ int base_baud;
++
++ if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) {
++ printk("serial: PNP device '");
++ printk_pnp_dev_id(dev->vendor, dev->device);
++ printk("' prepare failed\n");
++ return;
++ }
++
++ if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) {
++ printk("serial: PNP device '");
++ printk_pnp_dev_id(dev->vendor, dev->device);
++ printk("' activate failed\n");
++ return;
++ }
++
++ /*
++ * Run the initialization function, if any
++ */
++ if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0))
++ return;
++
++ /*
++ * Register the serial board in the array if we need to
++ * shutdown the board on a module unload or card removal
++ */
++ if (DEACTIVATE_FUNC(dev) || board->init_fn) {
++ for (k=0; k < NR_PCI_BOARDS; k++)
++ if (serial_pci_board[k].dev == 0)
++ break;
++ if (k >= NR_PCI_BOARDS)
++ return;
++ serial_pci_board[k].board = *board;
++ serial_pci_board[k].dev = dev;
++ }
++
++ 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;
++ serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
++#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
++ line = register_serial(&serial_req);
++ if (line < 0)
++ break;
++ rs_table[line].baud_base = base_baud;
++ rs_table[line].dev = dev;
++ }
++}
++#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */
++
++#ifdef ENABLE_SERIAL_PCI
++/*
++ * 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 <pazke at mail.tp.ru>, 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.
++ *
++ * SIIG initialization functions exported for use by parport_serial.c module.
++ */
++
++#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)
++
++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;
++}
++EXPORT_SYMBOL(pci_siig10x_fn);
++
++#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)
++
++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;
++}
++EXPORT_SYMBOL(pci_siig20x_fn);
++
++/* 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);
++
++#ifdef SERIAL_DEBUG_PCI
++ printk(KERN_DEBUG " Subsystem ID %lx (intel 960)\n",
++ (unsigned long) board->subdevice);
++#endif
++ /* 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;
++}
++
++/*
++ * HP's Remote Management Console. The Diva chip came in several
++ * different versions. N-class, L2000 and A500 have two Diva chips, each
++ * with 3 UARTs (the third UART on the second chip is unused). Superdome
++ * and Keystone have one Diva chip with 3 UARTs. Some later machines have
++ * one Diva chip, but it has been expanded to 5 UARTs.
++ */
++static int __devinit
++pci_hp_diva(struct pci_dev *dev, struct pci_board *board, int enable)
++{
++ if (!enable)
++ return 0;
++
++ switch (dev->subsystem_device) {
++ case 0x1049: /* Prelude Diva 1 */
++ case 0x1223: /* Superdome */
++ case 0x1226: /* Keystone */
++ case 0x1282: /* Everest / Longs Peak */
++ board->num_ports = 3;
++ break;
++ case 0x104A: /* Prelude Diva 2 */
++ board->num_ports = 2;
++ break;
++ case 0x104B: /* Maestro */
++ board->num_ports = 4;
++ break;
++ case 0x1227: /* Powerbar */
++ board->num_ports = 1;
++ break;
++ }
++
++ 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_b0_bt_2_921600,
++
++ 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_1_115200,
++ 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,
++ pbn_hp_diva,
++#ifdef CONFIG_DDB5074
++ pbn_nec_nile4,
++#endif
++
++ pbn_dci_pccom4,
++ pbn_dci_pccom8,
++
++ 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_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b0_bt_2_921600 */
++
++ { 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, 1, 115200 }, /* pbn_b2_1_115200 */
++ { 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 },
++ { SPCI_FL_BASE0, 5, 115200, 8, 0, pci_hp_diva, 0}, /* pbn_hp_diva */
++#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
++
++ {SPCI_FL_BASE3, 4, 115200, 8}, /* pbn_dci_pccom4 */
++ {SPCI_FL_BASE3, 8, 115200, 8}, /* pbn_dci_pccom8 */
++
++ { 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;
++}
++
++static int __devinit serial_init_one(struct pci_dev *dev,
++ const struct pci_device_id *ent)
++{
++ struct pci_board *board, tmp;
++ int rc;
++
++ 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 (%04x,%04x,%04x,%04x)\n"
++ "and the manufacturer and name of "
++ "serial board or modem board\n"
++ "to serial-pci-info at lists.sourceforge.net.\n",
++ dev->vendor, dev->device,
++ pci_get_subvendor(dev), pci_get_subdevice(dev));
++ }
++
++ start_pci_pnp_board(dev, board);
++
++ return 0;
++}
++
++static void __devexit serial_remove_one(struct pci_dev *dev)
++{
++ int i;
++
++ /*
++ * Iterate through all of the ports finding those that belong
++ * to this PCI device.
++ */
++ for(i = 0; i < NR_PORTS; i++) {
++ if (rs_table[i].dev != dev)
++ continue;
++ unregister_serial(i);
++ rs_table[i].dev = 0;
++ }
++ /*
++ * Now execute any board-specific shutdown procedure
++ */
++ for (i=0; i < NR_PCI_BOARDS; i++) {
++ struct pci_board_inst *brd = &serial_pci_board[i];
++
++ if (serial_pci_board[i].dev != dev)
++ continue;
++ if (brd->board.init_fn)
++ (brd->board.init_fn)(brd->dev, &brd->board, 0);
++ if (DEACTIVATE_FUNC(brd->dev))
++ (DEACTIVATE_FUNC(brd->dev))(brd->dev);
++ serial_pci_board[i].dev = 0;
++ }
++}
++
++
++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 at 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 at 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_bt_2_921600 },
++
++ /* Digitan DS560-558, from jimd at 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_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_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_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_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 at 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 at 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 <arh at 01019freenet.de> */
++ { 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 },
++
++ /* HP Diva card */
++ { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_SAS,
++ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++ pbn_hp_diva },
++ { PCI_VENDOR_ID_HP, 0x1290,
++ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++ pbn_b2_1_115200 },
++
++#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
++
++ { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,
++ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++ pbn_dci_pccom4 },
++ { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
++ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++ pbn_dci_pccom8 },
++
++ { 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, },
++ { 0, }
++};
++
++MODULE_DEVICE_TABLE(pci, serial_pci_tbl);
++
++static struct pci_driver serial_pci_driver = {
++ name: "serial",
++ probe: serial_init_one,
++ remove: __devexit_p(serial_remove_one),
++ id_table: serial_pci_tbl,
++};
++
++
++/*
++ * Query PCI space for known serial boards
++ * If found, add them to the PCI device space in rs_table[]
++ *
++ * Accept a maximum of eight boards
++ *
++ */
++static void __devinit probe_serial_pci(void)
++{
++#ifdef SERIAL_DEBUG_PCI
++ printk(KERN_DEBUG "Entered probe_serial_pci()\n");
++#endif
++
++ /* Register call PCI serial devices. Null out
++ * the driver name upon failure, as a signal
++ * not to attempt to unregister the driver later
++ */
++ if (pci_module_init (&serial_pci_driver) != 0)
++ serial_pci_driver.name = "";
++
++#ifdef SERIAL_DEBUG_PCI
++ printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n");
++#endif
++ return;
++}
++
++#endif /* ENABLE_SERIAL_PCI */
++
++#ifdef ENABLE_SERIAL_PNP
++
++struct pnp_board {
++ unsigned short vendor;
++ unsigned short device;
++};
++
++static struct pnp_board pnp_devices[] __devinitdata = {
++ /* Archtek America Corp. */
++ /* Archtek SmartLink Modem 3334BT Plug & Play */
++ { ISAPNP_VENDOR('A', 'A', 'C'), ISAPNP_DEVICE(0x000F) },
++ /* Anchor Datacomm BV */
++ /* SXPro 144 External Data Fax Modem Plug & Play */
++ { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0001) },
++ /* SXPro 288 External Data Fax Modem Plug & Play */
++ { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0002) },
++ /* Rockwell 56K ACF II Fax+Data+Voice Modem */
++ { ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021) },
++ /* AZT3005 PnP SOUND DEVICE */
++ { ISAPNP_VENDOR('A', 'Z', 'T'), ISAPNP_DEVICE(0x4001) },
++ /* Best Data Products Inc. Smart One 336F PnP Modem */
++ { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) },
++ /* Boca Research */
++ /* Boca Complete Ofc Communicator 14.4 Data-FAX */
++ { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) },
++ /* Boca Research 33,600 ACF Modem */
++ { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400) },
++ /* Boca 33.6 Kbps Internal FD34FSVD */
++ { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x3400) },
++ /* Boca 33.6 Kbps Internal FD34FSVD */
++ { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) },
++ /* Best Data Products Inc. Smart One 336F PnP Modem */
++ { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) },
++ /* Computer Peripherals Inc */
++ /* EuroViVa CommCenter-33.6 SP PnP */
++ { ISAPNP_VENDOR('C', 'P', 'I'), ISAPNP_DEVICE(0x4050) },
++ /* Creative Labs */
++ /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */
++ { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3001) },
++ /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */
++ { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3011) },
++ /* Creative */
++ /* Creative Modem Blaster Flash56 DI5601-1 */
++ { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x1032) },
++ /* Creative Modem Blaster V.90 DI5660 */
++ { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x2001) },
++ /* FUJITSU */
++ /* Fujitsu 33600 PnP-I2 R Plug & Play */
++ { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0202) },
++ /* Fujitsu FMV-FX431 Plug & Play */
++ { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0205) },
++ /* Fujitsu 33600 PnP-I4 R Plug & Play */
++ { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0206) },
++ /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */
++ { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0209) },
++ /* Archtek America Corp. */
++ /* Archtek SmartLink Modem 3334BT Plug & Play */
++ { ISAPNP_VENDOR('G', 'V', 'C'), ISAPNP_DEVICE(0x000F) },
++ /* Hayes */
++ /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */
++ { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x0001) },
++ /* Hayes Optima 336 V.34 + FAX + Voice PnP */
++ { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000C) },
++ /* Hayes Optima 336B V.34 + FAX + Voice PnP */
++ { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000D) },
++ /* Hayes Accura 56K Ext Fax Modem PnP */
++ { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5670) },
++ /* Hayes Accura 56K Ext Fax Modem PnP */
++ { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5674) },
++ /* Hayes Accura 56K Fax Modem PnP */
++ { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5675) },
++ /* Hayes 288, V.34 + FAX */
++ { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF000) },
++ /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */
++ { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF001) },
++ /* IBM */
++ /* IBM Thinkpad 701 Internal Modem Voice */
++ { ISAPNP_VENDOR('I', 'B', 'M'), ISAPNP_DEVICE(0x0033) },
++ /* Intertex */
++ /* Intertex 28k8 33k6 Voice EXT PnP */
++ { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC801) },
++ /* Intertex 33k6 56k Voice EXT PnP */
++ { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC901) },
++ /* Intertex 28k8 33k6 Voice SP EXT PnP */
++ { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD801) },
++ /* Intertex 33k6 56k Voice SP EXT PnP */
++ { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD901) },
++ /* Intertex 28k8 33k6 Voice SP INT PnP */
++ { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF401) },
++ /* Intertex 28k8 33k6 Voice SP EXT PnP */
++ { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF801) },
++ /* Intertex 33k6 56k Voice SP EXT PnP */
++ { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF901) },
++ /* Kortex International */
++ /* KORTEX 28800 Externe PnP */
++ { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0x4522) },
++ /* KXPro 33.6 Vocal ASVD PnP */
++ { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0xF661) },
++ /* Lasat */
++ /* LASAT Internet 33600 PnP */
++ { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4040) },
++ /* Lasat Safire 560 PnP */
++ { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4540) },
++ /* Lasat Safire 336 PnP */
++ { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x5440) },
++ /* Microcom, Inc. */
++ /* Microcom TravelPorte FAST V.34 Plug & Play */
++ { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x281) },
++ /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */
++ { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0336) },
++ /* Microcom DeskPorte FAST EP 28.8 Plug & Play */
++ { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0339) },
++ /* Microcom DeskPorte 28.8P Plug & Play */
++ { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0342) },
++ /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
++ { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0500) },
++ /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
++ { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0501) },
++ /* Microcom DeskPorte 28.8S Internal Plug & Play */
++ { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0502) },
++ /* Motorola */
++ /* Motorola BitSURFR Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1105) },
++ /* Motorola TA210 Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1111) },
++ /* Motorola HMTA 200 (ISDN) Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1114) },
++ /* Motorola BitSURFR Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1115) },
++ /* Motorola Lifestyle 28.8 Internal */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1190) },
++ /* Motorola V.3400 Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1501) },
++ /* Motorola Lifestyle 28.8 V.34 Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1502) },
++ /* Motorola Power 28.8 V.34 Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1505) },
++ /* Motorola ModemSURFR External 28.8 Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1509) },
++ /* Motorola Premier 33.6 Desktop Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150A) },
++ /* Motorola VoiceSURFR 56K External PnP */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150F) },
++ /* Motorola ModemSURFR 56K External PnP */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1510) },
++ /* Motorola ModemSURFR 56K Internal PnP */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1550) },
++ /* Motorola ModemSURFR Internal 28.8 Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1560) },
++ /* Motorola Premier 33.6 Internal Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1580) },
++ /* Motorola OnlineSURFR 28.8 Internal Plug & Play */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15B0) },
++ /* Motorola VoiceSURFR 56K Internal PnP */
++ { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15F0) },
++ /* Com 1 */
++ /* Deskline K56 Phone System PnP */
++ { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00A1) },
++ /* PC Rider K56 Phone System PnP */
++ { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00F2) },
++ /* Pace 56 Voice Internal Plug & Play Modem */
++ { ISAPNP_VENDOR('P', 'M', 'C'), ISAPNP_DEVICE(0x2430) },
++ /* Generic */
++ /* Generic standard PC COM port */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0500) },
++ /* Generic 16550A-compatible COM port */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0501) },
++ /* Compaq 14400 Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000) },
++ /* Compaq 2400/9600 Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC001) },
++ /* Dial-Up Networking Serial Cable between 2 PCs */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC031) },
++ /* Dial-Up Networking Parallel Cable between 2 PCs */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC032) },
++ /* Standard 9600 bps Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC100) },
++ /* Standard 14400 bps Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC101) },
++ /* Standard 28800 bps Modem*/
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC102) },
++ /* Standard Modem*/
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC103) },
++ /* Standard 9600 bps Modem*/
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC104) },
++ /* Standard 14400 bps Modem*/
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC105) },
++ /* Standard 28800 bps Modem*/
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC106) },
++ /* Standard Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC107) },
++ /* Standard 9600 bps Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC108) },
++ /* Standard 14400 bps Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC109) },
++ /* Standard 28800 bps Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10A) },
++ /* Standard Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10B) },
++ /* Standard 9600 bps Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10C) },
++ /* Standard 14400 bps Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10D) },
++ /* Standard 28800 bps Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10E) },
++ /* Standard Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10F) },
++ /* Standard PCMCIA Card Modem */
++ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x2000) },
++ /* Rockwell */
++ /* Modular Technology */
++ /* Rockwell 33.6 DPF Internal PnP */
++ /* Modular Technology 33.6 Internal PnP */
++ { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0030) },
++ /* Kortex International */
++ /* KORTEX 14400 Externe PnP */
++ { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0100) },
++ /* Viking Components, Inc */
++ /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */
++ { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x4920) },
++ /* Rockwell */
++ /* British Telecom */
++ /* Modular Technology */
++ /* Rockwell 33.6 DPF External PnP */
++ /* BT Prologue 33.6 External PnP */
++ /* Modular Technology 33.6 External PnP */
++ { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x00A0) },
++ /* Viking 56K FAX INT */
++ { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x0262) },
++ /* SupraExpress 28.8 Data/Fax PnP modem */
++ { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310) },
++ /* SupraExpress 33.6 Data/Fax PnP modem */
++ { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1421) },
++ /* SupraExpress 33.6 Data/Fax PnP modem */
++ { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1590) },
++ /* SupraExpress 33.6 Data/Fax PnP modem */
++ { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1760) },
++ /* Phoebe Micro */
++ /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */
++ { ISAPNP_VENDOR('T', 'E', 'X'), ISAPNP_DEVICE(0x0011) },
++ /* Archtek America Corp. */
++ /* Archtek SmartLink Modem 3334BT Plug & Play */
++ { ISAPNP_VENDOR('U', 'A', 'C'), ISAPNP_DEVICE(0x000F) },
++ /* 3Com Corp. */
++ /* Gateway Telepath IIvi 33.6 */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0000) },
++ /* Sportster Vi 14.4 PnP FAX Voicemail */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0004) },
++ /* U.S. Robotics 33.6K Voice INT PnP */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0006) },
++ /* U.S. Robotics 33.6K Voice EXT PnP */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0007) },
++ /* U.S. Robotics 33.6K Voice INT PnP */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2002) },
++ /* U.S. Robotics 56K Voice INT PnP */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2070) },
++ /* U.S. Robotics 56K Voice EXT PnP */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2080) },
++ /* U.S. Robotics 56K FAX INT */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031) },
++ /* U.S. Robotics 56K Voice INT PnP */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3070) },
++ /* U.S. Robotics 56K Voice EXT PnP */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3080) },
++ /* U.S. Robotics 56K Voice INT PnP */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3090) },
++ /* U.S. Robotics 56K Message */
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9100) },
++ /* U.S. Robotics 56K FAX EXT PnP*/
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9160) },
++ /* U.S. Robotics 56K FAX INT PnP*/
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9170) },
++ /* U.S. Robotics 56K Voice EXT PnP*/
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9180) },
++ /* U.S. Robotics 56K Voice INT PnP*/
++ { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9190) },
++ { 0, }
++};
++
++static inline void 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 = modem_names;
++
++ while (*tmp) {
++ if (strstr(name, *tmp))
++ return 1;
++ tmp++;
++ }
++ return 0;
++}
++
++static inline int 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 _INLINE_ serial_pnp_guess_board(struct pci_dev *dev,
++ struct pci_board *board)
++{
++ 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 1;
++
++ if (!res || res->next)
++ return 1;
++
++ 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 1;
++}
++
++static void __devinit probe_serial_pnp(void)
++{
++ struct pci_dev *dev = NULL;
++ struct pnp_board *pnp_board;
++ struct pci_board board;
++
++#ifdef SERIAL_DEBUG_PNP
++ printk("Entered probe_serial_pnp()\n");
++#endif
++ if (!isapnp_present()) {
++#ifdef SERIAL_DEBUG_PNP
++ printk("Leaving probe_serial_pnp() (no isapnp)\n");
++#endif
++ return;
++ }
++
++ isapnp_for_each_dev(dev) {
++ if (dev->active)
++ continue;
++
++ memset(&board, 0, sizeof(board));
++ board.flags = SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT;
++ board.num_ports = 1;
++ board.base_baud = 115200;
++
++ for (pnp_board = pnp_devices; pnp_board->vendor; pnp_board++)
++ if ((dev->vendor == pnp_board->vendor) &&
++ (dev->device == pnp_board->device))
++ break;
++
++ if (pnp_board->vendor) {
++ /* Special case that's more efficient to hardcode */
++ if ((pnp_board->vendor == ISAPNP_VENDOR('A', 'K', 'Y') &&
++ pnp_board->device == ISAPNP_DEVICE(0x1021)))
++ board.flags |= SPCI_FL_NO_SHIRQ;
++ } else {
++ if (serial_pnp_guess_board(dev, &board))
++ continue;
++ }
++
++ if (board.flags & SPCI_FL_NO_SHIRQ)
++ avoid_irq_share(dev);
++ start_pci_pnp_board(dev, &board);
++ }
++
++#ifdef SERIAL_DEBUG_PNP
++ printk("Leaving probe_serial_pnp() (probe finished)\n");
++#endif
++ return;
++}
++
++#endif /* ENABLE_SERIAL_PNP */
++
++/*
++ * The serial driver boot-time initialization code!
++ */
++static int __init rs_init(void)
++{
++ int i;
++ struct serial_state * state;
++
++ init_bh(SERIAL_BH, do_serial_bh);
++ init_timer(&serial_timer);
++ serial_timer.function = rs_timer;
++ mod_timer(&serial_timer, jiffies + RS_STROBE_TIME);
++
++ for (i = 0; i < NR_IRQS; i++) {
++ IRQ_ports[i] = 0;
++ IRQ_timeout[i] = 0;
++#ifdef CONFIG_SERIAL_MULTIPORT
++ memset(&rs_multiport[i], 0,
++ sizeof(struct rs_multiport_struct));
++#endif
++ }
++#ifdef CONFIG_SERIAL_CONSOLE
++ /*
++ * The interrupt of the serial console port
++ * can't be shared.
++ */
++ if (sercons.flags & CON_CONSDEV) {
++ for(i = 0; i < NR_PORTS; i++)
++ if (i != sercons.index &&
++ rs_table[i].irq == rs_table[sercons.index].irq)
++ rs_table[i].irq = 0;
++ }
++#endif
++ show_serial_version();
++
++ /* Initialize the tty_driver structure */
++
++ memset(&serial_driver, 0, sizeof(struct tty_driver));
++ serial_driver.magic = TTY_DRIVER_MAGIC;
++#if (LINUX_VERSION_CODE > 0x20100)
++ serial_driver.driver_name = "serial";
++#endif
++#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))
++ serial_driver.name = "tts/%d";
++#else
++ serial_driver.name = "ttyS";
++#endif
++ serial_driver.major = TTY_MAJOR;
++ serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET;
++ serial_driver.name_base = SERIAL_DEV_OFFSET;
++ serial_driver.num = NR_PORTS;
++ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
++ serial_driver.subtype = SERIAL_TYPE_NORMAL;
++ serial_driver.init_termios = tty_std_termios;
++ serial_driver.init_termios.c_cflag =
++ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
++ serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
++ serial_driver.refcount = &serial_refcount;
++ serial_driver.table = serial_table;
++ serial_driver.termios = serial_termios;
++ serial_driver.termios_locked = serial_termios_locked;
++
++ serial_driver.open = rs_open;
++ serial_driver.close = rs_close;
++ serial_driver.write = rs_write;
++ serial_driver.put_char = rs_put_char;
++ serial_driver.flush_chars = rs_flush_chars;
++ serial_driver.write_room = rs_write_room;
++ serial_driver.chars_in_buffer = rs_chars_in_buffer;
++ serial_driver.flush_buffer = rs_flush_buffer;
++ serial_driver.ioctl = rs_ioctl;
++ serial_driver.throttle = rs_throttle;
++ serial_driver.unthrottle = rs_unthrottle;
++ serial_driver.set_termios = rs_set_termios;
++ serial_driver.stop = rs_stop;
++ serial_driver.start = rs_start;
++ serial_driver.hangup = rs_hangup;
++#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */
++ serial_driver.break_ctl = rs_break;
++#endif
++#if (LINUX_VERSION_CODE >= 131343)
++ serial_driver.send_xchar = rs_send_xchar;
++ serial_driver.wait_until_sent = rs_wait_until_sent;
++ serial_driver.read_proc = rs_read_proc;
++#endif
++
++ /*
++ * The callout device is just like normal device except for
++ * major number and the subtype code.
++ */
++ callout_driver = serial_driver;
++#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))
++ callout_driver.name = "cua/%d";
++#else
++ callout_driver.name = "cua";
++#endif
++ callout_driver.major = TTYAUX_MAJOR;
++ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
++#if (LINUX_VERSION_CODE >= 131343)
++ callout_driver.read_proc = 0;
++ callout_driver.proc_entry = 0;
++#endif
++
++ if (tty_register_driver(&serial_driver))
++ panic("Couldn't register serial driver\n");
++ if (tty_register_driver(&callout_driver))
++ panic("Couldn't register callout driver\n");
++
++ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
++ state->magic = SSTATE_MAGIC;
++ state->line = i;
++ state->type = PORT_UNKNOWN;
++ state->custom_divisor = 0;
++ state->close_delay = 5*HZ/10;
++ state->closing_wait = 30*HZ;
++ state->callout_termios = callout_driver.init_termios;
++ state->normal_termios = serial_driver.init_termios;
++ state->icount.cts = state->icount.dsr =
++ state->icount.rng = state->icount.dcd = 0;
++ state->icount.rx = state->icount.tx = 0;
++ state->icount.frame = state->icount.parity = 0;
++ state->icount.overrun = state->icount.brk = 0;
++ state->irq = irq_cannonicalize(state->irq);
++ if (state->hub6)
++ state->io_type = SERIAL_IO_HUB6;
++ if (state->port && check_region(state->port,8))
++ continue;
++#ifdef CONFIG_MCA
++ if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus)
++ continue;
++#endif
++ if (state->flags & ASYNC_BOOT_AUTOCONF)
++ autoconfig(state);
++ }
++ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
++ if (state->type == PORT_UNKNOWN)
++ continue;
++ if ( (state->flags & ASYNC_BOOT_AUTOCONF)
++ && (state->flags & ASYNC_AUTO_IRQ)
++ && (state->port != 0 || state->iomem_base != 0))
++ state->irq = detect_uart_irq(state);
++ if (state->io_type == SERIAL_IO_MEM) {
++ printk(KERN_INFO"ttyS%02d%s at 0x%p (irq = %d) is a %s\n",
++ state->line + SERIAL_DEV_OFFSET,
++ (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
++ state->iomem_base, state->irq,
++ uart_config[state->type].name);
++ }
++ else {
++ printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n",
++ state->line + SERIAL_DEV_OFFSET,
++ (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
++ state->port, state->irq,
++ uart_config[state->type].name);
++ }
++ tty_register_devfs(&serial_driver, 0,
++ serial_driver.minor_start + state->line);
++ tty_register_devfs(&callout_driver, 0,
++ callout_driver.minor_start + state->line);
++ }
++#ifdef ENABLE_SERIAL_PCI
++ probe_serial_pci();
++#endif
++#ifdef ENABLE_SERIAL_PNP
++ probe_serial_pnp();
++#endif
++ return 0;
++}
++
++/*
++ * This is for use by architectures that know their serial console
++ * attributes only at run time. Not to be invoked after rs_init().
++ */
++int __init early_serial_setup(struct serial_struct *req)
++{
++ int i = req->line;
++
++ if (i >= NR_IRQS)
++ return(-ENOENT);
++ rs_table[i].magic = 0;
++ rs_table[i].baud_base = req->baud_base;
++ rs_table[i].port = req->port;
++ if (HIGH_BITS_OFFSET)
++ rs_table[i].port += (unsigned long) req->port_high <<
++ HIGH_BITS_OFFSET;
++ rs_table[i].irq = req->irq;
++ rs_table[i].flags = req->flags;
++ rs_table[i].close_delay = req->close_delay;
++ rs_table[i].io_type = req->io_type;
++ rs_table[i].hub6 = req->hub6;
++ rs_table[i].iomem_base = req->iomem_base;
++ rs_table[i].iomem_reg_shift = req->iomem_reg_shift;
++ rs_table[i].type = req->type;
++ rs_table[i].xmit_fifo_size = req->xmit_fifo_size;
++ rs_table[i].custom_divisor = req->custom_divisor;
++ rs_table[i].closing_wait = req->closing_wait;
++ return(0);
++}
++
++/*
++ * register_serial and unregister_serial allows for 16x50 serial ports to be
++ * configured at run-time, to support PCMCIA modems.
++ */
++
++/**
++ * 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 neccessary 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)
++{
++ int i;
++ unsigned long flags;
++ struct serial_state *state;
++ struct async_struct *info;
++ unsigned long port;
++
++ port = req->port;
++ if (HIGH_BITS_OFFSET)
++ port += (unsigned long) req->port_high << HIGH_BITS_OFFSET;
++
++ save_flags(flags); cli();
++ for (i = 0; i < NR_PORTS; i++) {
++ if ((rs_table[i].port == port) &&
++ (rs_table[i].iomem_base == req->iomem_base))
++ break;
++ }
++#ifdef __i386__
++ if (i == NR_PORTS) {
++ for (i = 4; i < NR_PORTS; i++)
++ if ((rs_table[i].type == PORT_UNKNOWN) &&
++ (rs_table[i].count == 0))
++ break;
++ }
++#endif
++ if (i == NR_PORTS) {
++ for (i = 0; i < NR_PORTS; i++)
++ if ((rs_table[i].type == PORT_UNKNOWN) &&
++ (rs_table[i].count == 0))
++ break;
++ }
++ if (i == NR_PORTS) {
++ restore_flags(flags);
++ return -1;
++ }
++ state = &rs_table[i];
++ if (rs_table[i].count) {
++ restore_flags(flags);
++ printk("Couldn't configure serial #%d (port=%ld,irq=%d): "
++ "device already open\n", i, port, req->irq);
++ return -1;
++ }
++ state->irq = req->irq;
++ state->port = port;
++ state->flags = req->flags;
++ state->io_type = req->io_type;
++ state->iomem_base = req->iomem_base;
++ state->iomem_reg_shift = req->iomem_reg_shift;
++ if (req->baud_base)
++ state->baud_base = req->baud_base;
++ if ((info = state->info) != NULL) {
++ info->port = port;
++ info->flags = req->flags;
++ info->io_type = req->io_type;
++ info->iomem_base = req->iomem_base;
++ info->iomem_reg_shift = req->iomem_reg_shift;
++ }
++ autoconfig(state);
++ if (state->type == PORT_UNKNOWN) {
++ restore_flags(flags);
++ printk("register_serial(): autoconfig failed\n");
++ return -1;
++ }
++ restore_flags(flags);
++
++ if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))
++ state->irq = detect_uart_irq(state);
++
++ printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
++ state->line + SERIAL_DEV_OFFSET,
++ state->iomem_base ? "iomem" : "port",
++ state->iomem_base ? (unsigned long)state->iomem_base :
++ state->port, state->irq, uart_config[state->type].name);
++ tty_register_devfs(&serial_driver, 0,
++ serial_driver.minor_start + state->line);
++ tty_register_devfs(&callout_driver, 0,
++ callout_driver.minor_start + state->line);
++ return state->line + SERIAL_DEV_OFFSET;
++}
++
++/**
++ * unregister_serial - deconfigure a 16x50 serial port
++ * @line: line to deconfigure
++ *
++ * The port specified is deconfigured and its resources are freed. Any
++ * user of the port is disconnected as if carrier was dropped. Line is
++ * the port number returned by register_serial().
++ */
++
++void unregister_serial(int line)
++{
++ unsigned long flags;
++ struct serial_state *state = &rs_table[line];
++
++ save_flags(flags); cli();
++ if (state->info && state->info->tty)
++ tty_hangup(state->info->tty);
++ state->type = PORT_UNKNOWN;
++ printk(KERN_INFO "ttyS%02d unloaded\n", state->line);
++ /* These will be hidden, because they are devices that will no longer
++ * be available to the system. (ie, PCMCIA modems, once ejected)
++ */
++ tty_unregister_devfs(&serial_driver,
++ serial_driver.minor_start + state->line);
++ tty_unregister_devfs(&callout_driver,
++ callout_driver.minor_start + state->line);
++ restore_flags(flags);
++}
++
++static void __exit rs_fini(void)
++{
++ unsigned long flags;
++ int e1, e2;
++ int i;
++ struct async_struct *info;
++
++ /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
++ del_timer_sync(&serial_timer);
++ save_flags(flags); cli();
++ remove_bh(SERIAL_BH);
++ if ((e1 = tty_unregister_driver(&serial_driver)))
++ printk("serial: failed to unregister serial driver (%d)\n",
++ e1);
++ if ((e2 = tty_unregister_driver(&callout_driver)))
++ printk("serial: failed to unregister callout driver (%d)\n",
++ e2);
++ restore_flags(flags);
++
++ for (i = 0; i < NR_PORTS; i++) {
++ if ((info = rs_table[i].info)) {
++ rs_table[i].info = NULL;
++ kfree(info);
++ }
++ if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) {
++#ifdef CONFIG_SERIAL_RSA
++ if (rs_table[i].type == PORT_RSA)
++ release_region(rs_table[i].port +
++ UART_RSA_BASE, 16);
++ else
++#endif
++ release_region(rs_table[i].port, 8);
++ }
++#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)
++ if (rs_table[i].iomem_base)
++ iounmap(rs_table[i].iomem_base);
++#endif
++ }
++#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)
++ for (i=0; i < NR_PCI_BOARDS; i++) {
++ struct pci_board_inst *brd = &serial_pci_board[i];
++
++ if (serial_pci_board[i].dev == 0)
++ continue;
++ if (brd->board.init_fn)
++ (brd->board.init_fn)(brd->dev, &brd->board, 0);
++ if (DEACTIVATE_FUNC(brd->dev))
++ (DEACTIVATE_FUNC(brd->dev))(brd->dev);
++ }
++#endif
++ if (tmp_buf) {
++ unsigned long pg = (unsigned long) tmp_buf;
++ tmp_buf = NULL;
++ free_page(pg);
++ }
++
++#ifdef ENABLE_SERIAL_PCI
++ if (serial_pci_driver.name[0])
++ pci_unregister_driver (&serial_pci_driver);
++#endif
++}
++
++module_init(rs_init);
++module_exit(rs_fini);
++MODULE_DESCRIPTION("Standard/generic (dumb) serial driver");
++MODULE_AUTHOR("Theodore Ts'o <tytso at mit.edu>");
++MODULE_LICENSE("GPL");
++
++
++/*
++ * ------------------------------------------------------------
++ * Serial console driver
++ * ------------------------------------------------------------
++ */
++#ifdef CONFIG_SERIAL_CONSOLE
++
++#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
++
++static struct async_struct async_sercons;
++
++/*
++ * Wait for transmitter & holding register to empty
++ */
++static inline void wait_for_xmitr(struct async_struct *info)
++{
++ unsigned int status, tmout = 1000000;
++
++ do {
++ status = serial_in(info, UART_LSR);
++
++ if (status & UART_LSR_BI)
++ lsr_break_flag = UART_LSR_BI;
++
++ if (--tmout == 0)
++ break;
++ } while((status & BOTH_EMPTY) != BOTH_EMPTY);
++
++ /* Wait for flow control if necessary */
++ if (info->flags & ASYNC_CONS_FLOW) {
++ tmout = 1000000;
++ while (--tmout &&
++ ((serial_in(info, UART_MSR) & UART_MSR_CTS) == 0));
++ }
++}
++
++
++/*
++ * 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 serial_console_write(struct console *co, const char *s,
++ unsigned count)
++{
++ static struct async_struct *info = &async_sercons;
++ int ier;
++ unsigned i;
++
++ /*
++ * First save the IER then disable the interrupts
++ */
++ ier = serial_in(info, UART_IER);
++ serial_out(info, UART_IER, 0x00);
++
++ /*
++ * Now, do each character
++ */
++ for (i = 0; i < count; i++, s++) {
++ wait_for_xmitr(info);
++
++ /*
++ * Send the character out.
++ * If a LF, also do CR...
++ */
++ serial_out(info, UART_TX, *s);
++ if (*s == 10) {
++ wait_for_xmitr(info);
++ serial_out(info, UART_TX, 13);
++ }
++ }
++
++ /*
++ * Finally, Wait for transmitter & holding register to empty
++ * and restore the IER
++ */
++ wait_for_xmitr(info);
++ serial_out(info, UART_IER, ier);
++}
++
++static kdev_t serial_console_device(struct console *c)
++{
++ return MKDEV(TTY_MAJOR, 64 + c->index);
++}
++
++/*
++ * Setup initial baud/bits/parity/flow control. We do two things here:
++ * - construct a cflag setting for the first rs_open()
++ * - initialize the serial port
++ * Return non-zero if we didn't find a serial port.
++ */
++static int __init serial_console_setup(struct console *co, char *options)
++{
++ static struct async_struct *info;
++ struct serial_state *state;
++ unsigned cval;
++ int baud = 9600;
++ int bits = 8;
++ int parity = 'n';
++ int doflow = 0;
++ int cflag = CREAD | HUPCL | CLOCAL;
++ int quot = 0;
++ char *s;
++
++ if (options) {
++ baud = simple_strtoul(options, NULL, 10);
++ s = options;
++ while(*s >= '0' && *s <= '9')
++ s++;
++ if (*s) parity = *s++;
++ if (*s) bits = *s++ - '0';
++ if (*s) doflow = (*s++ == 'r');
++ }
++
++ /*
++ * Now construct a cflag setting.
++ */
++ switch(baud) {
++ case 1200:
++ cflag |= B1200;
++ break;
++ case 2400:
++ cflag |= B2400;
++ break;
++ case 4800:
++ cflag |= B4800;
++ break;
++ case 19200:
++ cflag |= B19200;
++ break;
++ case 38400:
++ cflag |= B38400;
++ break;
++ case 57600:
++ cflag |= B57600;
++ break;
++ case 115200:
++ cflag |= B115200;
++ break;
++ case 9600:
++ default:
++ cflag |= B9600;
++ /*
++ * Set this to a sane value to prevent a divide error
++ */
++ baud = 9600;
++ break;
++ }
++ switch(bits) {
++ case 7:
++ cflag |= CS7;
++ break;
++ default:
++ case 8:
++ cflag |= CS8;
++ break;
++ }
++ switch(parity) {
++ case 'o': case 'O':
++ cflag |= PARODD;
++ break;
++ case 'e': case 'E':
++ cflag |= PARENB;
++ break;
++ }
++ co->cflag = cflag;
++
++ /*
++ * Divisor, bytesize and parity
++ */
++ state = rs_table + co->index;
++ if (doflow)
++ state->flags |= ASYNC_CONS_FLOW;
++ info = &async_sercons;
++ info->magic = SERIAL_MAGIC;
++ info->state = state;
++ info->port = state->port;
++ info->flags = state->flags;
++#ifdef CONFIG_HUB6
++ info->hub6 = state->hub6;
++#endif
++ info->io_type = state->io_type;
++ info->iomem_base = state->iomem_base;
++ info->iomem_reg_shift = state->iomem_reg_shift;
++ quot = state->baud_base / baud;
++ cval = cflag & (CSIZE | CSTOPB);
++#if defined(__powerpc__) || defined(__alpha__)
++ cval >>= 8;
++#else /* !__powerpc__ && !__alpha__ */
++ cval >>= 4;
++#endif /* !__powerpc__ && !__alpha__ */
++ if (cflag & PARENB)
++ cval |= UART_LCR_PARITY;
++ if (!(cflag & PARODD))
++ cval |= UART_LCR_EPAR;
++
++ /*
++ * Disable UART interrupts, set DTR and RTS high
++ * and set speed.
++ */
++ serial_out(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
++ serial_out(info, UART_DLL, quot & 0xff); /* LS of divisor */
++ serial_out(info, UART_DLM, quot >> 8); /* MS of divisor */
++ serial_out(info, UART_LCR, cval); /* reset DLAB */
++ serial_out(info, UART_IER, 0);
++ serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
++
++ /*
++ * If we read 0xff from the LSR, there is no UART here.
++ */
++ if (serial_in(info, UART_LSR) == 0xff)
++ return -1;
++
++ return 0;
++}
++
++static struct console sercons = {
++ name: "ttyS",
++ write: serial_console_write,
++ device: serial_console_device,
++ setup: serial_console_setup,
++ flags: CON_PRINTBUFFER,
++ index: -1,
++};
++
++/*
++ * Register console.
++ */
++void __init serial_console_init(void)
++{
++ register_console(&sercons);
++}
++#endif
++
++/*
++ Local variables:
++ compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c"
++ End:
++*/
+diff -urN kernel-source-2.4.27-8/drivers/char/serial_21285.c kernel-source-2.4.27-8-arm-1/drivers/char/serial_21285.c
+--- kernel-source-2.4.27-8/drivers/char/serial_21285.c 2002-08-03 01:39:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/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 <linux/config.h>
+-#include <linux/module.h>
+-#include <linux/errno.h>
+-#include <linux/signal.h>
+-#include <linux/sched.h>
+-#include <linux/interrupt.h>
+-#include <linux/tty.h>
+-#include <linux/tty_flip.h>
+-#include <linux/serial.h>
+-#include <linux/major.h>
+-#include <linux/ptrace.h>
+-#include <linux/ioport.h>
+-#include <linux/mm.h>
+-#include <linux/slab.h>
+-#include <linux/init.h>
+-#include <linux/console.h>
+-
+-#include <asm/io.h>
+-#include <asm/irq.h>
+-#include <asm/uaccess.h>
+-#include <asm/dec21285.h>
+-#include <asm/hardware.h>
+-
+-#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 kernel-source-2.4.27-8/drivers/char/serial_amba.c kernel-source-2.4.27-8-arm-1/drivers/char/serial_amba.c
+--- kernel-source-2.4.27-8/drivers/char/serial_amba.c 2005-01-19 09:57:54.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/serial_amba.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,2008 +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 <linux/config.h>
+-#include <linux/module.h>
+-#include <linux/errno.h>
+-#include <linux/signal.h>
+-#include <linux/sched.h>
+-#include <linux/interrupt.h>
+-#include <linux/tty.h>
+-#include <linux/tty_flip.h>
+-#include <linux/major.h>
+-#include <linux/string.h>
+-#include <linux/fcntl.h>
+-#include <linux/ptrace.h>
+-#include <linux/ioport.h>
+-#include <linux/mm.h>
+-#include <linux/slab.h>
+-#include <linux/init.h>
+-#include <linux/circ_buf.h>
+-#include <linux/serial.h>
+-#include <linux/console.h>
+-#include <linux/sysrq.h>
+-
+-#include <asm/system.h>
+-#include <asm/io.h>
+-#include <asm/irq.h>
+-#include <asm/uaccess.h>
+-#include <asm/bitops.h>
+-
+-#include <asm/hardware/serial_amba.h>
+-
+-#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;
+-
+- tty_wakeup(tty);
+-}
+-
+-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);
+- tty_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);
+- tty_ldisc_flush(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 kernel-source-2.4.27-8/drivers/char/st-m41st87.c kernel-source-2.4.27-8-arm-1/drivers/char/st-m41st87.c
+--- kernel-source-2.4.27-8/drivers/char/st-m41st87.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/st-m41st87.c 2005-02-18 17:48:42.000000000 +0000
+@@ -0,0 +1,1061 @@
++/*
++ * drivers/char/st-m41st87.c
++ *
++ * based on ds1307.c
++ *
++ * Device driver for STMicroelectronics M41ST87 series RTC
++ *
++ * Copyright (C) 2002 Intrinsyc Software Inc.
++ * Copyirhgt (C) 2004 Simtec Electronics
++ *
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++#include <linux/poll.h>
++#include <linux/i2c.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/rtc.h>
++#include <linux/string.h>
++#include <linux/miscdevice.h>
++#include <linux/proc_fs.h>
++
++
++#define PFX "M41ST87: "
++
++#define I2C_DRIVERID_M41ST87 I2C_DRIVERID_EXP0
++
++#define DEBUG 0
++
++#if 0
++#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
++#endif
++
++#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
++
++#if DEBUG
++static unsigned int rtc_debug = DEBUG;
++#else
++#define rtc_debug 0 /* gcc will remove all the debug code for us */
++#endif
++
++#define M41ST87_TENTHS (0x00)
++#define M41ST87_SECONDS (0x01)
++#define M41ST87_MINUTES (0x02)
++#define M41ST87_HOURS (0x03)
++#define M41ST87_DAY (0x04)
++#define M41ST87_DATE (0x05)
++#define M41ST87_MONTH (0x06)
++#define M41ST87_YEAR (0x07)
++#define M41ST87_CONTROL (0x08)
++#define M41ST87_WATCHDOG (0x09)
++#define M41ST87_ALARM_BASE (0x0A)
++#define M41ST87_ALMONTH (0x0A)
++#define M41ST87_ALDATE (0x0B)
++#define M41ST87_ALHOUR (0x0C)
++#define M41ST87_ALMINS (0x0D)
++#define M41ST87_ALSECS (0x0E)
++#define M41ST87_FLAGS (0x0F)
++#define M41ST87_SQW (0x13)
++#define M41ST87_TAMPER1 (0x14)
++#define M41ST87_TAMPER2 (0x15)
++
++#define M41ST87_SERIAL (0x16)
++#define M41ST87_SERIAL_SIZE (0x08)
++
++#define M41ST87_RAM_ADDR_START (0x20)
++#define M41ST87_RAM_ADDR_END (0x9f)
++
++#define M41ST87_OVERALL_SIZE (M41ST87_RAM_ADDR_END+1)
++
++
++#define M41ST87_RAM_SIZE ((M41ST87_RAM_ADDR_END - M41ST87_RAM_ADDR_START)+1)
++
++#define M41ST87_SECONDS_ST (1<<7)
++
++#define M41ST87_MINUTES_OFIE (1<<7)
++
++#define M41ST87_HOURS_CB0 (1<<6)
++#define M41ST87_HOURS_CB1 (1<<7)
++
++#define M41ST87_DAY_32kE (1<<3)
++#define M41ST87_DAY_CLRPW0 (1<<4)
++#define M41ST87_DAY_CLRPW1 (1<<5)
++#define M41ST87_DAY_THS (1<<6)
++#define M41ST87_DAY_TR (1<<7)
++
++#define M41ST87_FLAGS_TB2 (1<<0)
++#define M41ST87_FLAGS_TB1 (1<<1)
++#define M41ST87_FLAGS_OF (1<<2)
++#define M41ST87_FLAGS_BL (1<<4)
++#define M41ST87_FLAGS_AF (1<<6)
++#define M41ST87_FLAGS_WDF (1<<7)
++
++#define M41ST87_TAMPERx_CLR (1<<0)
++#define M41ST87_TAMPERx_CLREXT (1<<1)
++#define M41ST87_TAMPERx_TC (1<<2)
++#define M41ST87_TAMPERx_TDS (1<<3)
++#define M41ST87_TAMPERx_TPM (1<<4)
++#define M41ST87_TAMPERx_TCM (1<<5)
++#define M41ST87_TAMPERx_TIE (1<<6)
++#define M41ST87_TAMPERx_TEB (1<<7)
++
++#define M41ST87_ALDATE_RPT4 (1<<7)
++#define M41ST87_ALDATE_RPT5 (1<<6)
++
++#define M41ST87_ALHOUR_RPT3 (1<<7)
++#define M41ST87_ALHOUR_HT (1<<6)
++#define M41ST87_ALMINS_RPT2 (1<<7)
++#define M41ST87_ALSECS_RPT1 (1<<7)
++#
++
++#define M41ST87_SLAVE_ADDR ((0xD0) >> 1)
++
++#define PROC_M41ST87_NAME "driver/m41st87"
++
++static unsigned short slave_address = M41ST87_SLAVE_ADDR;
++
++struct i2c_driver m41st87_driver;
++struct i2c_client *m41st87_i2c_client = 0;
++
++static unsigned short ignore[] = { I2C_CLIENT_END };
++static unsigned short normal_addr[] = { M41ST87_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,
++};
++
++struct rtc_mem {
++ unsigned int loc;
++ unsigned int nr;
++ unsigned char *data;
++};
++
++
++struct m41st87_info {
++
++ unsigned char ht_reg; /* true if we init with HT bit set */
++ unsigned char init_reg[7]; /* values of registers read at start */
++
++ unsigned char serial[M41ST87_SERIAL_SIZE]; /* copy of serial no */
++};
++
++static int m41st87_convert_to_time( struct rtc_time *dt, char *buf);
++
++static int m41st87_rtc_ioctl( struct inode *, struct file *, unsigned int, unsigned long);
++static int m41st87_rtc_open(struct inode *inode, struct file *file);
++static int m41st87_rtc_release(struct inode *inode, struct file *file);
++
++static struct file_operations rtc_fops = {
++ owner: THIS_MODULE,
++ ioctl: m41st87_rtc_ioctl,
++ open: m41st87_rtc_open,
++ release: m41st87_rtc_release,
++};
++
++static struct miscdevice m41st87_rtc_miscdev = {
++ RTC_MINOR,
++ "rtc",
++ &rtc_fops
++};
++
++#define M41ST87_GETDATETIME 0
++#define M41ST87_SETTIME 1
++#define M41ST87_SETDATETIME 2
++#define M41ST87_MEM_READ 3
++#define M41ST87_MEM_WRITE 4
++#define M41ST87_SETALARM 5
++#define M41ST87_GETALARM 6
++
++static int m41st87_probe(struct i2c_adapter *adap);
++static int m41st87_detach(struct i2c_client *client);
++static int m41st87_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++struct i2c_driver m41st87_driver = {
++ name: "M41ST87",
++ id: I2C_DRIVERID_M41ST87,
++ flags: I2C_DF_NOTIFY,
++ attach_adapter: m41st87_probe,
++ detach_client: m41st87_detach,
++ command: m41st87_command
++};
++
++static spinlock_t m41st87_rtc_lock = SPIN_LOCK_UNLOCKED;
++
++/* i2c access routines */
++
++static int
++m41st87_i2c_read(struct i2c_client *client, int reg, char *buf, int len)
++{
++ unsigned char cr = reg;
++ int ret;
++ struct i2c_msg msgs[2] = {
++ { client->addr , 0, 1, &cr },
++ { client->addr , I2C_M_RD, len, buf }
++ };
++
++ if (client == NULL) {
++ printk("m41st87_i2c_read: client is NULL!\n");
++ return -EINVAL;
++ }
++
++
++ ret = i2c_transfer(client->adapter, msgs, 2);
++
++ if (ret != 2 && rtc_debug > 0)
++ printk("m41st87_i2c_read: failed %d, (%p, 0x%02x, %p, %d)\n",
++ ret, client, reg, buf, len);
++
++ return ret;
++}
++
++static int
++m41st87_i2c_write(struct i2c_client *client, int reg, char *buf, int len)
++{
++ unsigned char cr = reg;
++ struct i2c_msg msgs[2] = {
++ { client->addr , 0, 1, &cr },
++ { client->addr , I2C_M_NOSTART, len, buf }
++ };
++
++ if (rtc_debug > 0) {
++ int i;
++ int ret;
++
++ printk("m41st87_i2c_write(%p,0x%02x,%p,%d)\n",
++ client, reg, buf, len);
++
++ for (i = 0; i < len; i++) {
++ printk("%02x ", buf[i]);
++ if ((i % 8) == 7)
++ printk("\n");
++ }
++ printk("\n");
++
++ ret = i2c_transfer(client->adapter, msgs, 2);
++
++ printk("m41st87_i2c_write: i2c_transfer returned %d\n", ret);
++
++ return ret;
++ }
++
++ return i2c_transfer(client->adapter, msgs, 2);
++}
++
++
++static int
++m41st87_readram( char *buf, int len)
++{
++ unsigned long flags;
++ int ret;
++
++ if (m41st87_i2c_client == NULL)
++ return -EINVAL;
++
++ spin_lock_irqsave(&m41st87_rtc_lock, flags);
++ ret = m41st87_i2c_read(m41st87_i2c_client, 0x00, buf, len);
++ spin_unlock_irqrestore(&m41st87_rtc_lock,flags);
++
++ return ret;
++}
++
++static void
++m41st87_dumpram( void)
++{
++ unsigned char buf[M41ST87_RAM_SIZE];
++ int ret;
++
++ ret = m41st87_readram( buf, M41ST87_RAM_SIZE);
++
++ if( ret > 0)
++ {
++ int i;
++
++ printk("0000 ");
++ for( i=0; i<M41ST87_RAM_SIZE; i++)
++ {
++ printk ("%02X ", buf[i]);
++ if (buf[i] >= 32 && buf[i] <= 32) {
++ printk("%c", buf[i]);
++ } else {
++ printk(" ");
++ }
++
++ if( (i%8) == 7) printk ("\n%04x ",i+1);
++ }
++ printk ("\n");
++ }
++}
++
++int
++m41st87_attach(struct i2c_adapter *adap, int addr, unsigned short flags,int kind)
++{
++ struct i2c_client *c;
++ struct m41st87_info *mi;
++ struct rtc_time rtc_tm;
++ int ret;
++ int i;
++
++ if (rtc_debug > 0)
++ printk("m41st87_attach(%p, addr %d, %d, %d)\n",
++ adap, addr, flags, kind);
++
++ c = (struct i2c_client *)kmalloc(sizeof(*c), GFP_KERNEL);
++ if (!c)
++ return -ENOMEM;
++
++ mi = (struct m41st87_info *)kmalloc(sizeof(*mi), GFP_KERNEL);
++ if (!mi) {
++ kfree(c);
++ return -ENOMEM;
++ }
++
++ strcpy(c->name, "M41ST87");
++ c->id = m41st87_driver.id;
++ c->flags = 0;
++ c->addr = addr;
++ c->adapter = adap;
++ c->driver = &m41st87_driver;
++ c->data = mi;
++
++ m41st87_i2c_client = c;
++
++ if (rtc_debug > 0)
++ printk("created client %p, data %p\n", m41st87_i2c_client, mi);
++
++ /* read info from the rtc to see what state it is in */
++
++ ret = m41st87_i2c_read(c, M41ST87_SERIAL, mi->serial, M41ST87_SERIAL_SIZE);
++
++ if (ret != 2) {
++ printk("m41st87_attach: failed to read serial number (%d)\n", ret);
++ } else {
++ printk(PFX ": serial ");
++
++ for (i = 0; i < M41ST87_SERIAL_SIZE; i++) {
++ printk("%02x ", mi->serial[i]);
++ }
++
++ printk("\n");
++ }
++
++ if (m41st87_i2c_read(c, M41ST87_ALHOUR, &mi->ht_reg, 1) != 2)
++ printk("m41st87_attach: failed to read Alarm Hour register\n");
++
++ if (m41st87_i2c_read(c, M41ST87_SECONDS, mi->init_reg, 7) != 2)
++ printk("m41st87_attach: failed to read initial time\n");
++
++ if (rtc_debug > 0) {
++ printk("m41st87_attach: regs: ");
++ for (i = 0; i < 7; i++) {
++ printk("%02x ", mi->init_reg[i]);
++ }
++
++ printk("\n");
++ }
++
++ if (mi->ht_reg & M41ST87_ALHOUR_HT) {
++ unsigned char tmp = mi->ht_reg & ~M41ST87_ALHOUR_HT;
++
++ printk("m41st87_attach: time was halted at startup\n");
++ m41st87_i2c_write(c, M41ST87_ALHOUR, &tmp, 1);
++ }
++
++ if (mi->init_reg[0] & M41ST87_SECONDS_ST) {
++ unsigned char tmp = mi->ht_reg & ~M41ST87_SECONDS_ST;
++
++ printk("m41st87_attach: oscillator disabled, re-starting\n");
++ m41st87_i2c_write(c, M41ST87_SECONDS, &tmp, 1);
++ }
++
++ if ((m41st87_convert_to_time(&rtc_tm, mi->init_reg) >= 4)) {
++ unsigned char deftime[8];
++
++ printk("m41st87_attach: too many errors in time values, resetting\n");
++
++ deftime[M41ST87_TENTHS] = 0;
++ deftime[M41ST87_SECONDS] = 0;
++ deftime[M41ST87_MINUTES] = 0;
++ deftime[M41ST87_HOURS] = 0;
++ deftime[M41ST87_DAY] = 1;
++ deftime[M41ST87_DATE] = 1;
++ deftime[M41ST87_MONTH] = 1;
++ deftime[M41ST87_YEAR] = 0;
++
++ deftime[M41ST87_DAY] |= M41ST87_DAY_32kE;
++
++ m41st87_i2c_write(c, M41ST87_TENTHS, deftime, M41ST87_YEAR+1);
++ }
++
++
++ return i2c_attach_client(c);
++}
++
++static int
++m41st87_probe(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, m41st87_attach);
++}
++
++static int
++m41st87_detach(struct i2c_client *client)
++{
++ i2c_detach_client(client);
++ return 0;
++}
++
++static int
++bcd_to_bin(unsigned int val, unsigned int max, int *errs)
++{
++ int cv = (((val)&15) + ((val)>>4)*10);
++
++ if (((val & 0xf) > 0x9) | ((val & 0xf0) > 0x90)) {
++ (*errs)++;
++ cv = 1;
++ }
++
++ return cv;
++}
++
++static int
++m41st87_convert_alarm_to_time( struct rtc_time *dt, char *buf)
++{
++ int errs = 0;
++ if (rtc_debug > 1)
++ printk("m41st87_convert_alarm_to_time: %02x %02x %02x %02x %02x %02x\n",
++ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
++
++ dt->tm_sec = bcd_to_bin(buf[4] & 0x7f, 60, &errs);
++ dt->tm_min = bcd_to_bin(buf[3] & 0x7f, 60, &errs);
++ dt->tm_hour = bcd_to_bin(buf[2] & 0x3f, 24, &errs);
++ dt->tm_mday = bcd_to_bin(buf[1] & 0x3f, 32, &errs);
++
++ /* dt->tm_mon is zero-based */
++ dt->tm_mon = bcd_to_bin(buf[0] & 0x1f, 13, &errs) - 1;
++
++ return errs;
++}
++
++static int
++m41st87_convert_to_time( struct rtc_time *dt, char *buf)
++{
++ int errs = 0;
++
++ if (rtc_debug > 1)
++ printk("m41st87_convert_to_time: %02x %02x %02x %02x %02x %02x\n",
++ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
++
++ dt->tm_sec = bcd_to_bin(buf[0] & 0x7f, 60, &errs);
++ dt->tm_min = bcd_to_bin(buf[1] & 0x7f, 60, &errs);
++ dt->tm_hour = bcd_to_bin(buf[2] & 0x3f, 24, &errs);
++ dt->tm_mday = bcd_to_bin(buf[4] & 0x7f, 32, &errs);
++
++ /* dt->tm_mon is zero-based */
++ dt->tm_mon = bcd_to_bin(buf[5] & 0x1f, 13, &errs) - 1;
++ /* year is 1900 + dt->tm_year */
++ dt->tm_year = bcd_to_bin(buf[6], 101, &errs) + 100;
++
++ if( rtc_debug > 1)
++ {
++ printk("m41st87_get_datetime: yr=%d, mon=%d, mday=%d, hr=%d, min=%d, sec=%d\n",
++ dt->tm_year, dt->tm_mon, dt->tm_mday,
++ dt->tm_hour, dt->tm_min, dt->tm_sec);
++ }
++
++ return errs;
++}
++
++static int
++m41st87_get_datetime(struct i2c_client *client, struct rtc_time *dt)
++{
++ unsigned char buf[7];
++ int ret;
++
++ ret = m41st87_i2c_read(client, M41ST87_SECONDS, buf, 7);
++
++ if (ret == 2) {
++ m41st87_convert_to_time( dt, buf);
++ ret = 0;
++ }
++ else {
++ printk("m41st87_get_datetime: i2c_transfer() returned %d\n",ret);
++ if (ret == 0)
++ ret = -1;
++ }
++
++ return ret;
++}
++
++static int
++m41st87_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo)
++{
++ unsigned char buf[8];
++ int ret, len = 4;
++
++ if (client == NULL)
++ return -EINVAL;
++
++ if( rtc_debug > 0)
++ {
++ printk("m41st87_set_datetime: tm_year = %d\n", dt->tm_year);
++ printk("m41st87_set_datetime: tm_mon = %d\n", dt->tm_mon);
++ printk("m41st87_set_datetime: tm_mday = %d\n", dt->tm_mday);
++ printk("m41st87_set_datetime: tm_hour = %d\n", dt->tm_hour);
++ printk("m41st87_set_datetime: tm_min = %d\n", dt->tm_min);
++ printk("m41st87_set_datetime: tm_sec = %d\n", dt->tm_sec);
++ }
++
++ buf[0] = M41ST87_SECONDS; /* register address on M41ST87 */
++ buf[M41ST87_SECONDS] = (BIN_TO_BCD(dt->tm_sec));
++ buf[M41ST87_MINUTES] = (BIN_TO_BCD(dt->tm_min));
++ buf[M41ST87_HOURS] = (BIN_TO_BCD(dt->tm_hour));
++
++ if (datetoo) {
++ len = 8;
++ /* we skip buf[4] as we don't use day-of-week. */
++ buf[M41ST87_DAY] = 0;
++ buf[M41ST87_DATE] = (BIN_TO_BCD(dt->tm_mday));
++ buf[M41ST87_MONTH] = (BIN_TO_BCD(dt->tm_mon + 1));
++ buf[M41ST87_YEAR] = (BIN_TO_BCD((dt->tm_year - 100) % 100));
++ buf[M41ST87_DAY] |= M41ST87_DAY_32kE;
++ }
++
++
++ /* the chips keeps the century offset in bits 6 and 7 of the hours */
++ buf[3] |= ((dt->tm_year - 100) / 100) << 6;
++
++ if (rtc_debug > 0) {
++ int i;
++
++ printk("m41st87_set_datetime: %d: ", len);
++ for (i = 0; i < len; i++)
++ printk("%02x ", buf[i]);
++
++ printk("\n");
++ }
++
++ ret = i2c_master_send(client, (char *)buf, len);
++ if (ret == len)
++ ret = 0;
++ else
++ printk("m41st87_set_datetime: i2c_master_send() returned %d\n",ret);
++
++
++ return ret;
++}
++
++static int
++m41st87_read_mem(struct i2c_client *client, struct rtc_mem *mem)
++{
++ if (client == NULL)
++ return -EINVAL;
++
++ if ( (mem->loc < M41ST87_RAM_ADDR_START) ||
++ ((mem->loc + mem->nr -1) > M41ST87_RAM_ADDR_END) )
++ return -EINVAL;
++
++ return m41st87_i2c_read(client, mem->loc, mem->data, mem->nr);
++}
++
++static int
++m41st87_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 (client == NULL)
++ return -EINVAL;
++
++ if ( (mem->loc < M41ST87_RAM_ADDR_START) ||
++ ((mem->loc + mem->nr -1) > M41ST87_RAM_ADDR_END) )
++ return -EINVAL;
++
++ addr[0] = mem->loc;
++
++ return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;
++}
++
++static int
++m41st87_get_alarm(struct i2c_client *client, struct rtc_time *dt)
++{
++ unsigned char buf[7];
++ int ret;
++
++ ret = m41st87_i2c_read(client, M41ST87_ALARM_BASE, buf, 6);
++
++ if (ret == 2) {
++ m41st87_convert_alarm_to_time( dt, buf);
++ ret = 0;
++ }
++ else {
++ printk("m41st87_get_datetime: i2c_transfer() returned %d\n",ret);
++ if (ret == 0)
++ ret = -1;
++ }
++
++ return ret;
++}
++
++static void
++m41st87_alarm_encode(char *buf, int reg, int mask, int val)
++{
++ buf += (reg - M41ST87_ALARM_BASE);
++
++ *buf &= ~mask;
++ *buf |= (BIN_TO_BCD(val)) & mask;
++}
++
++static int
++m41st87_set_alarm(struct i2c_client *client, struct rtc_time *dt)
++{
++ char buf[7];
++ int res;
++
++ res = m41st87_i2c_read(client, M41ST87_ALARM_BASE, buf,
++ (M41ST87_ALSECS - M41ST87_ALARM_BASE)+1);
++ if (res != 2)
++ return -EIO;
++
++ m41st87_alarm_encode(buf, M41ST87_ALSECS, 0x7f, dt->tm_sec);
++ m41st87_alarm_encode(buf, M41ST87_ALMINS, 0x7f, dt->tm_min);
++ m41st87_alarm_encode(buf, M41ST87_ALHOUR, 0x3f, dt->tm_hour);
++ m41st87_alarm_encode(buf, M41ST87_ALDATE, 0x3f, dt->tm_mday-1);
++ m41st87_alarm_encode(buf, M41ST87_ALMONTH, 0x1f, dt->tm_mon-1);
++
++ res = m41st87_i2c_write(client, M41ST87_ALARM_BASE, buf,
++ (M41ST87_ALSECS - M41ST87_ALARM_BASE)+1);
++
++ if (res == 2)
++ return 0;
++
++ return res == 0 ? -EIO : res;
++}
++
++static int
++m41st87_command(struct i2c_client *client, unsigned int cmd, void *arg)
++{
++ if (client == NULL) {
++ printk(KERN_ERR "m41st87_command: called with NULL client\n");
++ return -EINVAL;
++ }
++
++ switch (cmd) {
++ case M41ST87_GETDATETIME:
++ return m41st87_get_datetime(client, arg);
++
++ case M41ST87_SETTIME:
++ return m41st87_set_datetime(client, arg, 0);
++
++ case M41ST87_SETDATETIME:
++ return m41st87_set_datetime(client, arg, 1);
++
++ case M41ST87_MEM_READ:
++ return m41st87_read_mem(client, arg);
++
++ case M41ST87_MEM_WRITE:
++ return m41st87_write_mem(client, arg);
++
++ case M41ST87_SETALARM:
++ return m41st87_set_alarm(client, arg);
++
++ case M41ST87_GETALARM:
++ return m41st87_get_alarm(client, arg);
++
++ default:
++ return -EINVAL;
++ }
++}
++
++static int
++m41st87_rtc_open(struct inode *inode, struct file *file)
++{
++ return 0;
++}
++
++static int
++m41st87_rtc_release(struct inode *inode, struct file *file)
++{
++ return 0;
++}
++
++static int
++m41st87_rtc_ioctl( struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ unsigned long flags;
++ struct rtc_time wtime;
++ int status = 0;
++
++ if (rtc_debug > 1)
++ printk("m41st87_rtc_ioctl: inode=%p, file=%p, cmd=%d, arg=%ld\n",
++ inode, file, cmd, arg);
++
++ 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_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_ALM_SET:
++ 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(&m41st87_rtc_lock, flags);
++ m41st87_command( m41st87_i2c_client,
++ M41ST87_SETALARM, &wtime);
++ spin_unlock_irqrestore(&m41st87_rtc_lock,flags);
++ break;
++
++
++ case RTC_ALM_READ:
++
++ spin_lock_irqsave(&m41st87_rtc_lock, flags);
++ m41st87_command( m41st87_i2c_client,
++ M41ST87_GETALARM, &wtime);
++ spin_unlock_irqrestore(&m41st87_rtc_lock,flags);
++
++ if( copy_to_user((void *)arg, &wtime,
++ sizeof (struct rtc_time)))
++ status = -EFAULT;
++ break;
++
++ case RTC_RD_TIME:
++ spin_lock_irqsave(&m41st87_rtc_lock, flags);
++ m41st87_command( m41st87_i2c_client,
++ M41ST87_GETDATETIME, &wtime);
++ spin_unlock_irqrestore(&m41st87_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(&m41st87_rtc_lock, flags);
++ m41st87_command( m41st87_i2c_client,
++ M41ST87_SETDATETIME, &wtime);
++ spin_unlock_irqrestore(&m41st87_rtc_lock,flags);
++ break;
++ }
++
++ return status;
++}
++
++static char *m41st87_sqw[] = {
++ "none",
++ "32768",
++ "8192",
++ "4096",
++ "2048",
++ "1024",
++ "512",
++ "256",
++ "128",
++ "64",
++ "32",
++ "16",
++ "8",
++ "4",
++ "2",
++ "1"
++};
++
++static char *
++m41st87_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 char *
++m41st87_alarm_rpt(unsigned int sel)
++{
++ switch (sel) {
++ case 0:
++ return "year";
++ case (1<<5):
++ return "month";
++ case (1<<5)|(1<<4):
++ return "day";
++ case (1<<5)|(1<<4)|(1<<3):
++ return "hour";
++ case (1<<5)|(1<<4)|(1<<3)|(1<<2):
++ return "minute";
++ case (1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1):
++ return "second";
++ }
++
++ return "?";
++}
++
++#define BIT(ctrl,bit) ((ctrl & bit) ? "1" : "0")
++#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no")
++#define NCHECK(ctrl,bit) ((ctrl & bit) ? "no" : "yes")
++#define ENABLED(ctrl,bit) ((ctrl & bit) ? "enabled" : "disabled")
++
++static int
++m41st87_show_tamper(char *buf, int reg, int which, int detected)
++{
++ char *p = buf;
++
++ p += sprintf(buf, "Tamper %d: %s\n", which, detected ? "detected" : "ok");
++
++ p += sprintf(p, " Enabled : %s\n", CHECK(reg, M41ST87_TAMPERx_TEB));
++ p += sprintf(p, " Interrupt : %s\n", CHECK(reg, M41ST87_TAMPERx_TIE));
++ p += sprintf(p, " Current High : %s\n", CHECK(reg, M41ST87_TAMPERx_TC));
++ p += sprintf(p, " Sample : %s\n", BIT(reg, M41ST87_TAMPERx_TDS));
++ p += sprintf(p, " Connect Mode : %s\n", BIT(reg, M41ST87_TAMPERx_TCM));
++ p += sprintf(p, " Polarity : %s\n", BIT(reg, M41ST87_TAMPERx_TPM));
++ p += sprintf(p, " Clear Ext : %s\n", BIT(reg, M41ST87_TAMPERx_CLREXT));
++
++ p += sprintf(p, "\n");
++
++ return p - buf;
++}
++
++static unsigned int
++m41st87_decode_alarm_rpt(char *buf)
++{
++ int res = 0;
++
++ if (buf[M41ST87_ALDATE - M41ST87_ALARM_BASE] & M41ST87_ALDATE_RPT5)
++ res |= 1<<4;
++
++ if (buf[M41ST87_ALDATE - M41ST87_ALARM_BASE] & M41ST87_ALDATE_RPT4)
++ res |= 1<<3;
++
++ if (buf[M41ST87_ALHOUR - M41ST87_ALARM_BASE] & M41ST87_ALHOUR_RPT3)
++ res |= 1<<2;
++
++ if (buf[M41ST87_ALMINS - M41ST87_ALARM_BASE] & M41ST87_ALMINS_RPT2)
++ res |= 1<<1;
++
++ if (buf[M41ST87_ALSECS - M41ST87_ALARM_BASE] & M41ST87_ALSECS_RPT1)
++ res |= 1<<0;
++
++ return res;
++}
++
++
++static int
++m41st87_rtc_proc_output( char *buf)
++{
++ struct m41st87_info *mi = (struct m41st87_info *)m41st87_i2c_client->data;
++ unsigned char ram[M41ST87_OVERALL_SIZE];
++ int ret;
++
++ char *p = buf;
++
++ if (mi == NULL) {
++ return sprintf(p, "Error: no struct info\n");
++ }
++
++ ret = m41st87_readram( ram, M41ST87_OVERALL_SIZE);
++
++ if( ret > 0)
++ {
++ int i;
++ struct rtc_time dt;
++ char text[9];
++
++ p += sprintf(p, "M41ST87\n");
++
++
++#if DEBUG
++ p += sprintf(p, "Debug level : %d\n", rtc_debug);
++#endif
++
++ m41st87_convert_to_time( &dt, ram + M41ST87_SECONDS);
++ p += sprintf(p, "Date/Time : %02d-%s-%04d %02d:%02d:%02d\n",
++ dt.tm_mday, m41st87_mon2str(dt.tm_mon), dt.tm_year + 1900,
++ dt.tm_hour, dt.tm_min, dt.tm_sec);
++
++ p += sprintf(p, "Serial number : ");
++ for (i = 0; i < M41ST87_SERIAL_SIZE; i++)
++ p += sprintf(p, "%02x", ram[M41ST87_SERIAL + i]);
++ p += sprintf(p, "\n");
++
++ if (mi->ht_reg & M41ST87_ALHOUR_HT) {
++ m41st87_convert_to_time( &dt, mi->init_reg);
++
++ p += sprintf(p, "Halt Date/Time : %02d-%s-%04d %02d:%02d:%02d\n",
++ dt.tm_mday, m41st87_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, "Square wave enabled : %s\n", CHECK(ram[7],0x10));
++ p += sprintf(p, "Frequency : %s\n", m41st87_sqw[ram[0x13]>>4]);
++ p += sprintf(p, "Oscillator running : %s\n", NCHECK(ram[M41ST87_SECONDS_ST], M41ST87_SECONDS_ST));
++ p += sprintf(p, "Oscillator fail : %s\n", CHECK(ram[M41ST87_FLAGS], M41ST87_FLAGS_OF));
++ p += sprintf(p, "Battery low : %s\n", CHECK(ram[M41ST87_FLAGS], M41ST87_FLAGS_BL));
++
++ p += sprintf(p, "Oscillator Fail IRQ : %s\n", ENABLED(ram[M41ST87_MINUTES], M41ST87_MINUTES_OFIE));
++
++ m41st87_convert_alarm_to_time(&dt, ram + M41ST87_ALARM_BASE);
++
++ p += sprintf(p, "Alarm time : %02d-%s %02d:%02d:%02d\n",
++ dt.tm_mday, m41st87_mon2str(dt.tm_mon),
++ dt.tm_hour, dt.tm_min, dt.tm_sec);
++
++ p += sprintf(p, "Alarm repeat : every %s\n",
++ m41st87_alarm_rpt(m41st87_decode_alarm_rpt(ram+M41ST87_ALARM_BASE)));
++
++#if 0
++ /* automatically clear the oscillator fail bit */
++
++ if (ram[M41ST87_FLAGS] & M41ST87_FLAGS_OF) {
++ ram[M41ST87_FLAGS] &= ~M41ST87_FLAGS_OF;
++
++ p += sprintf(p, "Cleared oscillator fail\n");
++
++ m41st87_i2c_write(m41st87_i2c_client, M41ST87_FLAGS, &ram[M41ST87_FLAGS], 1);
++ }
++
++#endif
++
++ p += sprintf(p, "\n");
++
++ p += m41st87_show_tamper(p, ram[M41ST87_TAMPER1], 1,
++ ram[M41ST87_FLAGS] & M41ST87_FLAGS_TB1);
++ p += m41st87_show_tamper(p, ram[M41ST87_TAMPER2], 2,
++ ram[M41ST87_FLAGS] & M41ST87_FLAGS_TB2);
++
++ p += sprintf(p, "RAM dump:\n");
++ text[8]='\0';
++ for( i=0; i<M41ST87_RAM_SIZE; i++)
++ {
++ p += sprintf(p, "%02X ", ram[i]);
++
++ if( (ram[i] < 32) || (ram[i]>126)) 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
++m41st87_rtc_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int len = m41st87_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 m41st87_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 M41ST87 RTC (%#x)\n",
++ normal_addr[0]);
++ return -EINVAL;
++ }
++
++ retval = i2c_add_driver(&m41st87_driver);
++
++ if (retval==0)
++ {
++ misc_register (&m41st87_rtc_miscdev);
++ create_proc_read_entry (PROC_M41ST87_NAME, 0, 0, m41st87_rtc_read_proc, NULL);
++ printk("I2C: M41ST87 RTC driver successfully loaded\n");
++
++ if (rtc_debug)
++ m41st87_dumpram();
++ }
++ return retval;
++}
++
++static __exit void m41st87_exit(void)
++{
++ remove_proc_entry (PROC_M41ST87_NAME, NULL);
++ misc_deregister(&m41st87_rtc_miscdev);
++ i2c_del_driver(&m41st87_driver);
++}
++
++module_init(m41st87_init);
++module_exit(m41st87_exit);
++
++#if DEBUG
++MODULE_PARM (rtc_debug, "i")
++MODULE_PARM_DESC(rtc_debug, "RTC debugging level");
++#endif
++
++MODULE_PARM (slave_address, "i");
++MODULE_PARM_DESC (slave_address, "I2C slave address for M41ST87 RTC.");
++
++MODULE_AUTHOR ("Intrinsyc Software Inc.");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/char/tty_io.c kernel-source-2.4.27-8-arm-1/drivers/char/tty_io.c
+--- kernel-source-2.4.27-8/drivers/char/tty_io.c 2005-01-19 09:57:54.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/tty_io.c 2005-02-18 17:48:42.000000000 +0000
+@@ -150,8 +150,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);
+@@ -163,6 +162,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
+@@ -2698,18 +2698,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
+@@ -2725,6 +2719,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;
+diff -urN kernel-source-2.4.27-8/drivers/char/tty_io.c.orig kernel-source-2.4.27-8-arm-1/drivers/char/tty_io.c.orig
+--- kernel-source-2.4.27-8/drivers/char/tty_io.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/tty_io.c.orig 2005-01-19 09:57:54.000000000 +0000
+@@ -0,0 +1,2873 @@
++/*
++ * linux/drivers/char/tty_io.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ */
++
++/*
++ * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
++ * or rs-channels. It also implements echoing, cooked mode etc.
++ *
++ * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
++ *
++ * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
++ * tty_struct and tty_queue structures. Previously there was an array
++ * of 256 tty_struct's which was statically allocated, and the
++ * tty_queue structures were allocated at boot time. Both are now
++ * dynamically allocated only when the tty is open.
++ *
++ * 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
++ *
++ * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
++ * which can be dynamically activated and de-activated by the line
++ * discipline handling modules (like SLIP).
++ *
++ * NOTE: pay no attention to the line discipline code (yet); its
++ * interface is still subject to change in this version...
++ * -- TYT, 1/31/92
++ *
++ * Added functionality to the OPOST tty handling. No delays, but all
++ * other bits should be there.
++ * -- Nick Holloway <alfie at dcs.warwick.ac.uk>, 27th May 1993.
++ *
++ * Rewrote canonical mode and added more termios flags.
++ * -- julian at uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
++ *
++ * Reorganized FASYNC support so mouse code can share it.
++ * -- ctm at ardi.com, 9Sep95
++ *
++ * New TIOCLINUX variants added.
++ * -- mj at k332.feld.cvut.cz, 19-Nov-95
++ *
++ * Restrict vt switching via ioctl()
++ * -- grif at cs.ucr.edu, 5-Dec-95
++ *
++ * Move console and virtual terminal code to more appropriate files,
++ * implement CONFIG_VT and generalize console device interface.
++ * -- Marko Kohtala <Marko.Kohtala at hut.fi>, March 97
++ *
++ * Rewrote init_dev and release_dev to eliminate races.
++ * -- Bill Hawes <whawes at star.net>, June 97
++ *
++ * Added devfs support.
++ * -- C. Scott Ananian <cananian at alumni.princeton.edu>, 13-Jan-1998
++ *
++ * Added support for a Unix98-style ptmx device.
++ * -- C. Scott Ananian <cananian at alumni.princeton.edu>, 14-Jan-1998
++ *
++ * Reduced memory usage for older ARM systems
++ * -- Russell King <rmk at arm.linux.org.uk>
++ *
++ * Move do_SAK() into process context. Less stack use in devfs functions.
++ * alloc_tty_struct() always uses kmalloc() -- Andrew Morton <andrewm at uow.edu.eu> 17Mar01
++ */
++
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/major.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/fcntl.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++#include <linux/devpts_fs.h>
++#include <linux/file.h>
++#include <linux/console.h>
++#include <linux/timer.h>
++#include <linux/ctype.h>
++#include <linux/kd.h>
++#include <linux/mm.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/smp_lock.h>
++
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++
++#include <linux/kbd_kern.h>
++#include <linux/vt_kern.h>
++#include <linux/selection.h>
++#include <linux/devfs_fs_kernel.h>
++
++#include <linux/kmod.h>
++
++#ifdef CONFIG_VT
++extern void con_init_devfs (void);
++#endif
++
++extern void disable_early_printk(void);
++
++#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
++#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
++#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
++#define PTMX_DEV MKDEV(TTYAUX_MAJOR,2)
++
++#undef TTY_DEBUG_HANGUP
++
++#define TTY_PARANOIA_CHECK 1
++#define CHECK_TTY_COUNT 1
++
++struct termios tty_std_termios; /* for the benefit of tty drivers */
++struct tty_driver *tty_drivers; /* linked list of tty drivers */
++
++#ifdef CONFIG_UNIX98_PTYS
++extern struct tty_driver ptm_driver[]; /* Unix98 pty masters; for /dev/ptmx */
++extern struct tty_driver pts_driver[]; /* Unix98 pty slaves; for /dev/ptmx */
++#endif
++
++static void initialize_tty_struct(struct tty_struct *tty);
++
++static ssize_t tty_read(struct file *, char *, size_t, loff_t *);
++static ssize_t tty_write(struct file *, const char *, size_t, loff_t *);
++static unsigned int tty_poll(struct file *, poll_table *);
++static int tty_open(struct inode *, struct file *);
++static int tty_release(struct inode *, struct file *);
++int tty_ioctl(struct inode * inode, struct file * file,
++ unsigned int cmd, unsigned long arg);
++static int tty_fasync(int fd, struct file * filp, int on);
++extern int vme_scc_init (void);
++extern long vme_scc_console_init(void);
++extern int serial167_init(void);
++extern long serial167_console_init(void);
++extern void console_8xx_init(void);
++extern void au1x00_serial_console_init(void);
++extern int rs_8xx_init(void);
++extern void mac_scc_console_init(void);
++extern void hwc_console_init(void);
++extern void hwc_tty_init(void);
++extern void con3215_init(void);
++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 sgi_serial_console_init(void);
++extern void sn_sal_serial_console_init(void);
++extern void sci_console_init(void);
++extern void dec_serial_console_init(void);
++extern void tx3912_console_init(void);
++extern void tx3912_rs_init(void);
++extern void txx927_console_init(void);
++extern void txx9_rs_init(void);
++extern void txx9_serial_console_init(void);
++extern void sb1250_serial_console_init(void);
++extern void arc_console_init(void);
++extern int hvc_console_init(void);
++
++#ifndef MIN
++#define MIN(a,b) ((a) < (b) ? (a) : (b))
++#endif
++#ifndef MAX
++#define MAX(a,b) ((a) < (b) ? (b) : (a))
++#endif
++
++static struct tty_struct *alloc_tty_struct(void)
++{
++ struct tty_struct *tty;
++
++ tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
++ if (tty)
++ memset(tty, 0, sizeof(struct tty_struct));
++ return tty;
++}
++
++static inline void free_tty_struct(struct tty_struct *tty)
++{
++ kfree(tty);
++}
++
++/*
++ * This routine returns the name of tty.
++ */
++static char *
++_tty_make_name(struct tty_struct *tty, const char *name, char *buf)
++{
++ int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;
++
++ if (!tty) /* Hmm. NULL pointer. That's fun. */
++ strcpy(buf, "NULL tty");
++ else
++ sprintf(buf, name,
++ idx + tty->driver.name_base);
++
++ return buf;
++}
++
++#define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \
++ (tty)->driver.name_base)
++
++char *tty_name(struct tty_struct *tty, char *buf)
++{
++ return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf);
++}
++
++inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
++ const char *routine)
++{
++#ifdef TTY_PARANOIA_CHECK
++ static const char badmagic[] = KERN_WARNING
++ "Warning: bad magic number for tty struct (%s) in %s\n";
++ static const char badtty[] = KERN_WARNING
++ "Warning: null TTY for (%s) in %s\n";
++
++ if (!tty) {
++ printk(badtty, kdevname(device), routine);
++ return 1;
++ }
++ if (tty->magic != TTY_MAGIC) {
++ printk(badmagic, kdevname(device), routine);
++ return 1;
++ }
++#endif
++ return 0;
++}
++
++static int check_tty_count(struct tty_struct *tty, const char *routine)
++{
++#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)
++ count++;
++ }
++ file_list_unlock();
++ if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
++ tty->driver.subtype == PTY_TYPE_SLAVE &&
++ tty->link && tty->link->count)
++ count++;
++ if (tty->count != count) {
++ printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
++ "!= #fd's(%d) in %s\n",
++ kdevname(tty->device), tty->count, count, routine);
++ return count;
++ }
++#endif
++ return 0;
++}
++
++/*
++ * This is probably overkill for real world processors but
++ * they are not on hot paths so a little discipline won't do
++ * any harm.
++ */
++
++static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
++{
++ down(&tty->termios_sem);
++ tty->termios->c_line = num;
++ up(&tty->termios_sem);
++}
++
++/*
++ * This guards the refcounted line discipline lists. The lock
++ * must be taken with irqs off because there are hangup path
++ * callers who will do ldisc lookups and cannot sleep.
++ */
++
++spinlock_t tty_ldisc_lock = SPIN_LOCK_UNLOCKED;
++DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
++struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */
++
++int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
++{
++
++ unsigned long flags;
++ int ret = 0;
++
++ if (disc < N_TTY || disc >= NR_LDISCS)
++ return -EINVAL;
++
++ spin_lock_irqsave(&tty_ldisc_lock, flags);
++ if (new_ldisc) {
++ tty_ldiscs[disc] = *new_ldisc;
++ tty_ldiscs[disc].num = disc;
++ tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
++ tty_ldiscs[disc].refcount = 0;
++ } else {
++ if(tty_ldiscs[disc].refcount)
++ ret = -EBUSY;
++ else
++ tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
++ }
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++
++ return ret;
++
++}
++
++
++EXPORT_SYMBOL(tty_register_ldisc);
++
++struct tty_ldisc *tty_ldisc_get(int disc)
++{
++ unsigned long flags;
++ struct tty_ldisc *ld;
++
++ if (disc < N_TTY || disc >= NR_LDISCS)
++ return NULL;
++
++ spin_lock_irqsave(&tty_ldisc_lock, flags);
++
++ ld = &tty_ldiscs[disc];
++ /* Check the entry is defined */
++ if(ld->flags & LDISC_FLAG_DEFINED)
++ ld->refcount++;
++ else
++ ld = NULL;
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++ return ld;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_get);
++
++void tty_ldisc_put(int disc)
++{
++ struct tty_ldisc *ld;
++ unsigned long flags;
++
++ if (disc < N_TTY || disc >= NR_LDISCS)
++ BUG();
++
++ spin_lock_irqsave(&tty_ldisc_lock, flags);
++ ld = &tty_ldiscs[disc];
++ if(ld->refcount <= 0)
++ BUG();
++ ld->refcount--;
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_put);
++
++void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
++{
++ tty->ldisc = *ld;
++ tty->ldisc.refcount = 0;
++}
++
++/**
++ * tty_ldisc_try - internal helper
++ * @tty: the tty
++ *
++ * Make a single attempt to grab and bump the refcount on
++ * the tty ldisc. Return 0 on failure or 1 on success. This is
++ * used to implement both the waiting and non waiting versions
++ * of tty_ldisc_ref
++ */
++
++static int tty_ldisc_try(struct tty_struct *tty)
++{
++ unsigned long flags;
++ struct tty_ldisc *ld;
++ int ret = 0;
++
++ spin_lock_irqsave(&tty_ldisc_lock, flags);
++ ld = &tty->ldisc;
++ if(test_bit(TTY_LDISC, &tty->flags))
++ {
++ ld->refcount++;
++ ret = 1;
++ }
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++ return ret;
++}
++
++/**
++ * tty_ldisc_ref_wait - wait for the tty ldisc
++ * @tty: tty device
++ *
++ * Dereference the line discipline for the terminal and take a
++ * reference to it. If the line discipline is in flux then
++ * wait patiently until it changes.
++ *
++ * Note: Must not be called from an IRQ/timer context. The caller
++ * must also be careful not to hold other locks that will deadlock
++ * against a discipline change, such as an existing ldisc reference
++ * (which we check for)
++ */
++
++struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
++{
++ /* wait_event is a macro */
++ wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
++ return &tty->ldisc;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
++
++/**
++ * tty_ldisc_ref - get the tty ldisc
++ * @tty: tty device
++ *
++ * Dereference the line discipline for the terminal and take a
++ * reference to it. If the line discipline is in flux then
++ * return NULL. Can be called from IRQ and timer functions.
++ */
++
++struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
++{
++ if(tty_ldisc_try(tty))
++ return &tty->ldisc;
++ return NULL;
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_ref);
++
++
++void tty_ldisc_deref(struct tty_ldisc *ld)
++{
++
++ unsigned long flags;
++
++ if(ld == NULL)
++ BUG();
++
++ spin_lock_irqsave(&tty_ldisc_lock, flags);
++ if(ld->refcount == 0)
++ printk(KERN_EMERG "tty_ldisc_deref: no references.\n");
++ else
++ ld->refcount--;
++ if(ld->refcount == 0)
++ wake_up(&tty_ldisc_wait);
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_deref);
++
++/**
++ * tty_ldisc_enable - allow ldisc use
++ * @tty: terminal to activate ldisc on
++ *
++ * Set the TTY_LDISC flag when the line discipline can be called
++ * again. Do neccessary wakeups for existing sleepers.
++ *
++ * Note: nobody should set this bit except via this function. Clearing
++ * directly is allowed.
++ */
++
++static void tty_ldisc_enable(struct tty_struct *tty)
++{
++ set_bit(TTY_LDISC, &tty->flags);
++ wake_up(&tty_ldisc_wait);
++}
++
++/**
++ * tty_set_ldisc - set line discipline
++ * @tty: the terminal to set
++ * @ldisc: the line discipline
++ *
++ * Set the discipline of a tty line. Must be called from a process
++ * context.
++ */
++
++static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
++{
++ int retval = 0;
++ struct tty_ldisc o_ldisc;
++ char buf[64];
++ int work;
++ unsigned long flags;
++ struct tty_ldisc *ld;
++
++ if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
++ return -EINVAL;
++
++restart:
++
++ if (tty->ldisc.num == ldisc)
++ return 0; /* We are already in the desired discipline */
++
++ ld = tty_ldisc_get(ldisc);
++ /* Eduardo Blanco <ejbs at cs.cs.com.uy> */
++ /* Cyrus Durgin <cider at speakeasy.org> */
++ if (ld == NULL)
++ {
++ char modname [20];
++ sprintf(modname, "tty-ldisc-%d", ldisc);
++ request_module (modname);
++ ld = tty_ldisc_get(ldisc);
++ }
++
++ if (ld == NULL)
++ return -EINVAL;
++
++
++ o_ldisc = tty->ldisc;
++ tty_wait_until_sent(tty, 0);
++
++ /*
++ * Make sure we don't change while someone holds a
++ * reference to the line discipline. The TTY_LDISC bit
++ * prevents anyone taking a reference once it is clear.
++ * We need the lock to avoid racing reference takers.
++ */
++
++ spin_lock_irqsave(&tty_ldisc_lock, flags);
++ if(tty->ldisc.refcount)
++ {
++ /* Free the new ldisc we grabbed. Must drop the lock
++ first. */
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++ tty_ldisc_put(ldisc);
++ /*
++ * There are several reasons we may be busy, including
++ * random momentary I/O traffic. We must therefore
++ * retry. We could distinguish between blocking ops
++ * and retries if we made tty_ldisc_wait() smarter. That
++ * is up for discussion.
++ */
++ if(wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
++ return -ERESTARTSYS;
++ goto restart;
++ }
++ clear_bit(TTY_LDISC, &tty->flags);
++ clear_bit(TTY_DONT_FLIP, &tty->flags);
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++
++ /*
++ * From this point on we know nobody has an ldisc
++ * usage reference, nor can they obtain one until
++ * we say so later on.
++ */
++
++ /*
++ * Wait for ->hangup_work and ->flip.work handlers to terminate
++ */
++ run_task_queue(&tq_timer);
++ flush_scheduled_tasks();
++
++ /* Shutdown the current discipline. */
++ if (tty->ldisc.close)
++ (tty->ldisc.close)(tty);
++
++ /* Now set up the new line discipline. */
++ tty_ldisc_assign(tty, ld);
++ tty_set_termios_ldisc(tty, ldisc);
++ if (tty->ldisc.open)
++ retval = (tty->ldisc.open)(tty);
++ if (retval < 0) {
++ tty_ldisc_put(ldisc);
++ /* There is an outstanding reference here so this is safe */
++ tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num));
++ tty_set_termios_ldisc(tty, tty->ldisc.num);
++ if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
++ tty_ldisc_put(o_ldisc.num);
++ /* This driver is always present */
++ tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
++ tty_set_termios_ldisc(tty, N_TTY);
++ if (tty->ldisc.open) {
++ int r = tty->ldisc.open(tty);
++
++ if (r < 0)
++ panic("Couldn't open N_TTY ldisc for "
++ "%s --- error %d.",
++ tty_name(tty, buf), r);
++ }
++ }
++ }
++ /* At this point we hold a reference to the new ldisc and a
++ reference to the old ldisc. If we ended up flipping back
++ to the existing ldisc we have two references to it */
++
++ if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc)
++ tty->driver.set_ldisc(tty);
++
++ tty_ldisc_put(o_ldisc.num);
++
++ /*
++ * Allow ldisc referencing to occur as soon as the driver
++ * ldisc callback completes.
++ */
++ tty_ldisc_enable(tty);
++
++ return retval;
++}
++
++/*
++ * This routine returns a tty driver structure, given a device number
++ */
++struct tty_driver *get_tty_driver(kdev_t device)
++{
++ int major, minor;
++ struct tty_driver *p;
++
++ minor = MINOR(device);
++ major = MAJOR(device);
++
++ for (p = tty_drivers; p; p = p->next) {
++ if (p->major != major)
++ continue;
++ if (minor < p->minor_start)
++ continue;
++ if (minor >= p->minor_start + p->num)
++ continue;
++ return p;
++ }
++ return NULL;
++}
++
++/*
++ * If we try to write to, or set the state of, a terminal and we're
++ * not in the foreground, send a SIGTTOU. If the signal is blocked or
++ * ignored, go ahead and perform the operation. (POSIX 7.2)
++ */
++int tty_check_change(struct tty_struct * tty)
++{
++ if (current->tty != tty)
++ return 0;
++ if (tty->pgrp <= 0) {
++ printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n");
++ return 0;
++ }
++ if (current->pgrp == tty->pgrp)
++ return 0;
++ if (is_ignored(SIGTTOU))
++ return 0;
++ if (is_orphaned_pgrp(current->pgrp))
++ return -EIO;
++ (void) kill_pg(current->pgrp,SIGTTOU,1);
++ return -ERESTARTSYS;
++}
++
++static ssize_t hung_up_tty_read(struct file * file, char * buf,
++ size_t count, loff_t *ppos)
++{
++ /* Can't seek (pread) on ttys. */
++ if (ppos != &file->f_pos)
++ return -ESPIPE;
++ return 0;
++}
++
++static ssize_t hung_up_tty_write(struct file * file, const char * buf,
++ size_t count, loff_t *ppos)
++{
++ /* Can't seek (pwrite) on ttys. */
++ if (ppos != &file->f_pos)
++ return -ESPIPE;
++ return -EIO;
++}
++
++/* No kernel lock held - none needed ;) */
++static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait)
++{
++ return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
++}
++
++static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
++ unsigned int cmd, unsigned long arg)
++{
++ return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
++}
++
++static struct file_operations tty_fops = {
++ llseek: no_llseek,
++ read: tty_read,
++ write: tty_write,
++ poll: tty_poll,
++ ioctl: tty_ioctl,
++ open: tty_open,
++ release: tty_release,
++ fasync: tty_fasync,
++};
++
++static struct file_operations hung_up_tty_fops = {
++ llseek: no_llseek,
++ read: hung_up_tty_read,
++ write: hung_up_tty_write,
++ poll: hung_up_tty_poll,
++ ioctl: hung_up_tty_ioctl,
++ release: tty_release,
++};
++
++static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED;
++static struct file *redirect;
++
++/**
++ * tty_wakeup - request more data
++ * @tty: terminal
++ *
++ * Internal and external helper for wakeups of tty. This function
++ * informs the line discipline if present that the driver is ready\
++ * to receive more output data.
++ */
++
++void tty_wakeup(struct tty_struct *tty)
++{
++ struct tty_ldisc *ld;
++
++ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
++ ld = tty_ldisc_ref(tty);
++ if(ld) {
++ if(ld->write_wakeup)
++ ld->write_wakeup(tty);
++ tty_ldisc_deref(ld);
++ }
++ }
++ wake_up_interruptible(&tty->write_wait);
++}
++
++EXPORT_SYMBOL_GPL(tty_wakeup);
++
++void tty_ldisc_flush(struct tty_struct *tty)
++{
++ struct tty_ldisc *ld = tty_ldisc_ref(tty);
++ if(ld) {
++ if(ld->flush_buffer)
++ ld->flush_buffer(tty);
++ tty_ldisc_deref(ld);
++ }
++}
++
++EXPORT_SYMBOL_GPL(tty_ldisc_flush);
++
++void do_tty_hangup(void *data)
++{
++ struct tty_struct *tty = (struct tty_struct *) data;
++ struct file * cons_filp = NULL;
++ struct file *f = NULL;
++ struct task_struct *p;
++ struct list_head *l;
++ struct tty_ldisc *ld;
++ int closecount = 0, n;
++
++ if (!tty)
++ return;
++
++ /* inuse_filps is protected by the single kernel lock */
++ lock_kernel();
++
++ spin_lock(&redirect_lock);
++ if (redirect && redirect->private_data == tty) {
++ f = redirect;
++ 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) {
++ struct file * filp = list_entry(l, struct file, f_list);
++ if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV ||
++ filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) {
++ cons_filp = filp;
++ continue;
++ }
++ if (filp->f_op != &tty_fops)
++ continue;
++ closecount++;
++ tty_fasync(-1, filp, 0); /* can't block */
++ filp->f_op = &hung_up_tty_fops;
++ }
++ file_list_unlock();
++
++ /* FIXME! What are the locking issues here? This may me overdoing things.. */
++ ld = tty_ldisc_ref(tty);
++ if(ld != NULL)
++ {
++ if (ld->flush_buffer)
++ ld->flush_buffer(tty);
++ if (tty->driver.flush_buffer)
++ tty->driver.flush_buffer(tty);
++ if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && ld->write_wakeup)
++ ld->write_wakeup(tty);
++ if (ld->hangup)
++ ld->hangup(tty);
++ }
++
++ /* FIXME: Once we trust the LDISC code better we can wait here for
++ ldisc completion and fix the driver call race */
++
++ wake_up_interruptible(&tty->write_wait);
++ wake_up_interruptible(&tty->read_wait);
++
++ /*
++ * Shutdown the current line discipline, and reset it to
++ * N_TTY.
++ */
++
++ if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
++ {
++ down(&tty->termios_sem);
++ *tty->termios = tty->driver.init_termios;
++ up(&tty->termios_sem);
++ }
++
++ /* Defer ldisc switch */
++ /* tty_deferred_ldisc_switch(N_TTY)
++ This should get done automatically when the port closes and
++ tty_release is called */
++
++ read_lock(&tasklist_lock);
++ for_each_task(p) {
++ if ((tty->session > 0) && (p->session == tty->session) &&
++ p->leader) {
++ send_sig(SIGHUP,p,1);
++ send_sig(SIGCONT,p,1);
++ if (tty->pgrp > 0)
++ p->tty_old_pgrp = tty->pgrp;
++ }
++ if (p->tty == tty)
++ p->tty = NULL;
++ }
++ read_unlock(&tasklist_lock);
++
++ tty->flags = 0;
++ tty->session = 0;
++ tty->pgrp = -1;
++ tty->ctrl_status = 0;
++ /*
++ * If one of the devices matches a console pointer, we
++ * cannot just call hangup() because that will cause
++ * tty->count and state->count to go out of sync.
++ * So we just call close() the right number of times.
++ */
++ if (cons_filp) {
++ if (tty->driver.close)
++ for (n = 0; n < closecount; n++)
++ tty->driver.close(tty, cons_filp);
++ } else if (tty->driver.hangup)
++ (tty->driver.hangup)(tty);
++
++ /* We don't want to have driver/ldisc interactions beyond
++ the ones we did here. The driver layer expects no
++ calls after ->hangup() from the ldisc side. However we
++ can't yet guarantee all that */
++
++ set_bit(TTY_HUPPED, &tty->flags);
++ if(ld) {
++ tty_ldisc_enable(tty);
++ tty_ldisc_deref(ld);
++ }
++ unlock_kernel();
++ if (f)
++ fput(f);
++}
++
++void tty_hangup(struct tty_struct * tty)
++{
++#ifdef TTY_DEBUG_HANGUP
++ char buf[64];
++
++ printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
++#endif
++ schedule_task(&tty->tq_hangup);
++}
++
++void tty_vhangup(struct tty_struct * tty)
++{
++#ifdef TTY_DEBUG_HANGUP
++ char buf[64];
++
++ printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
++#endif
++ do_tty_hangup((void *) tty);
++}
++
++int tty_hung_up_p(struct file * filp)
++{
++ return (filp->f_op == &hung_up_tty_fops);
++}
++
++/*
++ * This function is typically called only by the session leader, when
++ * it wants to disassociate itself from its controlling tty.
++ *
++ * It performs the following functions:
++ * (1) Sends a SIGHUP and SIGCONT to the foreground process group
++ * (2) Clears the tty from being controlling the session
++ * (3) Clears the controlling tty for all processes in the
++ * session group.
++ *
++ * The argument on_exit is set to 1 if called when a process is
++ * exiting; it is 0 if called by the ioctl TIOCNOTTY.
++ */
++void disassociate_ctty(int on_exit)
++{
++ struct tty_struct *tty = current->tty;
++ struct task_struct *p;
++ int tty_pgrp = -1;
++
++ if (tty) {
++ tty_pgrp = tty->pgrp;
++ if (on_exit && tty->driver.type != TTY_DRIVER_TYPE_PTY)
++ tty_vhangup(tty);
++ } else {
++ if (current->tty_old_pgrp) {
++ kill_pg(current->tty_old_pgrp, SIGHUP, on_exit);
++ kill_pg(current->tty_old_pgrp, SIGCONT, on_exit);
++ }
++ return;
++ }
++ if (tty_pgrp > 0) {
++ kill_pg(tty_pgrp, SIGHUP, on_exit);
++ if (!on_exit)
++ kill_pg(tty_pgrp, SIGCONT, on_exit);
++ }
++
++ current->tty_old_pgrp = 0;
++ tty->session = 0;
++ tty->pgrp = -1;
++
++ read_lock(&tasklist_lock);
++ for_each_task(p)
++ if (p->session == current->session)
++ p->tty = NULL;
++ read_unlock(&tasklist_lock);
++}
++
++void stop_tty(struct tty_struct *tty)
++{
++ if (tty->stopped)
++ return;
++ tty->stopped = 1;
++ if (tty->link && tty->link->packet) {
++ tty->ctrl_status &= ~TIOCPKT_START;
++ tty->ctrl_status |= TIOCPKT_STOP;
++ wake_up_interruptible(&tty->link->read_wait);
++ }
++ if (tty->driver.stop)
++ (tty->driver.stop)(tty);
++}
++
++void start_tty(struct tty_struct *tty)
++{
++ if (!tty->stopped || tty->flow_stopped)
++ return;
++ tty->stopped = 0;
++ if (tty->link && tty->link->packet) {
++ tty->ctrl_status &= ~TIOCPKT_STOP;
++ tty->ctrl_status |= TIOCPKT_START;
++ wake_up_interruptible(&tty->link->read_wait);
++ }
++ if (tty->driver.start)
++ (tty->driver.start)(tty);
++ /* If we have a running line discipline it may need kicking */
++ tty_wakeup(tty);
++}
++
++static ssize_t tty_read(struct file * file, char * buf, size_t count,
++ loff_t *ppos)
++{
++ int i;
++ struct tty_struct * tty;
++ struct inode *inode;
++ struct tty_ldisc *ld;
++
++ /* Can't seek (pread) on ttys. */
++ if (ppos != &file->f_pos)
++ return -ESPIPE;
++
++ tty = (struct tty_struct *)file->private_data;
++ inode = file->f_dentry->d_inode;
++ if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
++ return -EIO;
++ if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
++ return -EIO;
++
++ /* This check not only needs to be done before reading, but also
++ whenever read_chan() gets woken up after sleeping, so I've
++ moved it to there. This should only be done for the N_TTY
++ line discipline, anyway. Same goes for write_chan(). -- jlc. */
++#if 0
++ if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */
++ (tty->pgrp > 0) &&
++ (current->tty == tty) &&
++ (tty->pgrp != current->pgrp))
++ if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))
++ return -EIO;
++ else {
++ (void) kill_pg(current->pgrp, SIGTTIN, 1);
++ return -ERESTARTSYS;
++ }
++#endif
++ /* We want to wait for the line discipline to sort out in this
++ situation */
++ ld = tty_ldisc_ref_wait(tty);
++ lock_kernel();
++ if (ld->read)
++ i = (ld->read)(tty,file,buf,count);
++ else
++ i = -EIO;
++ tty_ldisc_deref(ld);
++ unlock_kernel();
++ if (i > 0)
++ inode->i_atime = CURRENT_TIME;
++ return i;
++}
++
++/*
++ * Split writes up in sane blocksizes to avoid
++ * denial-of-service type attacks
++ */
++static inline ssize_t do_tty_write(
++ ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
++ struct tty_struct *tty,
++ struct file *file,
++ const unsigned char *buf,
++ size_t count)
++{
++ ssize_t ret = 0, written = 0;
++
++ if (file->f_flags & O_NONBLOCK) {
++ if (down_trylock(&tty->atomic_write))
++ return -EAGAIN;
++ }
++ else {
++ if (down_interruptible(&tty->atomic_write))
++ return -ERESTARTSYS;
++ }
++ if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) {
++ lock_kernel();
++ written = write(tty, file, buf, count);
++ unlock_kernel();
++ } else {
++ for (;;) {
++ unsigned long size = MAX(PAGE_SIZE*2,16384);
++ if (size > count)
++ size = count;
++ lock_kernel();
++ ret = write(tty, file, buf, size);
++ unlock_kernel();
++ if (ret <= 0)
++ break;
++ written += ret;
++ buf += ret;
++ count -= ret;
++ if (!count)
++ break;
++ ret = -ERESTARTSYS;
++ if (signal_pending(current))
++ break;
++ if (current->need_resched)
++ schedule();
++ }
++ }
++ if (written) {
++ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
++ ret = written;
++ }
++ up(&tty->atomic_write);
++ return ret;
++}
++
++
++static ssize_t tty_write(struct file * file, const char * buf, size_t count,
++ loff_t *ppos)
++{
++ int is_console;
++ struct tty_struct * tty;
++ struct inode *inode = file->f_dentry->d_inode;
++ ssize_t ret;
++ struct tty_ldisc *ld;
++
++ /* Can't seek (pwrite) on ttys. */
++ if (ppos != &file->f_pos)
++ return -ESPIPE;
++
++ /*
++ * For now, we redirect writes from /dev/console as
++ * well as /dev/tty0.
++ */
++ inode = file->f_dentry->d_inode;
++ is_console = (inode->i_rdev == SYSCONS_DEV ||
++ inode->i_rdev == CONSOLE_DEV);
++
++ if (is_console) {
++ struct file *p = NULL;
++
++ spin_lock(&redirect_lock);
++ if (redirect) {
++ get_file(redirect);
++ p = redirect;
++ }
++ spin_unlock(&redirect_lock);
++
++ if (p) {
++ ssize_t res = p->f_op->write(p, buf, count, &p->f_pos);
++ fput(p);
++ return res;
++ }
++ }
++
++ tty = (struct tty_struct *)file->private_data;
++ if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
++ return -EIO;
++ if (!tty || !tty->driver.write || (test_bit(TTY_IO_ERROR, &tty->flags)))
++ return -EIO;
++#if 0
++ if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
++ (current->tty == tty) && (tty->pgrp != current->pgrp)) {
++ if (is_orphaned_pgrp(current->pgrp))
++ return -EIO;
++ if (!is_ignored(SIGTTOU)) {
++ (void) kill_pg(current->pgrp, SIGTTOU, 1);
++ return -ERESTARTSYS;
++ }
++ }
++#endif
++
++ ld = tty_ldisc_ref_wait(tty);
++ if (!ld->write)
++ ret = -EIO;
++ else
++ ret = do_tty_write(ld->write, tty, file,
++ (const unsigned char __user *)buf, count);
++ tty_ldisc_deref(ld);
++ return ret;
++}
++
++/* Semaphore to protect creating and releasing a tty. This is shared with
++ vt.c for deeply disgusting hack reasons */
++static DECLARE_MUTEX(tty_sem);
++
++static void down_tty_sem(int index)
++{
++ down(&tty_sem);
++}
++
++static void up_tty_sem(int index)
++{
++ up(&tty_sem);
++}
++
++static void release_mem(struct tty_struct *tty, int idx);
++
++/*
++ * WSH 06/09/97: Rewritten to remove races and properly clean up after a
++ * failed open. The new code protects the open with a semaphore, so it's
++ * really quite straightforward. The semaphore locking can probably be
++ * relaxed for the (most common) case of reopening a tty.
++ */
++static int init_dev(kdev_t device, struct tty_struct **ret_tty)
++{
++ 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;
++ int retval=0;
++ int idx;
++
++ driver = get_tty_driver(device);
++ if (!driver)
++ return -ENODEV;
++
++ idx = MINOR(device) - driver->minor_start;
++
++ /*
++ * Check whether we need to acquire the tty semaphore to avoid
++ * race conditions. For now, play it safe.
++ */
++ down_tty_sem(idx);
++
++ /* check whether we're reopening an existing tty */
++ tty = driver->table[idx];
++ if (tty) goto fast_track;
++
++ /*
++ * 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 locked termios may be retained.)
++ */
++
++ o_tty = NULL;
++ tp = o_tp = NULL;
++ ltp = o_ltp = NULL;
++
++ tty = alloc_tty_struct();
++ if(!tty)
++ goto fail_no_mem;
++ initialize_tty_struct(tty);
++ tty->device = device;
++ tty->driver = *driver;
++
++ tp_loc = &driver->termios[idx];
++ if (!*tp_loc) {
++ tp = (struct termios *) kmalloc(sizeof(struct termios),
++ GFP_KERNEL);
++ if (!tp)
++ goto free_mem_out;
++ *tp = driver->init_termios;
++ }
++
++ ltp_loc = &driver->termios_locked[idx];
++ if (!*ltp_loc) {
++ ltp = (struct termios *) kmalloc(sizeof(struct termios),
++ GFP_KERNEL);
++ if (!ltp)
++ goto free_mem_out;
++ memset(ltp, 0, sizeof(struct termios));
++ }
++
++ if (driver->type == TTY_DRIVER_TYPE_PTY) {
++ o_tty = alloc_tty_struct();
++ if (!o_tty)
++ goto free_mem_out;
++ initialize_tty_struct(o_tty);
++ o_tty->device = (kdev_t) MKDEV(driver->other->major,
++ driver->other->minor_start + idx);
++ o_tty->driver = *driver->other;
++
++ o_tp_loc = &driver->other->termios[idx];
++ if (!*o_tp_loc) {
++ o_tp = (struct termios *)
++ kmalloc(sizeof(struct termios), GFP_KERNEL);
++ if (!o_tp)
++ goto free_mem_out;
++ *o_tp = driver->other->init_termios;
++ }
++
++ o_ltp_loc = &driver->other->termios_locked[idx];
++ if (!*o_ltp_loc) {
++ o_ltp = (struct termios *)
++ kmalloc(sizeof(struct termios), GFP_KERNEL);
++ if (!o_ltp)
++ goto free_mem_out;
++ memset(o_ltp, 0, sizeof(struct termios));
++ }
++
++ /*
++ * Everything allocated ... set up the o_tty structure.
++ */
++ driver->other->table[idx] = o_tty;
++ if (!*o_tp_loc)
++ *o_tp_loc = o_tp;
++ if (!*o_ltp_loc)
++ *o_ltp_loc = o_ltp;
++ o_tty->termios = *o_tp_loc;
++ o_tty->termios_locked = *o_ltp_loc;
++ (*driver->other->refcount)++;
++ if (driver->subtype == PTY_TYPE_MASTER)
++ o_tty->count++;
++
++ /* Establish the links in both directions */
++ tty->link = o_tty;
++ 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
++ * there's no need to null out the local pointers.
++ */
++ driver->table[idx] = tty;
++
++ if (!*tp_loc)
++ *tp_loc = tp;
++ if (!*ltp_loc)
++ *ltp_loc = ltp;
++ tty->termios = *tp_loc;
++ tty->termios_locked = *ltp_loc;
++ (*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.
++ */
++ if (tty->ldisc.open) {
++ retval = (tty->ldisc.open)(tty);
++ if (retval)
++ goto release_mem_out;
++ }
++ if (o_tty && o_tty->ldisc.open) {
++ retval = (o_tty->ldisc.open)(o_tty);
++ if (retval) {
++ if (tty->ldisc.close)
++ (tty->ldisc.close)(tty);
++ goto release_mem_out;
++ }
++ set_bit(TTY_LDISC, &o_tty->flags);
++ tty_ldisc_enable(o_tty);
++ }
++ tty_ldisc_enable(tty);
++ goto success;
++
++ /*
++ * This fast open can be used if the tty is already open.
++ * No memory is allocated, and the only failures are from
++ * attempting to open a closing tty or attempting multiple
++ * opens on a pty master.
++ */
++fast_track:
++ if (test_bit(TTY_CLOSING, &tty->flags)) {
++ retval = -EIO;
++ goto end_init;
++ }
++ if (driver->type == TTY_DRIVER_TYPE_PTY &&
++ driver->subtype == PTY_TYPE_MASTER) {
++ /*
++ * special case for PTY masters: only one open permitted,
++ * and the slave side open count is incremented as well.
++ */
++ if (tty->count) {
++ retval = -EIO;
++ goto end_init;
++ }
++ tty->link->count++;
++ }
++ tty->count++;
++ tty->driver = *driver; /* N.B. why do this every time?? */
++ /* FIXME */
++ if(!test_bit(TTY_LDISC, &tty->flags))
++ printk(KERN_ERR "init_dev but no ldisc\n");
++success:
++ *ret_tty = tty;
++
++ /* All paths come through here to release the semaphore */
++end_init:
++ up_tty_sem(idx);
++ return retval;
++
++ /* Release locally allocated memory ... nothing placed in slots */
++free_mem_out:
++ if (o_tp)
++ kfree(o_tp);
++ if (o_tty)
++ free_tty_struct(o_tty);
++ if (ltp)
++ kfree(ltp);
++ if (tp)
++ kfree(tp);
++ free_tty_struct(tty);
++
++fail_no_mem:
++ retval = -ENOMEM;
++ goto end_init;
++
++ /* call the tty release_mem routine to clean out this slot */
++release_mem_out:
++ printk(KERN_INFO "init_dev: ldisc open failed, "
++ "clearing slot %d\n", idx);
++ release_mem(tty, idx);
++ goto end_init;
++}
++
++/*
++ * Releases memory associated with a tty structure, and clears out the
++ * driver table slots.
++ */
++static void release_mem(struct tty_struct *tty, int idx)
++{
++ struct tty_struct *o_tty;
++ struct termios *tp;
++
++ if ((o_tty = tty->link) != NULL) {
++ o_tty->driver.table[idx] = NULL;
++ if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
++ tp = o_tty->driver.termios[idx];
++ o_tty->driver.termios[idx] = NULL;
++ kfree(tp);
++ }
++ o_tty->magic = 0;
++ (*o_tty->driver.refcount)--;
++ list_del_init(&o_tty->tty_files);
++ free_tty_struct(o_tty);
++ }
++
++ tty->driver.table[idx] = NULL;
++ if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
++ tp = tty->driver.termios[idx];
++ tty->driver.termios[idx] = NULL;
++ kfree(tp);
++ }
++ tty->magic = 0;
++ (*tty->driver.refcount)--;
++ list_del_init(&tty->tty_files);
++ free_tty_struct(tty);
++}
++
++/*
++ * Even releasing the tty structures is a tricky business.. We have
++ * to be very careful that the structures are all released at the
++ * same time, as interrupts might otherwise get the wrong pointers.
++ *
++ * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
++ * lead to double frees or releasing memory still in use.
++ */
++static void release_dev(struct file * filp)
++{
++ struct tty_struct *tty, *o_tty;
++ int pty_master, tty_closing, o_tty_closing, do_sleep;
++ int idx;
++ char buf[64];
++ unsigned long flags;
++
++ tty = (struct tty_struct *)filp->private_data;
++ if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev"))
++ return;
++
++ check_tty_count(tty, "release_dev");
++
++ tty_fasync(-1, filp, 0);
++
++ idx = MINOR(tty->device) - tty->driver.minor_start;
++ pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
++ tty->driver.subtype == PTY_TYPE_MASTER);
++ o_tty = tty->link;
++
++#ifdef TTY_PARANOIA_CHECK
++ if (idx < 0 || idx >= tty->driver.num) {
++ printk(KERN_DEBUG "release_dev: bad idx when trying to "
++ "free (%s)\n", kdevname(tty->device));
++ return;
++ }
++ if (tty != tty->driver.table[idx]) {
++ printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
++ "for (%s)\n", idx, kdevname(tty->device));
++ return;
++ }
++ if (tty->termios != tty->driver.termios[idx]) {
++ printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
++ "for (%s)\n",
++ idx, kdevname(tty->device));
++ return;
++ }
++ if (tty->termios_locked != tty->driver.termios_locked[idx]) {
++ printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
++ "termios_locked for (%s)\n",
++ idx, kdevname(tty->device));
++ return;
++ }
++#endif
++
++#ifdef TTY_DEBUG_HANGUP
++ printk(KERN_DEBUG "release_dev of %s (tty count=%d)...",
++ tty_name(tty, buf), tty->count);
++#endif
++
++#ifdef TTY_PARANOIA_CHECK
++ if (tty->driver.other) {
++ if (o_tty != tty->driver.other->table[idx]) {
++ printk(KERN_DEBUG "release_dev: other->table[%d] "
++ "not o_tty for (%s)\n",
++ idx, kdevname(tty->device));
++ return;
++ }
++ if (o_tty->termios != tty->driver.other->termios[idx]) {
++ printk(KERN_DEBUG "release_dev: other->termios[%d] "
++ "not o_termios for (%s)\n",
++ idx, kdevname(tty->device));
++ return;
++ }
++ 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",
++ idx, kdevname(tty->device));
++ return;
++ }
++ if (o_tty->link != tty) {
++ printk(KERN_DEBUG "release_dev: bad pty pointers\n");
++ return;
++ }
++ }
++#endif
++
++ if (tty->driver.close)
++ tty->driver.close(tty, filp);
++
++ /*
++ * Sanity check: if tty->count is going to zero, there shouldn't be
++ * any waiters on tty->read_wait or tty->write_wait. We test the
++ * wait queues and kick everyone out _before_ actually starting to
++ * close. This ensures that we won't block while releasing the tty
++ * structure.
++ *
++ * The test for the o_tty closing is necessary, since the master and
++ * slave sides may close in any order. If the slave side closes out
++ * first, its count will be one, since the master side holds an open.
++ * Thus this test wouldn't be triggered at the time the slave closes,
++ * so we do it now.
++ *
++ * Note that it's possible for the tty to be opened again while we're
++ * flushing out waiters. By recalculating the closing flags before
++ * each iteration we avoid any problems.
++ */
++ while (1) {
++ tty_closing = tty->count <= 1;
++ o_tty_closing = o_tty &&
++ (o_tty->count <= (pty_master ? 1 : 0));
++ do_sleep = 0;
++
++ if (tty_closing) {
++ if (waitqueue_active(&tty->read_wait)) {
++ wake_up(&tty->read_wait);
++ do_sleep++;
++ }
++ if (waitqueue_active(&tty->write_wait)) {
++ wake_up(&tty->write_wait);
++ do_sleep++;
++ }
++ }
++ if (o_tty_closing) {
++ if (waitqueue_active(&o_tty->read_wait)) {
++ wake_up(&o_tty->read_wait);
++ do_sleep++;
++ }
++ if (waitqueue_active(&o_tty->write_wait)) {
++ wake_up(&o_tty->write_wait);
++ do_sleep++;
++ }
++ }
++ if (!do_sleep)
++ break;
++
++ 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
++ * block, so it's safe to proceed with closing.
++ */
++ if (pty_master) {
++ if (--o_tty->count < 0) {
++ printk(KERN_WARNING "release_dev: bad pty slave count "
++ "(%d) for %s\n",
++ o_tty->count, tty_name(o_tty, buf));
++ o_tty->count = 0;
++ }
++ }
++ if (--tty->count < 0) {
++ printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n",
++ tty->count, tty_name(tty, buf));
++ tty->count = 0;
++ }
++
++ /*
++ * We've decremented tty->count, so we should zero out
++ * filp->private_data, to break the link between the tty and
++ * the file descriptor. Otherwise if filp_close() blocks before
++ * the file descriptor is removed from the inuse_filp
++ * list, check_tty_count() could observe a discrepancy and
++ * printk a warning message to the user.
++ */
++ filp->private_data = 0;
++
++ /*
++ * Perform some housekeeping before deciding whether to return.
++ *
++ * Set the TTY_CLOSING flag if this was the last open. In the
++ * case of a pty we may have to wait around for the other side
++ * to close, and TTY_CLOSING makes sure we can't be reopened.
++ */
++ if(tty_closing)
++ set_bit(TTY_CLOSING, &tty->flags);
++ if(o_tty_closing)
++ set_bit(TTY_CLOSING, &o_tty->flags);
++
++ /*
++ * If _either_ side is closing, make sure there aren't any
++ * processes that still think tty or o_tty is their controlling
++ * tty.
++ */
++ if (tty_closing || o_tty_closing) {
++ struct task_struct *p;
++
++ read_lock(&tasklist_lock);
++ for_each_task(p) {
++ if (p->tty == tty || (o_tty && p->tty == o_tty))
++ p->tty = NULL;
++ }
++ read_unlock(&tasklist_lock);
++ }
++
++ /* 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
++
++ /*
++ * Prevent flush_to_ldisc() from rescheduling the work for later. Then
++ * kill any delayed work. As this is the final close it does not
++ * race with the set_ldisc code path.
++ */
++ clear_bit(TTY_LDISC, &tty->flags);
++ clear_bit(TTY_DONT_FLIP, &tty->flags);
++
++ /*
++ * Wait for ->hangup_work and ->flip.work handlers to terminate
++ */
++
++ run_task_queue(&tq_timer);
++ flush_scheduled_tasks();
++
++ /*
++ * Wait for any short term users (we know they are just driver
++ * side waiters as the file is closing so user count on the file
++ * side is zero.
++ */
++
++ spin_lock_irqsave(&tty_ldisc_lock, flags);
++ while(tty->ldisc.refcount)
++ {
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++ wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
++ spin_lock_irqsave(&tty_ldisc_lock, flags);
++ }
++ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
++
++ /*
++ * Shutdown the current line discipline, and reset it to N_TTY.
++ * N.B. why reset ldisc when we're releasing the memory??
++ * FIXME: this MUST get fixed for the new reflocking
++ */
++ if (tty->ldisc.close)
++ (tty->ldisc.close)(tty);
++ tty_ldisc_put(tty->ldisc.num);
++
++ /*
++ * Switch the line discipline back
++ */
++ tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
++ tty_set_termios_ldisc(tty,N_TTY);
++
++ if (o_tty) {
++ /* FIXME: could o_tty be in setldisc here ? */
++ clear_bit(TTY_LDISC, &o_tty->flags);
++ if (o_tty->ldisc.close)
++ (o_tty->ldisc.close)(o_tty);
++ tty_ldisc_put(o_tty->ldisc.num);
++ tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY));
++ tty_set_termios_ldisc(o_tty,N_TTY);
++ }
++
++ /*
++ * The release_mem function takes care of the details of clearing
++ * the slots and preserving the termios structure.
++ */
++ release_mem(tty, idx);
++}
++
++/*
++ * tty_open and tty_release keep up the tty count that contains the
++ * number of opens done on a tty. We cannot use the inode-count, as
++ * different inodes might point to the same tty.
++ *
++ * Open-counting is needed for pty masters, as well as for keeping
++ * track of serial lines: DTR is dropped when the last close happens.
++ * (This is not done solely through tty->count, now. - Ted 1/27/92)
++ *
++ * The termios state of a pty is reset on first open so that
++ * settings don't persist across reuse.
++ */
++static int tty_open(struct inode * inode, struct file * filp)
++{
++ struct tty_struct *tty;
++ int noctty, retval;
++ kdev_t device;
++ unsigned short saved_flags;
++ char buf[64];
++
++ saved_flags = filp->f_flags;
++retry_open:
++ noctty = filp->f_flags & O_NOCTTY;
++ device = inode->i_rdev;
++ if (device == TTY_DEV) {
++ if (!current->tty)
++ return -ENXIO;
++ device = current->tty->device;
++ filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
++ /* noctty = 1; */
++ }
++#ifdef CONFIG_VT
++ if (device == CONSOLE_DEV) {
++ extern int fg_console;
++ device = MKDEV(TTY_MAJOR, fg_console + 1);
++ noctty = 1;
++ }
++#endif
++ if (device == SYSCONS_DEV) {
++ struct console *c = console_drivers;
++ while(c && !c->device)
++ c = c->next;
++ if (!c)
++ return -ENODEV;
++ device = c->device(c);
++ filp->f_flags |= O_NONBLOCK; /* Don't let /dev/console block */
++ noctty = 1;
++ }
++
++ if (device == PTMX_DEV) {
++#ifdef CONFIG_UNIX98_PTYS
++
++ /* find a free pty. */
++ int major, minor;
++ struct tty_driver *driver;
++
++ /* find a device that is not in use. */
++ retval = -1;
++ for ( major = 0 ; major < UNIX98_NR_MAJORS ; major++ ) {
++ driver = &ptm_driver[major];
++ for (minor = driver->minor_start ;
++ minor < driver->minor_start + driver->num ;
++ minor++) {
++ device = MKDEV(driver->major, minor);
++ if (!init_dev(device, &tty)) goto ptmx_found; /* ok! */
++ }
++ }
++ return -EIO; /* no free ptys */
++ ptmx_found:
++ set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
++ minor -= driver->minor_start;
++ devpts_pty_new(driver->other->name_base + minor, MKDEV(driver->other->major, minor + driver->other->minor_start));
++ tty_register_devfs(&pts_driver[major], DEVFS_FL_DEFAULT,
++ pts_driver[major].minor_start + minor);
++ noctty = 1;
++ goto init_dev_done;
++
++#else /* CONFIG_UNIX_98_PTYS */
++
++ return -ENODEV;
++
++#endif /* CONFIG_UNIX_98_PTYS */
++ }
++
++ retval = init_dev(device, &tty);
++ if (retval)
++ return retval;
++
++#ifdef CONFIG_UNIX98_PTYS
++init_dev_done:
++#endif
++ filp->private_data = tty;
++ file_move(filp, &tty->tty_files);
++ check_tty_count(tty, "tty_open");
++ if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
++ tty->driver.subtype == PTY_TYPE_MASTER)
++ noctty = 1;
++#ifdef TTY_DEBUG_HANGUP
++ printk(KERN_DEBUG "opening %s...", tty_name(tty, buf));
++#endif
++ if (tty->driver.open)
++ retval = tty->driver.open(tty, filp);
++ else
++ retval = -ENODEV;
++ filp->f_flags = saved_flags;
++
++ if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
++ retval = -EBUSY;
++
++ if (retval) {
++#ifdef TTY_DEBUG_HANGUP
++ printk(KERN_DEBUG "error %d in opening %s...", retval,
++ tty_name(tty, buf));
++#endif
++
++ release_dev(filp);
++ if (retval != -ERESTARTSYS)
++ return retval;
++ if (signal_pending(current))
++ return retval;
++ schedule();
++ /*
++ * Need to reset f_op in case a hangup happened.
++ */
++ filp->f_op = &tty_fops;
++ goto retry_open;
++ }
++ if (!noctty &&
++ current->leader &&
++ !current->tty &&
++ tty->session == 0) {
++ task_lock(current);
++ current->tty = tty;
++ task_unlock(current);
++ current->tty_old_pgrp = 0;
++ tty->session = current->session;
++ tty->pgrp = current->pgrp;
++ }
++ if ((tty->driver.type == TTY_DRIVER_TYPE_SERIAL) &&
++ (tty->driver.subtype == SERIAL_TYPE_CALLOUT) &&
++ (tty->count == 1)) {
++ static int nr_warns;
++ if (nr_warns < 5) {
++ printk(KERN_WARNING "tty_io.c: "
++ "process %d (%s) used obsolete /dev/%s - "
++ "update software to use /dev/ttyS%d\n",
++ current->pid, current->comm,
++ tty_name(tty, buf), TTY_NUMBER(tty));
++ nr_warns++;
++ }
++ }
++ return 0;
++}
++
++static int tty_release(struct inode * inode, struct file * filp)
++{
++ lock_kernel();
++ release_dev(filp);
++ unlock_kernel();
++ return 0;
++}
++
++/* No kernel lock held - fine */
++static unsigned int tty_poll(struct file * filp, poll_table * wait)
++{
++ struct tty_struct * tty;
++ struct tty_ldisc *ld;
++ int ret = 0;
++
++ tty = (struct tty_struct *)filp->private_data;
++ if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_poll"))
++ return 0;
++
++ ld = tty_ldisc_ref_wait(tty);
++ if (ld->poll)
++ ret = (ld->poll)(tty, filp, wait);
++ tty_ldisc_deref(ld);
++ return ret;
++}
++
++static int tty_fasync(int fd, struct file * filp, int on)
++{
++ struct tty_struct * tty;
++ int retval;
++
++ 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;
++
++ if (on) {
++ if (!waitqueue_active(&tty->read_wait))
++ tty->minimum_to_wake = 1;
++ if (filp->f_owner.pid == 0) {
++ filp->f_owner.pid = (-tty->pgrp) ? : current->pid;
++ filp->f_owner.uid = current->uid;
++ filp->f_owner.euid = current->euid;
++ }
++ } else {
++ if (!tty->fasync && !waitqueue_active(&tty->read_wait))
++ tty->minimum_to_wake = N_TTY_BUF_SIZE;
++ }
++ return 0;
++}
++
++static int tiocsti(struct tty_struct *tty, char * arg)
++{
++ char ch, mbz = 0;
++ struct tty_ldisc *ld;
++
++ if ((current->tty != tty) && !suser())
++ return -EPERM;
++ if (get_user(ch, arg))
++ return -EFAULT;
++ ld = tty_ldisc_ref_wait(tty);
++ ld->receive_buf(tty, &ch, &mbz, 1);
++ tty_ldisc_deref(ld);
++ return 0;
++}
++
++static int tiocgwinsz(struct tty_struct *tty, struct winsize * arg)
++{
++ if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
++ return -EFAULT;
++ return 0;
++}
++
++static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
++ struct winsize * arg)
++{
++ struct winsize tmp_ws;
++
++ if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
++ return -EFAULT;
++ if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
++ return 0;
++ if (tty->pgrp > 0)
++ kill_pg(tty->pgrp, SIGWINCH, 1);
++ if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0))
++ kill_pg(real_tty->pgrp, SIGWINCH, 1);
++ tty->winsize = tmp_ws;
++ real_tty->winsize = tmp_ws;
++ return 0;
++}
++
++static int tioccons(struct inode *inode, struct file *file)
++{
++ if (inode->i_rdev == SYSCONS_DEV ||
++ inode->i_rdev == CONSOLE_DEV) {
++ struct file *f;
++ if (!suser())
++ return -EPERM;
++ spin_lock(&redirect_lock);
++ f = redirect;
++ redirect = NULL;
++ spin_unlock(&redirect_lock);
++ if (f)
++ fput(f);
++ return 0;
++ }
++ spin_lock(&redirect_lock);
++ if (redirect) {
++ spin_unlock(&redirect_lock);
++ return -EBUSY;
++ }
++ get_file(file);
++ redirect = file;
++ spin_unlock(&redirect_lock);
++ return 0;
++}
++
++
++static int fionbio(struct file *file, int *arg)
++{
++ int nonblock;
++
++ if (get_user(nonblock, arg))
++ return -EFAULT;
++
++ if (nonblock)
++ file->f_flags |= O_NONBLOCK;
++ else
++ file->f_flags &= ~O_NONBLOCK;
++ return 0;
++}
++
++static int tiocsctty(struct tty_struct *tty, int arg)
++{
++ if (current->leader &&
++ (current->session == tty->session))
++ return 0;
++ /*
++ * The process must be a session leader and
++ * not have a controlling tty already.
++ */
++ if (!current->leader || current->tty)
++ return -EPERM;
++ if (tty->session > 0) {
++ /*
++ * This tty is already the controlling
++ * tty for another session group!
++ */
++ if ((arg == 1) && suser()) {
++ /*
++ * Steal it away
++ */
++ struct task_struct *p;
++
++ read_lock(&tasklist_lock);
++ for_each_task(p)
++ if (p->tty == tty)
++ p->tty = NULL;
++ read_unlock(&tasklist_lock);
++ } else
++ return -EPERM;
++ }
++ task_lock(current);
++ current->tty = tty;
++ task_unlock(current);
++ current->tty_old_pgrp = 0;
++ tty->session = current->session;
++ tty->pgrp = current->pgrp;
++ return 0;
++}
++
++static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
++{
++ /*
++ * (tty == real_tty) is a cheap way of
++ * testing if the tty is NOT a master pty.
++ */
++ if (tty == real_tty && current->tty != real_tty)
++ return -ENOTTY;
++ return put_user(real_tty->pgrp, arg);
++}
++
++static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
++{
++ pid_t pgrp;
++ int retval = tty_check_change(real_tty);
++
++ if (retval == -EIO)
++ return -ENOTTY;
++ if (retval)
++ return retval;
++ if (!current->tty ||
++ (current->tty != real_tty) ||
++ (real_tty->session != current->session))
++ return -ENOTTY;
++ if (get_user(pgrp, (pid_t *) arg))
++ return -EFAULT;
++ if (pgrp < 0)
++ return -EINVAL;
++ if (session_of_pgrp(pgrp) != current->session)
++ return -EPERM;
++ real_tty->pgrp = pgrp;
++ return 0;
++}
++
++static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
++{
++ /*
++ * (tty == real_tty) is a cheap way of
++ * testing if the tty is NOT a master pty.
++ */
++ if (tty == real_tty && current->tty != real_tty)
++ return -ENOTTY;
++ if (real_tty->session <= 0)
++ return -ENOTTY;
++ return put_user(real_tty->session, arg);
++}
++
++static int tiocttygstruct(struct tty_struct *tty, struct tty_struct *arg)
++{
++ if (copy_to_user(arg, tty, sizeof(*arg)))
++ return -EFAULT;
++ return 0;
++}
++
++static int tiocsetd(struct tty_struct *tty, int *arg)
++{
++ int ldisc;
++
++ if (get_user(ldisc, arg))
++ return -EFAULT;
++ return tty_set_ldisc(tty, ldisc);
++}
++
++static int send_break(struct tty_struct *tty, int duration)
++{
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ tty->driver.break_ctl(tty, -1);
++ if (!signal_pending(current))
++ schedule_timeout(duration);
++ tty->driver.break_ctl(tty, 0);
++ if (signal_pending(current))
++ return -EINTR;
++ return 0;
++}
++
++static int tty_generic_brk(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
++{
++ if (cmd == TCSBRK && arg)
++ {
++ /* tcdrain case */
++ int retval = tty_check_change(tty);
++ if (retval)
++ return retval;
++ tty_wait_until_sent(tty, 0);
++ if (signal_pending(current))
++ return -EINTR;
++ }
++ return 0;
++}
++
++/*
++ * Split this up, as gcc can choke on it otherwise..
++ */
++int tty_ioctl(struct inode * inode, struct file * file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct tty_struct *tty, *real_tty;
++ int retval;
++ struct tty_ldisc *ld;
++
++ tty = (struct tty_struct *)file->private_data;
++ if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
++ return -EINVAL;
++
++ real_tty = tty;
++ if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
++ tty->driver.subtype == PTY_TYPE_MASTER)
++ real_tty = tty->link;
++
++ /*
++ * Break handling by driver
++ */
++ if (!tty->driver.break_ctl) {
++ switch(cmd) {
++ case TIOCSBRK:
++ case TIOCCBRK:
++ 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:
++ case TCSBRKP:
++ retval = -ENOIOCTLCMD;
++ if (tty->driver.ioctl)
++ retval = tty->driver.ioctl(tty, file, cmd, arg);
++ /* Not driver handled */
++ if (retval == -ENOIOCTLCMD)
++ retval = tty_generic_brk(tty, file, cmd, arg);
++ return retval;
++ }
++ }
++
++ /*
++ * Factor out some common prep work
++ */
++ switch (cmd) {
++ case TIOCSETD:
++ case TIOCSBRK:
++ case TIOCCBRK:
++ case TCSBRK:
++ case TCSBRKP:
++ retval = tty_check_change(tty);
++ if (retval)
++ return retval;
++ if (cmd != TIOCCBRK) {
++ tty_wait_until_sent(tty, 0);
++ if (signal_pending(current))
++ return -EINTR;
++ }
++ break;
++ }
++
++ switch (cmd) {
++ case TIOCSTI:
++ return tiocsti(tty, (char *)arg);
++ case TIOCGWINSZ:
++ return tiocgwinsz(tty, (struct winsize *) arg);
++ case TIOCSWINSZ:
++ return tiocswinsz(tty, real_tty, (struct winsize *) arg);
++ case TIOCCONS:
++ return real_tty!=tty ? -EINVAL : tioccons(inode, file);
++ case FIONBIO:
++ return fionbio(file, (int *) arg);
++ case TIOCEXCL:
++ set_bit(TTY_EXCLUSIVE, &tty->flags);
++ return 0;
++ case TIOCNXCL:
++ clear_bit(TTY_EXCLUSIVE, &tty->flags);
++ return 0;
++ case TIOCNOTTY:
++ if (current->tty != tty)
++ return -ENOTTY;
++ if (current->leader)
++ disassociate_ctty(0);
++ task_lock(current);
++ current->tty = NULL;
++ task_unlock(current);
++ return 0;
++ case TIOCSCTTY:
++ return tiocsctty(tty, arg);
++ case TIOCGPGRP:
++ return tiocgpgrp(tty, real_tty, (pid_t *) arg);
++ case TIOCSPGRP:
++ return tiocspgrp(tty, real_tty, (pid_t *) arg);
++ case TIOCGSID:
++ return tiocgsid(tty, real_tty, (pid_t *) arg);
++ case TIOCGETD:
++ /* FIXME: check this is ok */
++ return put_user(tty->ldisc.num, (int *) arg);
++ case TIOCSETD:
++ return tiocsetd(tty, (int *) arg);
++#ifdef CONFIG_VT
++ case TIOCLINUX:
++ return tioclinux(tty, arg);
++#endif
++ case TIOCTTYGSTRUCT:
++ return tiocttygstruct(tty, (struct tty_struct *) arg);
++
++ /*
++ * Break handling
++ */
++ 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;
++ case TCSBRK: /* SVID version: non-zero arg --> no break */
++ /*
++ * XXX is the above comment correct, or the
++ * code below correct? Is this ioctl used at
++ * all by anyone?
++ */
++ if (!arg)
++ return send_break(tty, HZ/4);
++ return 0;
++ case TCSBRKP: /* support for POSIX tcsendbreak() */
++ return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
++ }
++ if (tty->driver.ioctl) {
++ retval = (tty->driver.ioctl)(tty, file, cmd, arg);
++ if (retval != -ENOIOCTLCMD)
++ return retval;
++ }
++ ld = tty_ldisc_ref_wait(tty);
++ retval = -EINVAL;
++ if (ld->ioctl) {
++ retval = ld->ioctl(tty, file, cmd, arg);
++ if (retval == -ENOIOCTLCMD)
++ retval = -EINVAL;
++ }
++ tty_ldisc_deref(ld);
++ return retval;
++}
++
++
++/*
++ * This implements the "Secure Attention Key" --- the idea is to
++ * 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
++ * with the current tty must be dead before the new getty is allowed
++ * to spawn.
++ *
++ * Now, if it would be correct ;-/ The current code has a nasty hole -
++ * it doesn't catch files in flight. We may send the descriptor to ourselves
++ * via AF_UNIX socket, close it and later fetch from socket. FIXME.
++ *
++ * Nasty bug: do_SAK is being called in interrupt context. This can
++ * deadlock. We punt it up to process context. AKPM - 16Mar2001
++ */
++static void __do_SAK(void *arg)
++{
++#ifdef TTY_SOFT_SAK
++ tty_hangup(tty);
++#else
++ struct tty_struct *tty = arg;
++ struct task_struct *p;
++ int session;
++ int i;
++ struct file *filp;
++ struct tty_ldisc *disc;
++
++ if (!tty)
++ return;
++ session = tty->session;
++ /* We don't want an ldisc switch during this */
++ disc = tty_ldisc_ref(tty);
++ if (disc && disc->flush_buffer)
++ disc->flush_buffer(tty);
++ tty_ldisc_deref(disc);
++
++ if (tty->driver.flush_buffer)
++ tty->driver.flush_buffer(tty);
++
++ read_lock(&tasklist_lock);
++ for_each_task(p) {
++ if ((p->tty == tty) ||
++ ((session > 0) && (p->session == session))) {
++ send_sig(SIGKILL, p, 1);
++ continue;
++ }
++ task_lock(p);
++ if (p->files) {
++ read_lock(&p->files->file_lock);
++ for (i=0; i < p->files->max_fds; i++) {
++ filp = fcheck_files(p->files, i);
++ if (filp && (filp->f_op == &tty_fops) &&
++ (filp->private_data == tty)) {
++ send_sig(SIGKILL, p, 1);
++ break;
++ }
++ }
++ read_unlock(&p->files->file_lock);
++ }
++ task_unlock(p);
++ }
++ read_unlock(&tasklist_lock);
++#endif
++}
++
++/*
++ * The tq handling here is a little racy - tty->SAK_tq may already be queued.
++ * But there's no mechanism to fix that without futzing with tqueue_lock.
++ * Fortunately we don't need to worry, because if ->SAK_tq is already queued,
++ * the values which we write to it will be identical to the values which it
++ * already has. --akpm
++ */
++void do_SAK(struct tty_struct *tty)
++{
++ if (!tty)
++ return;
++ PREPARE_TQUEUE(&tty->SAK_tq, __do_SAK, tty);
++ schedule_task(&tty->SAK_tq);
++}
++
++/*
++ * This routine is called out of the software interrupt to flush data
++ * from the flip buffer to the line discipline.
++ */
++static void flush_to_ldisc(void *private_)
++{
++ struct tty_struct *tty = (struct tty_struct *) private_;
++ unsigned char *cp;
++ char *fp;
++ int count;
++ unsigned long flags;
++ struct tty_ldisc *disc;
++
++ disc = tty_ldisc_ref(tty);
++ if (disc == NULL) /* !TTY_LDISC */
++ return;
++
++ if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
++ queue_task(&tty->flip.tqueue, &tq_timer);
++ goto out;
++ }
++ if (tty->flip.buf_num) {
++ cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
++ fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
++ tty->flip.buf_num = 0;
++
++ save_flags(flags); cli();
++ tty->flip.char_buf_ptr = tty->flip.char_buf;
++ tty->flip.flag_buf_ptr = tty->flip.flag_buf;
++ } else {
++ cp = tty->flip.char_buf;
++ fp = tty->flip.flag_buf;
++ tty->flip.buf_num = 1;
++
++ save_flags(flags); cli();
++ tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
++ tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
++ }
++ count = tty->flip.count;
++ tty->flip.count = 0;
++ restore_flags(flags);
++
++ disc->receive_buf(tty, cp, fp, count);
++out:
++ tty_ldisc_deref(disc);
++}
++
++/*
++ * Call the ldisc flush directly from a driver. This function may
++ * return an error and need retrying by the user.
++ */
++
++int tty_push_data(struct tty_struct *tty, unsigned char *cp, unsigned char *fp, int count)
++{
++ int ret = 0;
++ struct tty_ldisc *disc;
++
++ disc = tty_ldisc_ref(tty);
++ if(test_bit(TTY_DONT_FLIP, &tty->flags))
++ ret = -EAGAIN;
++ else if(disc == NULL)
++ ret = -EIO;
++ else
++ disc->receive_buf(tty, cp, fp, count);
++ tty_ldisc_deref(disc);
++ return ret;
++
++}
++
++/*
++ * Routine which returns the baud rate of the tty
++ *
++ * Note that the baud_table needs to be kept in sync with the
++ * include/asm/termbits.h file.
++ */
++static int baud_table[] = {
++ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
++ 9600, 19200, 38400, 57600, 115200, 230400, 460800,
++#ifdef __sparc__
++ 76800, 153600, 307200, 614400, 921600
++#else
++ 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
++ 2500000, 3000000, 3500000, 4000000
++#endif
++};
++
++static int n_baud_table = sizeof(baud_table)/sizeof(int);
++
++int tty_get_baud_rate(struct tty_struct *tty)
++{
++ unsigned int cflag, i;
++
++ cflag = tty->termios->c_cflag;
++
++ i = cflag & CBAUD;
++ if (i & CBAUDEX) {
++ i &= ~CBAUDEX;
++ if (i < 1 || i+15 >= n_baud_table)
++ tty->termios->c_cflag &= ~CBAUDEX;
++ else
++ i += 15;
++ }
++ if (i==15 && tty->alt_speed) {
++ if (!tty->warned) {
++ printk(KERN_WARNING "Use of setserial/setrocket to "
++ "set SPD_* flags is deprecated\n");
++ tty->warned = 1;
++ }
++ return(tty->alt_speed);
++ }
++
++ return baud_table[i];
++}
++
++void tty_flip_buffer_push(struct tty_struct *tty)
++{
++ if (tty->low_latency)
++ flush_to_ldisc((void *) tty);
++ else
++ queue_task(&tty->flip.tqueue, &tq_timer);
++}
++
++/*
++ * This subroutine initializes a tty structure.
++ */
++static void initialize_tty_struct(struct tty_struct *tty)
++{
++ memset(tty, 0, sizeof(struct tty_struct));
++ tty->magic = TTY_MAGIC;
++ tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
++ tty->pgrp = -1;
++ tty->flip.char_buf_ptr = tty->flip.char_buf;
++ tty->flip.flag_buf_ptr = tty->flip.flag_buf;
++ tty->flip.tqueue.routine = flush_to_ldisc;
++ tty->flip.tqueue.data = tty;
++ init_MUTEX(&tty->flip.pty_sem);
++ init_MUTEX(&tty->termios_sem);
++ init_waitqueue_head(&tty->write_wait);
++ init_waitqueue_head(&tty->read_wait);
++ tty->tq_hangup.routine = do_tty_hangup;
++ tty->tq_hangup.data = tty;
++ sema_init(&tty->atomic_read, 1);
++ sema_init(&tty->atomic_write, 1);
++ spin_lock_init(&tty->read_lock);
++ INIT_LIST_HEAD(&tty->tty_files);
++ INIT_TQUEUE(&tty->SAK_tq, 0, 0);
++}
++
++/*
++ * The default put_char routine if the driver did not define one.
++ */
++void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
++{
++ tty->driver.write(tty, 0, &ch, 1);
++}
++
++/*
++ * Register a tty device described by <driver>, with minor number <minor>.
++ */
++void tty_register_devfs (struct tty_driver *driver, unsigned int flags, unsigned minor)
++{
++#ifdef CONFIG_DEVFS_FS
++ umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
++ kdev_t device = MKDEV (driver->major, minor);
++ int idx = minor - driver->minor_start;
++ char buf[32];
++
++ switch (device) {
++ case TTY_DEV:
++ case PTMX_DEV:
++ mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
++ break;
++ default:
++ if (driver->major == PTY_MASTER_MAJOR)
++ mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
++ break;
++ }
++ 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);
++ return;
++ }
++# ifdef CONFIG_UNIX98_PTYS
++ if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) &&
++ (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) )
++ flags |= DEVFS_FL_CURRENT_OWNER;
++# endif
++ sprintf(buf, driver->name, idx + driver->name_base);
++ devfs_register (NULL, buf, flags | DEVFS_FL_DEFAULT,
++ driver->major, minor, mode, &tty_fops, NULL);
++#endif /* CONFIG_DEVFS_FS */
++}
++
++void tty_unregister_devfs (struct tty_driver *driver, unsigned minor)
++{
++#ifdef CONFIG_DEVFS_FS
++ void * handle;
++ int idx = minor - driver->minor_start;
++ char buf[32];
++
++ sprintf(buf, driver->name, idx + driver->name_base);
++ handle = devfs_find_handle (NULL, buf, driver->major, minor,
++ DEVFS_SPECIAL_CHR, 0);
++ devfs_unregister (handle);
++#endif /* CONFIG_DEVFS_FS */
++}
++
++EXPORT_SYMBOL(tty_register_devfs);
++EXPORT_SYMBOL(tty_unregister_devfs);
++
++/*
++ * Called by a tty driver to register itself.
++ */
++int tty_register_driver(struct tty_driver *driver)
++{
++ int error;
++ int i;
++
++ if (driver->flags & TTY_DRIVER_INSTALLED)
++ return 0;
++
++ error = devfs_register_chrdev(driver->major, driver->name, &tty_fops);
++ if (error < 0)
++ return error;
++ else if(driver->major == 0)
++ driver->major = error;
++
++ 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);
++ }
++ proc_tty_register_driver(driver);
++ return error;
++}
++
++/*
++ * Called by a tty driver to unregister itself.
++ */
++int tty_unregister_driver(struct tty_driver *driver)
++{
++ int retval;
++ struct tty_driver *p;
++ int i, found = 0;
++ struct termios *tp;
++ const char *othername = NULL;
++
++ if (*driver->refcount)
++ return -EBUSY;
++
++ for (p = tty_drivers; p; p = p->next) {
++ if (p == driver)
++ found++;
++ else if (p->major == driver->major)
++ othername = p->name;
++ }
++
++ if (!found)
++ return -ENOENT;
++
++ if (othername == NULL) {
++ retval = devfs_unregister_chrdev(driver->major, driver->name);
++ if (retval)
++ return retval;
++ } else
++ devfs_register_chrdev(driver->major, othername, &tty_fops);
++
++ if (driver->prev)
++ driver->prev->next = driver->next;
++ else
++ tty_drivers = driver->next;
++
++ if (driver->next)
++ driver->next->prev = driver->prev;
++
++ /*
++ * Free the termios and termios_locked structures because
++ * we don't want to get memory leaks when modular tty
++ * drivers are removed from the kernel.
++ */
++ for (i = 0; i < driver->num; i++) {
++ tp = driver->termios[i];
++ if (tp) {
++ driver->termios[i] = NULL;
++ kfree(tp);
++ }
++ tp = driver->termios_locked[i];
++ if (tp) {
++ driver->termios_locked[i] = NULL;
++ kfree(tp);
++ }
++ tty_unregister_devfs(driver, driver->minor_start + i);
++ }
++ proc_tty_unregister_driver(driver);
++ return 0;
++}
++
++
++/*
++ * Initialize the console device. This is called *early*, so
++ * we can't necessarily depend on lots of kernel help here.
++ * Just do some early initializations, and do the complex setup
++ * later.
++ */
++void __init console_init(void)
++{
++ /* Setup the default TTY line discipline. */
++ memset(tty_ldiscs, 0, NR_LDISCS*sizeof(struct tty_ldisc));
++ (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
++
++ /*
++ * 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));
++ memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
++ tty_std_termios.c_iflag = ICRNL | IXON;
++ tty_std_termios.c_oflag = OPOST | ONLCR;
++ tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL;
++ tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
++ ECHOCTL | ECHOKE | IEXTEN;
++
++ /*
++ * set up the console device so that later boot sequences can
++ * inform about problems etc..
++ */
++#ifdef CONFIG_EARLY_PRINTK
++ disable_early_printk();
++#endif
++#ifdef CONFIG_HVC_CONSOLE
++ hvc_console_init();
++#endif
++#ifdef CONFIG_VT
++ con_init();
++#endif
++#ifdef CONFIG_AU1X00_SERIAL_CONSOLE
++ au1x00_serial_console_init();
++#endif
++#ifdef CONFIG_SERIAL_CONSOLE
++#if (defined(CONFIG_8xx) || defined(CONFIG_CPM2))
++ console_8xx_init();
++#elif defined(CONFIG_MAC_SERIAL) && defined(CONFIG_SERIAL)
++ if (_machine == _MACH_Pmac)
++ mac_scc_console_init();
++ else
++ serial_console_init();
++#elif defined(CONFIG_MAC_SERIAL)
++ mac_scc_console_init();
++#elif defined(CONFIG_PARISC)
++ pdc_console_init();
++#elif defined(CONFIG_SERIAL)
++ serial_console_init();
++#endif /* CONFIG_8xx */
++#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC)
++ vme_scc_console_init();
++#endif
++#if defined(CONFIG_SERIAL167)
++ serial167_console_init();
++#endif
++#if defined(CONFIG_SH_SCI)
++ sci_console_init();
++#endif
++#endif
++#ifdef CONFIG_SERIAL_DEC_CONSOLE
++ dec_serial_console_init();
++#endif
++#ifdef CONFIG_TN3270_CONSOLE
++ tub3270_con_init();
++#endif
++#ifdef CONFIG_TN3215
++ con3215_init();
++#endif
++#ifdef CONFIG_HWC
++ hwc_console_init();
++#endif
++#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();
++#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
++#ifdef CONFIG_TXX927_SERIAL_CONSOLE
++ txx927_console_init();
++#endif
++#ifdef CONFIG_SERIAL_TXX9_CONSOLE
++ txx9_serial_console_init();
++#endif
++#ifdef CONFIG_SIBYTE_SB1250_DUART_CONSOLE
++ sb1250_serial_console_init();
++#endif
++#ifdef CONFIG_IP22_SERIAL
++ sgi_serial_console_init();
++#endif
++}
++
++static struct tty_driver dev_tty_driver, dev_syscons_driver;
++#ifdef CONFIG_UNIX98_PTYS
++static struct tty_driver dev_ptmx_driver;
++#endif
++#ifdef CONFIG_VT
++static struct tty_driver dev_console_driver;
++#endif
++
++/*
++ * Ok, now we can initialize the rest of the tty devices and can count
++ * on memory allocations, interrupts etc..
++ */
++void __init tty_init(void)
++{
++ /*
++ * dev_tty_driver and dev_console_driver are actually magic
++ * devices which get redirected at open time. Nevertheless,
++ * we register them so that register_chrdev is called
++ * appropriately.
++ */
++ memset(&dev_tty_driver, 0, sizeof(struct tty_driver));
++ dev_tty_driver.magic = TTY_DRIVER_MAGIC;
++ dev_tty_driver.driver_name = "/dev/tty";
++ dev_tty_driver.name = dev_tty_driver.driver_name + 5;
++ dev_tty_driver.name_base = 0;
++ dev_tty_driver.major = TTYAUX_MAJOR;
++ dev_tty_driver.minor_start = 0;
++ 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");
++
++ dev_syscons_driver = dev_tty_driver;
++ dev_syscons_driver.driver_name = "/dev/console";
++ dev_syscons_driver.name = dev_syscons_driver.driver_name + 5;
++ dev_syscons_driver.major = TTYAUX_MAJOR;
++ dev_syscons_driver.minor_start = 1;
++ dev_syscons_driver.type = TTY_DRIVER_TYPE_SYSTEM;
++ dev_syscons_driver.subtype = SYSTEM_TYPE_SYSCONS;
++
++ if (tty_register_driver(&dev_syscons_driver))
++ 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.
++ */
++#ifdef CONFIG_VT
++ con_init_devfs();
++#endif
++
++#ifdef CONFIG_UNIX98_PTYS
++ dev_ptmx_driver = dev_tty_driver;
++ dev_ptmx_driver.driver_name = "/dev/ptmx";
++ dev_ptmx_driver.name = dev_ptmx_driver.driver_name + 5;
++ dev_ptmx_driver.major= MAJOR(PTMX_DEV);
++ dev_ptmx_driver.minor_start = MINOR(PTMX_DEV);
++ dev_ptmx_driver.type = TTY_DRIVER_TYPE_SYSTEM;
++ dev_ptmx_driver.subtype = SYSTEM_TYPE_SYSPTMX;
++
++ 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";
++ dev_console_driver.name = dev_console_driver.driver_name + 5;
++ dev_console_driver.major = TTY_MAJOR;
++ dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM;
++ dev_console_driver.subtype = SYSTEM_TYPE_CONSOLE;
++
++ if (tty_register_driver(&dev_console_driver))
++ panic("Couldn't register /dev/tty0 driver\n");
++
++ kbd_init();
++#endif
++
++#ifdef CONFIG_SGI_L1_SERIAL_CONSOLE
++ if (ia64_platform_is("sn2")) {
++ sn_sal_serial_console_init();
++ return; /* only one console right now for SN2 */
++ }
++#endif
++#ifdef CONFIG_ESPSERIAL /* init ESP before rs, so rs doesn't see the port */
++ espserial_init();
++#endif
++#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC)
++ vme_scc_init();
++#endif
++#ifdef CONFIG_SERIAL_TX3912
++ tx3912_rs_init();
++#endif
++#ifdef CONFIG_ROCKETPORT
++ rp_init();
++#endif
++#ifdef CONFIG_SERIAL167
++ serial167_init();
++#endif
++#ifdef CONFIG_CYCLADES
++ cy_init();
++#endif
++#ifdef CONFIG_STALLION
++ stl_init();
++#endif
++#ifdef CONFIG_ISTALLION
++ stli_init();
++#endif
++#ifdef CONFIG_DIGI
++ pcxe_init();
++#endif
++#ifdef CONFIG_DIGIEPCA
++ pc_init();
++#endif
++#ifdef CONFIG_SPECIALIX
++ specialix_init();
++#endif
++#if (defined(CONFIG_8xx) || defined(CONFIG_CPM2))
++ rs_8xx_init();
++#endif /* CONFIG_8xx */
++ pty_init();
++#ifdef CONFIG_MOXA_SMARTIO
++ mxser_init();
++#endif
++#ifdef CONFIG_MOXA_INTELLIO
++ moxa_init();
++#endif
++#ifdef CONFIG_VT
++ vcs_init();
++#endif
++#ifdef CONFIG_TN3270
++ tub3270_init();
++#endif
++#ifdef CONFIG_TN3215
++ tty3215_init();
++#endif
++#ifdef CONFIG_HWC
++ hwc_tty_init();
++#endif
++#ifdef CONFIG_A2232
++ a2232board_init();
++#endif
++}
+diff -urN kernel-source-2.4.27-8/drivers/char/wdt285.c kernel-source-2.4.27-8-arm-1/drivers/char/wdt285.c
+--- kernel-source-2.4.27-8/drivers/char/wdt285.c 2003-06-13 15:51:33.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/char/wdt285.c 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/char/wdt977.c kernel-source-2.4.27-8-arm-1/drivers/char/wdt977.c
+--- kernel-source-2.4.27-8/drivers/char/wdt977.c 2002-11-28 23:53:12.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/char/wdt977.c 2005-02-18 17:48:42.000000000 +0000
+@@ -27,6 +27,7 @@
+ #include <asm/io.h>
+ #include <asm/system.h>
+ #include <asm/mach-types.h>
++#include <asm/uaccess.h>
+
+ #define WATCHDOG_MINOR 130
+
+diff -urN kernel-source-2.4.27-8/drivers/cpufreq/Kconfig kernel-source-2.4.27-8-arm-1/drivers/cpufreq/Kconfig
+--- kernel-source-2.4.27-8/drivers/cpufreq/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/cpufreq/Kconfig 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/cpufreq/Makefile kernel-source-2.4.27-8-arm-1/drivers/cpufreq/Makefile
+--- kernel-source-2.4.27-8/drivers/cpufreq/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/cpufreq/Makefile 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/cpufreq/cpufreq.c kernel-source-2.4.27-8-arm-1/drivers/cpufreq/cpufreq.c
+--- kernel-source-2.4.27-8/drivers/cpufreq/cpufreq.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/cpufreq/cpufreq.c 2005-02-18 17:48:42.000000000 +0000
+@@ -0,0 +1,720 @@
++/*
++ * linux/kernel/cpufreq.c
++ *
++ * Copyright (C) 2001 Russell King
++ * (C) 2002 - 2003 Dominik Brodowski <linux at brodo.de>
++ *
++ * 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 <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/notifier.h>
++#include <linux/cpufreq.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++
++#include <asm/semaphore.h>
++/**
++ * 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; i<NR_CPUS; i++) {
++ struct cpufreq_policy *policy = cpufreq_cpu_get(i);
++ if (!policy)
++ goto done;
++ down(&policy->lock);
++
++ 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<NR_CPUS; i++) {
++ if (cpu_online(i))
++ cpufreq_add_dev(i);
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cpufreq_register_driver);
++
++
++/**
++ * cpufreq_unregister_driver - unregister the current CPUFreq driver
++ *
++ * Unregister the current CPUFreq driver. Only call this if you have
++ * the right to do so, i.e. if you have succeeded in initialising before!
++ * Returns zero if successful, and -EINVAL if the cpufreq_driver is
++ * currently not initialised.
++ */
++int cpufreq_unregister_driver(struct cpufreq_driver *driver)
++{
++ unsigned long flags;
++ unsigned int i;
++
++ if (!cpufreq_driver || (driver != cpufreq_driver))
++ return -EINVAL;
++
++ for (i=0; i<NR_CPUS; i++) {
++ if (cpu_online(i))
++ cpufreq_remove_dev(i);
++ }
++
++ spin_lock_irqsave(&cpufreq_driver_lock, flags);
++ cpufreq_driver = NULL;
++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cpufreq_unregister_driver);
+diff -urN kernel-source-2.4.27-8/drivers/cpufreq/freq_table.c kernel-source-2.4.27-8-arm-1/drivers/cpufreq/freq_table.c
+--- kernel-source-2.4.27-8/drivers/cpufreq/freq_table.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/cpufreq/freq_table.c 2005-02-18 17:48:42.000000000 +0000
+@@ -0,0 +1,203 @@
++/*
++ * linux/drivers/cpufreq/freq_table.c
++ *
++ * Copyright (C) 2002 - 2003 Dominik Brodowski
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/cpufreq.h>
++
++/*********************************************************************
++ * 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 <linux at brodo.de>");
++MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
++MODULE_LICENSE ("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/cpufreq/proc_intf.c kernel-source-2.4.27-8-arm-1/drivers/cpufreq/proc_intf.c
+--- kernel-source-2.4.27-8/drivers/cpufreq/proc_intf.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/cpufreq/proc_intf.c 2005-02-18 17:48:42.000000000 +0000
+@@ -0,0 +1,244 @@
++/*
++ * linux/drivers/cpufreq/proc_intf.c
++ *
++ * Copyright (C) 2002 - 2003 Dominik Brodowski
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/cpufreq.h>
++#include <linux/ctype.h>
++#include <linux/proc_fs.h>
++#include <asm/uaccess.h>
++
++
++/**
++ * 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;i<NR_CPUS;i++) {
++ if (!cpu_online(i))
++ continue;
++
++ if (cpufreq_get_policy(&policy, i))
++ continue;
++
++ if (!policy.cpuinfo.max_freq)
++ continue;
++
++ min_pctg = (policy.min * 100) / policy.cpuinfo.max_freq;
++ max_pctg = (policy.max * 100) / policy.cpuinfo.max_freq;
++
++ p += sprintf(p, "CPU%3d %9d kHz (%3d %%) - %9d kHz (%3d %%) - ",
++ i , policy.min, min_pctg, policy.max, max_pctg);
++ switch (policy.policy) {
++ case CPUFREQ_POLICY_POWERSAVE:
++ p += sprintf(p, "powersave\n");
++ break;
++ case CPUFREQ_POLICY_PERFORMANCE:
++ p += sprintf(p, "performance\n");
++ break;
++ case CPUFREQ_POLICY_GOVERNOR:
++ p += snprintf(p, CPUFREQ_NAME_LEN, "%s\n", policy.governor->name);
++ 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; i<NR_CPUS; i++)
++ {
++ policy.cpu = i;
++ if (cpu_online(i))
++ cpufreq_set_policy(&policy);
++ }
++ }
++ else
++ cpufreq_set_policy(&policy);
++
++ return count;
++}
++
++
++/**
++ * cpufreq_proc_init - add "cpufreq" to the /proc root directory
++ *
++ * This function adds "cpufreq" to the /proc root directory.
++ */
++static int __init cpufreq_proc_init (void)
++{
++ struct proc_dir_entry *entry = NULL;
++
++ /* are these acceptable values? */
++ entry = create_proc_entry("cpufreq", S_IFREG|S_IRUGO|S_IWUSR,
++ &proc_root);
++
++ if (!entry) {
++ printk(KERN_ERR "unable to create /proc/cpufreq entry\n");
++ return -EIO;
++ } else {
++ entry->read_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 <linux at brodo.de>");
++MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface");
++MODULE_LICENSE ("GPL");
++
++module_init(cpufreq_proc_init);
++module_exit(cpufreq_proc_exit);
+diff -urN kernel-source-2.4.27-8/drivers/cpufreq/userspace.c kernel-source-2.4.27-8-arm-1/drivers/cpufreq/userspace.c
+--- kernel-source-2.4.27-8/drivers/cpufreq/userspace.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/cpufreq/userspace.c 2005-02-18 17:48:42.000000000 +0000
+@@ -0,0 +1,591 @@
++/*
++ * drivers/cpufreq/userspace.c
++ *
++ * Copyright (C) 2001 Russell King
++ * (C) 2002 - 2003 Dominik Brodowski <linux at brodo.de>
++ *
++ * $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 <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/smp.h>
++#include <linux/init.h>
++#include <linux/spinlock.h>
++#include <linux/interrupt.h>
++#include <linux/ctype.h>
++#include <linux/cpufreq.h>
++#include <linux/sysctl.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/sysfs.h>
++
++#include <asm/uaccess.h>
++
++#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 <linux at brodo.de>, Russell King <rmk at arm.linux.org.uk>");
++MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
++MODULE_LICENSE ("GPL");
++
++module_init(cpufreq_gov_userspace_init);
++module_exit(cpufreq_gov_userspace_exit);
+diff -urN kernel-source-2.4.27-8/drivers/i2c/Config.in kernel-source-2.4.27-8-arm-1/drivers/i2c/Config.in
+--- kernel-source-2.4.27-8/drivers/i2c/Config.in 2004-04-14 14:05:29.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/Config.in 2005-02-18 17:48:42.000000000 +0000
+@@ -17,6 +17,18 @@
+ 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_BAST" = "y" ]; then
++ dep_tristate ' Samsung S3C2410 bit based I2C adapter' CONFIG_I2C_S3C2410BIT $CONFIG_I2C
++ fi
++ 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 +61,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 kernel-source-2.4.27-8/drivers/i2c/Makefile kernel-source-2.4.27-8-arm-1/drivers/i2c/Makefile
+--- kernel-source-2.4.27-8/drivers/i2c/Makefile 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/Makefile 2005-02-18 17:48:42.000000000 +0000
+@@ -8,12 +8,22 @@
+ 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
++obj-$(CONFIG_I2C_S3C2410BIT) += i2c-bit-s3c2410.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
+diff -urN kernel-source-2.4.27-8/drivers/i2c/i2c-algo-bit.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-algo-bit.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-algo-bit.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-algo-bit.c 2005-02-18 17:48:42.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 at 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 <simon at tk.uni-linz.ac.at>");
+ 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 kernel-source-2.4.27-8/drivers/i2c/i2c-algo-pcf.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-algo-pcf.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-algo-pcf.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-algo-pcf.c 2005-02-18 17:48:42.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 <hb at spacetec.no>");
+ 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 kernel-source-2.4.27-8/drivers/i2c/i2c-bit-s3c2410.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-bit-s3c2410.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-bit-s3c2410.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-bit-s3c2410.c 2005-02-18 17:48:42.000000000 +0000
+@@ -0,0 +1,252 @@
++/* linux/drivers/i2c/i2c-bit-s3c2410.o
++ *
++ * I2C bit-wise driver for S3C2410
++ *
++ *
++ * We've done a bit-wise driver here as it is simpler than trying to sort
++ * out the driver for the proper on-board i2c controller at the moment.
++*/
++
++/* 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. */
++/* ------------------------------------------------------------------------- */
++
++/* With some changes from Kyösti Mälkki <kmalkki at cc.hut.fi> and even
++ Frodo Looijaard <frodol at dds.nl> */
++
++/* S3C2410 Version by Ben Dooks <ben at simtec.co.uk>
++ *
++ * (c) 2003 Simtec Electronics
++*/
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/version.h>
++#include <linux/init.h>
++
++#include <asm/uaccess.h>
++
++#include <linux/ioport.h>
++#include <asm/io.h>
++#include <linux/errno.h>
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch-s3c2410/S3C2410-gpio.h>
++
++/* ----- 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 */
++#define DEBINIT(x) x /* detection status messages */
++
++#if 0
++#define dbg2(x...) printk(x)
++#else
++#define dbg2(x...) do { } while(0)
++#endif
++
++#define dbgi(x...) printk(x)
++
++/* ----- local functions ---------------------------------------------- */
++
++
++/* SDA is on GPIO port E, Pin 15
++ * SCL is on GPIO port E, Pin 14
++*/
++
++static void bit_s3c2410_setscl(void *data, int state)
++{
++ unsigned long dat;
++
++ dbg2(__FUNCTION__ ": data=%p, state=%d\n", data, state);
++
++ dat = __raw_readl(S3C2410_GPEDAT);
++
++ dat &= ~(1<<14);
++
++ if (state) {
++ dat |= 1<<14;
++ } else {
++ dat &= ~(1<<14);
++ }
++
++ __raw_writel(dat, S3C2410_GPEDAT);
++}
++
++static void bit_s3c2410_setsda(void *data, int state)
++{
++ unsigned long con, dat;
++
++ dbg2(__FUNCTION__ ": data=%p, state=%d\n", data, state);
++
++ con = __raw_readl(S3C2410_GPECON);
++ dat = __raw_readl(S3C2410_GPEDAT);
++
++ con &= ~S3C2410_GPE15_MASK;
++ dat &= ~(1<<15);
++
++ /* even though we do have open-collector outputs, we change the pin to
++ * an input to create the high-level on the bus... */
++ if (state) {
++ con |= S3C2410_GPE15_INP;
++ dat &= ~(1<<15);
++ } else {
++ con |= S3C2410_GPE15_OUTP;
++ dat &= ~(1<<15);
++ }
++
++ __raw_writel(con, S3C2410_GPECON);
++ __raw_writel(dat, S3C2410_GPEDAT);
++}
++
++static int bit_s3c2410_getscl(void *data)
++{
++ unsigned long dat;
++
++ dat = __raw_readl(S3C2410_GPEDAT);
++
++ return (dat & (1<<14)) ? 1 : 0;
++}
++
++static int bit_s3c2410_getsda(void *data)
++{
++ unsigned long dat, con;
++ unsigned int ret;
++
++ con = __raw_readl(S3C2410_GPECON);
++ con &= ~S3C2410_GPE15_MASK;
++ con |= S3C2410_GPE15_INP;
++ __raw_writel(con, S3C2410_GPECON);
++
++ dat = __raw_readl(S3C2410_GPEDAT);
++ ret = (dat & (1<<15)) ? 1 : 0;
++ return ret;
++}
++
++static int bit_s3c2410_init(void)
++{
++ unsigned long reg;
++
++ dbgi(__FUNCTION__ ": GPECON=%08x, GPEDATA=%08x\n",
++ __raw_readl(S3C2410_GPECON),
++ __raw_readl(S3C2410_GPEDAT));
++
++ /* ensure we'll pull clock high when we init the lines */
++ reg = __raw_readl(S3C2410_GPEDAT);
++ reg |= 1<<14;
++ __raw_writel(reg, S3C2410_GPEDAT);
++
++ reg = __raw_readl(S3C2410_GPECON);
++ reg &= ~(S3C2410_GPE15_MASK | S3C2410_GPE14_MASK);
++ reg |= S3C2410_GPE15_INP | S3C2410_GPE14_OUTP;
++ __raw_writel(reg, S3C2410_GPECON);
++
++ /* ensure the line is actually high (do we need to do this?) */
++ reg = __raw_readl(S3C2410_GPEDAT);
++ reg |= 1<<14;
++ __raw_writel(reg, S3C2410_GPEDAT);
++
++ dbgi(__FUNCTION__ ": GPECON=%08x, GPEDAT=%08x\n",
++ (unsigned int)reg, __raw_readl(S3C2410_GPEDAT));
++
++ return 0;
++}
++
++static void __exit bit_s3c2410_exit(void)
++{
++ /* todo - sort out putting pins into input mode */
++}
++
++static int bit_s3c2410_reg(struct i2c_client *client)
++{
++ return 0;
++}
++
++static int bit_s3c2410_unreg(struct i2c_client *client)
++{
++ return 0;
++}
++
++static void bit_s3c2410_inc_use(struct i2c_adapter *adap)
++{
++ MOD_INC_USE_COUNT;
++}
++
++static void bit_s3c2410_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_s3c2410_data = {
++ NULL,
++ bit_s3c2410_setsda,
++ bit_s3c2410_setscl,
++ bit_s3c2410_getsda,
++ bit_s3c2410_getscl,
++ 80, 80, 100, /* waits, timeout */
++};
++
++static struct i2c_adapter bit_s3c2410_ops = {
++ "Samsung S3C2410 bitwise I2C",
++ I2C_HW_B_S3C2410,
++ NULL,
++ &bit_s3c2410_data,
++ bit_s3c2410_inc_use,
++ bit_s3c2410_dec_use,
++ bit_s3c2410_reg,
++ bit_s3c2410_unreg,
++};
++
++static int __init i2c_bits3c2410_init(void)
++{
++ if (bit_s3c2410_init()==0) {
++ if(i2c_bit_add_bus(&bit_s3c2410_ops) < 0)
++ return -ENODEV;
++ } else {
++ return -ENODEV;
++ }
++
++ printk("i2c-bit-s3c2410.o: initialised ok\n");
++ return 0;
++}
++
++
++static void __exit i2c_bits3c2410_exit(void)
++{
++ i2c_bit_del_bus(&bit_s3c2410_ops);
++ bit_s3c2410_exit();
++}
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_AUTHOR("Simon G. Vogl, Ben Dooks <ben at simtec.co.uk>");
++MODULE_DESCRIPTION("I2C bitwise bus driver for Samsung S3C2410");
++MODULE_LICENSE("GPL");
++
++module_init(i2c_bits3c2410_init);
++module_exit(i2c_bits3c2410_exit);
++
++
++
++
+diff -urN kernel-source-2.4.27-8/drivers/i2c/i2c-core.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-core.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-core.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-core.c 2005-02-18 17:48:42.000000000 +0000
+@@ -67,7 +67,7 @@
+ static int driver_count;
+
+ /**** debug level */
+-static int i2c_debug=1;
++static int i2c_debug = 0;
+
+ /* ---------------------------------------------------
+ * /proc entry declarations
+@@ -77,7 +77,7 @@
+ #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,
+ loff_t *ppos);
+@@ -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;
+ }
+
+
+@@ -1239,116 +1238,6 @@
+ 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 */
+-
+- 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 <simon at tk.uni-linz.ac.at>");
+ 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 kernel-source-2.4.27-8/drivers/i2c/i2c-dev.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-dev.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-dev.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-dev.c 2005-02-18 17:48:42.000000000 +0000
+@@ -50,11 +50,6 @@
+ #include <linux/i2c.h>
+ #include <linux/i2c-dev.h>
+
+-#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,
+@@ -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,
+@@ -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,7 +507,7 @@
+ if ((res = i2c_del_driver(&i2cdev_driver))) {
+ printk("i2c-dev.o: Driver deregistration failed, "
+ "module not removed.\n");
+- return res;
++ return;
+ }
+ i2cdev_initialized --;
+ }
+@@ -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 <frodol at dds.nl> and Simon G. Vogl <simon at tk.uni-linz.ac.at>");
+ 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 kernel-source-2.4.27-8/drivers/i2c/i2c-elektor.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-elektor.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-elektor.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-elektor.c 2005-02-18 17:48:42.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 <hb at spacetec.no>");
+ 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 kernel-source-2.4.27-8/drivers/i2c/i2c-elv.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-elv.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-elv.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-elv.c 2005-02-18 17:48:42.000000000 +0000
+@@ -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
+ }
+
+ /* ------------------------------------------------------------------------
+@@ -167,7 +163,7 @@
+ 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 <simon at tk.uni-linz.ac.at>");
+ 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 kernel-source-2.4.27-8/drivers/i2c/i2c-frodo.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-frodo.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-frodo.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-frodo.c 2005-02-18 17:48:42.000000000 +0000
+@@ -0,0 +1,114 @@
++
++/*
++ * linux/drivers/i2c/i2c-frodo.c
++ *
++ * Author: Abraham van der Merwe <abraham at 2d3d.co.za>
++ *
++ * 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 <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <asm/hardware.h>
++
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++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 <abraham at 2d3d.co.za>");
++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 kernel-source-2.4.27-8/drivers/i2c/i2c-guide.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-guide.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-guide.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-guide.c 2005-02-18 17:48:42.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 at 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 <linux/kernel.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/string.h> /* for 2.0 kernels to get NULL */
++#include <asm/errno.h> /* for 2.0 kernels to get ENODEV */
++#include <asm/io.h>
++
++#include <asm/hardware/cs89712.h> // io operation ep_writel()
++#include <asm/hardware/clps7111.h> // io operation clps_writel()
++#include <asm/arch-clps711x/hardware.h> // io operation clps_writel()
++
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++/* ----- 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 <tsong at iders.ca>");
++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 kernel-source-2.4.27-8/drivers/i2c/i2c-omaha.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-omaha.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-omaha.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-omaha.c 2005-02-18 17:48:42.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 <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/version.h>
++#include <linux/init.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/ioport.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++#include <asm/io.h>
++#include <asm/hardware.h>
++
++/* ----- 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 <support at arm.com>");
++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 kernel-source-2.4.27-8/drivers/i2c/i2c-philips-par.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-philips-par.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-philips-par.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-philips-par.c 2005-02-18 17:48:42.000000000 +0000
+@@ -257,7 +257,7 @@
+ 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);
+
+@@ -266,7 +266,7 @@
+ 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 kernel-source-2.4.27-8/drivers/i2c/i2c-velleman.c kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-velleman.c
+--- kernel-source-2.4.27-8/drivers/i2c/i2c-velleman.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/i2c/i2c-velleman.c 2005-02-18 17:48:42.000000000 +0000
+@@ -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 <simon at tk.uni-linz.ac.at>");
+ 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 kernel-source-2.4.27-8/drivers/ide/Config.in kernel-source-2.4.27-8-arm-1/drivers/ide/Config.in
+--- kernel-source-2.4.27-8/drivers/ide/Config.in 2005-01-19 09:57:48.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/Config.in 2005-02-18 17:48:42.000000000 +0000
+@@ -107,6 +107,12 @@
+ 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_ARCH_BAST" = "y" ]; then
++ dep_bool ' BAST Onboard IDE' CONFIG_BLK_DEV_IDE_BAST $CONFIG_ARCH_BAST
++ 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 kernel-source-2.4.27-8/drivers/ide/arm/Makefile kernel-source-2.4.27-8-arm-1/drivers/ide/arm/Makefile
+--- kernel-source-2.4.27-8/drivers/ide/arm/Makefile 2003-06-13 15:51:33.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/arm/Makefile 2005-02-18 17:48:42.000000000 +0000
+@@ -6,6 +6,8 @@
+
+ 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
++obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o
+
+ EXTRA_CFLAGS := -I../
+
+diff -urN kernel-source-2.4.27-8/drivers/ide/arm/bast-ide.c kernel-source-2.4.27-8-arm-1/drivers/ide/arm/bast-ide.c
+--- kernel-source-2.4.27-8/drivers/ide/arm/bast-ide.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/arm/bast-ide.c 2005-02-18 17:48:42.000000000 +0000
+@@ -0,0 +1,146 @@
++/* linux/drivers/ide/bast-ide.c
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ *
++ * 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:
++ * 22-May-2003 BJD Created file, PIO code
++ *
++*/
++
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/blkdev.h>
++#include <linux/errno.h>
++#include <linux/ide.h>
++#include <linux/init.h>
++
++#include <asm/io.h>
++#include <asm/arch/map.h>
++
++static void bast_ide_input_data (ide_drive_t *drive, void *, unsigned int);
++static void bast_ide_output_data (ide_drive_t *drive, void *, unsigned int);
++static void bast_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int);
++static void bast_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int);
++
++extern void __raw_m8_readsl(void *reg, void *data, int count);
++extern void __raw_m4_readsl(void *reg, void *data, int count);
++
++/* choose io function to use */
++
++#if 0
++#define in_data_fn(datareg, data, count) __raw_m8_readsl((void *)datareg, data, count);
++#endif
++
++#if 1
++#define in_data_fn(datareg, data, count) __raw_m4_readsl((void *)datareg, data, count);
++#endif
++
++#ifndef in_data_fn
++#define in_data_fn(dr,dt,ct) insl(dr,dt,ct)
++#endif
++
++static int bast_ide_speedups = 1;
++
++static inline int
++bastide_register_channel(unsigned int base, unsigned int aux, int irq)
++{
++ ide_hwif_t *hwif;
++ hw_regs_t hw;
++ int res;
++ int i;
++
++ memset(&hw, 0, sizeof(hw));
++
++ base += BAST_IDE_CS;
++ aux += BAST_IDE_CS;
++
++ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
++ hw.io_ports[i] = (ide_ioreg_t)(base);
++
++ base += 0x20;
++ }
++
++ hw.io_ports[IDE_CONTROL_OFFSET] = aux + (6 * 0x20);
++ hw.irq = irq;
++
++ i = IDE_CONTROL_OFFSET;
++ hwif = NULL;
++ res = ide_register_hw(&hw, &hwif);
++
++ /* replace the standard read/write data function */
++ if (hwif != NULL && bast_ide_speedups) {
++ hwif->ata_input_data = &bast_ide_input_data;
++ hwif->ata_output_data = &bast_ide_output_data;
++ hwif->atapi_input_bytes = &bast_atapi_input_bytes;
++ hwif->atapi_output_bytes = &bast_atapi_output_bytes;
++ }
++
++ if (hwif !=NULL)
++ printk(" %s: at %08x (%08x)\n",hwif->name,base,aux);
++
++ return res;
++}
++
++int bastide_init(void)
++{
++ printk("bast: IDE driver, (c) 2003 Simtec Electronics\n");
++
++ bastide_register_channel(BAST_VA_IDEPRI, BAST_VA_IDEPRIAUX, IRQ_IDE0);
++ bastide_register_channel(BAST_VA_IDESEC, BAST_VA_IDESECAUX, IRQ_IDE1);
++
++ return 0;
++}
++
++
++static void bast_ide_input_data (ide_drive_t *drive, void *data, unsigned int count)
++{
++ unsigned long datareg = IDE_DATA_REG;
++ //printk("bast: ide_input_data: data=%p, count=%d\n", data, count);
++ in_data_fn(datareg, data, count);
++ return;
++}
++
++static void bast_ide_output_data (ide_drive_t *drive, void *data, unsigned int count)
++{
++ unsigned long datareg = IDE_DATA_REG;
++ //printk("bast: ide_output_data: data=%p, count=%d\n", data, count);
++ outsl(datareg, data, count);
++ return;
++}
++
++static void bast_atapi_input_bytes(ide_drive_t *drive, void *data, unsigned int count)
++{
++ unsigned long datareg = IDE_DATA_REG;
++ unsigned char *tmp = (unsigned char *)data;
++ count++;
++
++ //printk("bast: atapi_input_bytes: bytes=%d, data=%p\n", count, data);
++ in_data_fn(datareg, data, count >> 2);
++
++ if ((count & 3) >= 2) {
++ tmp += count & ~0x3;
++ insw(datareg, tmp, 1);
++ }
++
++ return;
++}
++
++static void bast_atapi_output_bytes(ide_drive_t *drive, void *data, unsigned int count)
++{
++ unsigned long datareg = IDE_DATA_REG;
++ unsigned char *tmp = (unsigned char *)data;
++ count++;
++
++ //printk("bast: atapi_output_bytes: bytes=%d, data=%p\n", count, data);
++ outsl(datareg, data, count >> 2);
++
++ if ((count & 3) >= 2) {
++ tmp += count & ~3;
++ outsw(datareg, tmp, 1);
++ }
++ return;
++}
+diff -urN kernel-source-2.4.27-8/drivers/ide/arm/icside.c kernel-source-2.4.27-8-arm-1/drivers/ide/arm/icside.c
+--- kernel-source-2.4.27-8/drivers/ide/arm/icside.c 2003-06-13 15:51:33.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/arm/icside.c 2005-02-18 17:48:42.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 <asm/ecard.h>
+ #include <asm/io.h>
+
+-#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;
++ ide_hwif_t *hwif = HWIF(drive);
++ struct icside_state *state = hwif->hwif_data;
++ unsigned long flags;
+
+- id = inb(addr) & 1;
+- id |= (inb(addr + 1) & 1) << 1;
+- id |= (inb(addr + 2) & 1) << 2;
+- id |= (inb(addr + 3) & 1) << 3;
++ local_irq_save(flags);
+
+- switch (id) {
+- case 0: /* A3IN */
+- printk("icside: A3IN unsupported\n");
+- break;
++ state->channel = hwif->channel;
+
+- case 1: /* A3USER */
+- printk("icside: A3USER unsupported\n");
++ 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 3: /* ARCIN V6 */
+- printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
+- iftype = ics_if_arcin_v6;
+- break;
+-
+- 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;
+-
+- 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 at arm.linux.org.uk\n");
+- printk("icside: defaulting to ARCIN V5\n");
+- iftype = ics_if_arcin_v5;
++ 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;
+
++ BUG_ON(hwif->sg_dma_active);
++
++ if (rq->cmd == IDE_DRIVE_TASKFILE) {
++ ide_task_t *args = rq->special;
++
++ 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 char *virt_addr = bh->b_data;
+- unsigned int size = bh->b_size;
++ 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;
+
+- while ((bh = bh->b_reqnext) != NULL) {
+- if ((virt_addr + size) != (unsigned char *)bh->b_data)
++ bh = bh->b_reqnext;
++ if (bh == NULL)
+ break;
+- size += bh->b_size;
+- }
+- memset(&sg[nents], 0, sizeof(*sg));
+- sg[nents].address = virt_addr;
+- sg[nents].length = size;
++ } while (lastend == bh_phys(bh));
++
++ sg++;
+ nents++;
+ } while (bh != NULL);
++ }
+
+- return pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction);
+-}
++ nents = pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction);
+
+-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);
++ hwif->sg_nents = nents;
+ }
+
+-/* 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;
+-
+- pci_unmap_sg(NULL, sg, nents, HWIF(drive)->sg_dma_direction);
+-}
+
+-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;
+
+- 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.
++ * Limit the transfer speed to MW_DMA_2.
+ */
+- drive->drive_data = 250;
++ if (xfer_mode > XFER_MW_DMA_2)
++ xfer_mode = XFER_MW_DMA_2;
++
++ switch (xfer_mode) {
++ case XFER_MW_DMA_2:
++ 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;
++}
++
++static int icside_dma_on(ide_drive_t *drive)
++{
++ drive->using_dma = 1;
++ return icside_dma_host_on(drive);
+ }
+
+-int icside_dma_check(ide_drive_t *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;
+
+- 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));
+
+- /* Route the DMA signals to
+- * to the correct interface.
++ ide_build_sglist(drive, rq);
++
++ /*
++ * 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,6 +717,10 @@
+ if (!hwif->sg_table)
+ goto failed;
+
++ 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;
+@@ -682,24 +737,33 @@
+ 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_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);
++
++ state->hwif[0] = hwif;
+
+- return hwif ? 0 : -1;
++ 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);
+
+-#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+- if (ec->dma != NO_DMA) {
+- if (request_dma(ec->dma, hwif->name))
+- goto no_dma;
++ if (!hwif || !mate)
++ return -ENODEV;
+
+- if (hwif) {
++ 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;
+- 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->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;
+- 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;
+- }
++
++ 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;
++
++ type = inb(addr) & 1;
++ type |= (inb(addr + 1) & 1) << 1;
++ type |= (inb(addr + 2) & 1) << 2;
++ type |= (inb(addr + 3) & 1) << 3;
++
++ state->type = type;
++ }
+
+- ec = ecard_find(0, icside_cids);
+- if (ec == NULL)
++ switch (state->type) {
++ case ICS_TYPE_A3IN:
++ printk(KERN_WARNING "icside: A3IN unsupported\n");
++ ret = -ENODEV;
+ break;
+
+- ecard_claim(ec);
++ case ICS_TYPE_A3USER:
++ printk(KERN_WARNING "icside: A3USER unsupported\n");
++ ret = -ENODEV;
++ break;
+
+- switch (icside_identifyif(ec)) {
+- case ics_if_arcin_v5:
+- result = icside_register_v5(ec, autodma);
++ case ICS_TYPE_V5:
++ ret = icside_register_v5(state, ec);
+ break;
+
+- case ics_if_arcin_v6:
+- result = icside_register_v6(ec, autodma);
++ case ICS_TYPE_V6:
++ ret = icside_register_v6(state, ec);
+ break;
+
+ default:
+- result = -1;
++ printk(KERN_WARNING "icside: unknown interface type\n");
++ ret = -ENODEV;
+ break;
+ }
+
+- if (result)
+- ecard_release(ec);
+- } while (1);
++ if (ret == 0) {
++ ecard_set_drvdata(ec, state);
++ } else {
++ kfree(state);
++ }
++ out:
++ return ret;
++}
+
+- return 0;
++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 kernel-source-2.4.27-8/drivers/ide/arm/rstation-ide.c kernel-source-2.4.27-8-arm-1/drivers/ide/arm/rstation-ide.c
+--- kernel-source-2.4.27-8/drivers/ide/arm/rstation-ide.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/arm/rstation-ide.c 2005-02-18 17:48:42.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 <linux/module.h>
++#include <linux/slab.h>
++#include <linux/blkdev.h>
++#include <linux/errno.h>
++#include <linux/ide.h>
++#include <linux/init.h>
++
++#include <asm/hardware/iomd.h>
++#include <asm/mach-types.h>
++#include <asm/io.h>
++
++#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 kernel-source-2.4.27-8/drivers/ide/ide-proc.c kernel-source-2.4.27-8-arm-1/drivers/ide/ide-proc.c
+--- kernel-source-2.4.27-8/drivers/ide/ide-proc.c 2005-01-19 09:57:48.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/ide-proc.c 2005-02-18 17:48:42.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/ide/ide.c kernel-source-2.4.27-8-arm-1/drivers/ide/ide.c
+--- kernel-source-2.4.27-8/drivers/ide/ide.c 2005-01-19 09:57:49.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/ide.c 2005-02-18 17:48:42.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;
+@@ -2531,6 +2545,18 @@
+ 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_IDE_BAST
++ {
++ extern void bastide_init(void);
++ bastide_init();
++ }
++#endif /* CONFIG_BLK_DEV_IDE_BAST */
+ #ifdef CONFIG_BLK_DEV_GAYLE
+ {
+ extern void gayle_init(void);
+diff -urN kernel-source-2.4.27-8/drivers/ide/pci/Makefile kernel-source-2.4.27-8-arm-1/drivers/ide/pci/Makefile
+--- kernel-source-2.4.27-8/drivers/ide/pci/Makefile 2004-04-14 14:05:29.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/pci/Makefile 2005-02-18 17:48:42.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/ide/pci/sl82c105.c kernel-source-2.4.27-8-arm-1/drivers/ide/pci/sl82c105.c
+--- kernel-source-2.4.27-8/drivers/ide/pci/sl82c105.c 2003-06-13 15:51:33.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/pci/sl82c105.c 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/ide/pci/sl82c105.c.2419 kernel-source-2.4.27-8-arm-1/drivers/ide/pci/sl82c105.c.2419
+--- kernel-source-2.4.27-8/drivers/ide/pci/sl82c105.c.2419 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ide/pci/sl82c105.c.2419 2005-02-18 17:48:42.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 <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/timer.h>
++#include <linux/mm.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/blkdev.h>
++#include <linux/hdreg.h>
++#include <linux/pci.h>
++#include <linux/ide.h>
++
++#include <asm/io.h>
++#include <asm/dma.h>
++
++#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 kernel-source-2.4.27-8/drivers/input/Config.in kernel-source-2.4.27-8-arm-1/drivers/input/Config.in
+--- kernel-source-2.4.27-8/drivers/input/Config.in 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/input/Config.in 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/input/Makefile kernel-source-2.4.27-8-arm-1/drivers/input/Makefile
+--- kernel-source-2.4.27-8/drivers/input/Makefile 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/input/Makefile 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/input/mx1ts.c kernel-source-2.4.27-8-arm-1/drivers/input/mx1ts.c
+--- kernel-source-2.4.27-8/drivers/input/mx1ts.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/input/mx1ts.c 2005-02-18 17:48:42.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/smp.h>
++#include <linux/smp_lock.h>
++#include <linux/sched.h>
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/pm.h>
++
++#include <asm/dma.h>
++
++#include <linux/input.h>
++
++#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 <jonm at bluemug.com>");
++MODULE_DESCRIPTION("MX1 touchscreen driver");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/input/mx1ts.h kernel-source-2.4.27-8-arm-1/drivers/input/mx1ts.h
+--- kernel-source-2.4.27-8/drivers/input/mx1ts.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/input/mx1ts.h 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/l3/Config.in kernel-source-2.4.27-8-arm-1/drivers/l3/Config.in
+--- kernel-source-2.4.27-8/drivers/l3/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/l3/Config.in 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/l3/Makefile kernel-source-2.4.27-8-arm-1/drivers/l3/Makefile
+--- kernel-source-2.4.27-8/drivers/l3/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/l3/Makefile 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/l3/l3-algo-bit.c kernel-source-2.4.27-8-arm-1/drivers/l3/l3-algo-bit.c
+--- kernel-source-2.4.27-8/drivers/l3/l3-algo-bit.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/l3/l3-algo-bit.c 2005-02-18 17:48:42.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 <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/algo-bit.h>
++
++#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 kernel-source-2.4.27-8/drivers/l3/l3-bit-sa1100.c kernel-source-2.4.27-8-arm-1/drivers/l3/l3-bit-sa1100.c
+--- kernel-source-2.4.27-8/drivers/l3/l3-bit-sa1100.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/l3/l3-bit-sa1100.c 2005-02-18 17:48:42.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ioport.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/i2c-algo-bit.h>
++#include <linux/l3/algo-bit.h>
++
++#include <asm/system.h>
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/arch/assabet.h>
++
++#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 kernel-source-2.4.27-8/drivers/l3/l3-core.c kernel-source-2.4.27-8-arm-1/drivers/l3/l3-core.c
+--- kernel-source-2.4.27-8/drivers/l3/l3-core.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/l3/l3-core.c 2005-02-18 17:48:42.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 <linux/module.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/proc_fs.h>
++#include <linux/kmod.h>
++#include <linux/init.h>
++#include <linux/l3/l3.h>
++
++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 kernel-source-2.4.27-8/drivers/l3/l3-sa1111.c kernel-source-2.4.27-8-arm-1/drivers/l3/l3-sa1111.c
+--- kernel-source-2.4.27-8/drivers/l3/l3-sa1111.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/l3/l3-sa1111.c 2005-02-18 17:48:42.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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/l3/l3.h>
++
++#include <asm/hardware.h>
++#include <asm/semaphore.h>
++#include <asm/mach-types.h>
++#include <asm/arch/assabet.h>
++#include <asm/hardware/sa1111.h>
++
++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 kernel-source-2.4.27-8/drivers/media/video/Config.in kernel-source-2.4.27-8-arm-1/drivers/media/video/Config.in
+--- kernel-source-2.4.27-8/drivers/media/video/Config.in 2004-04-14 14:05:30.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/media/video/Config.in 2005-02-18 17:48:42.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/media/video/Makefile kernel-source-2.4.27-8-arm-1/drivers/media/video/Makefile
+--- kernel-source-2.4.27-8/drivers/media/video/Makefile 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/media/video/Makefile 2005-02-18 17:48:42.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/media/video/cyberpro.c kernel-source-2.4.27-8-arm-1/drivers/media/video/cyberpro.c
+--- kernel-source-2.4.27-8/drivers/media/video/cyberpro.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/media/video/cyberpro.c 2005-02-18 17:48:42.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/videodev.h>
++#include <linux/video_decoder.h>
++#include <linux/mm.h>
++#include <linux/i2c-old.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/kmod.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/pgtable.h>
++#include <asm/pgalloc.h>
++
++MODULE_AUTHOR("Russell King <rmk at arm.linux.org.uk>");
++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 kernel-source-2.4.27-8/drivers/media/video/i2c-old.c kernel-source-2.4.27-8-arm-1/drivers/media/video/i2c-old.c
+--- kernel-source-2.4.27-8/drivers/media/video/i2c-old.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/media/video/i2c-old.c 2005-02-18 17:48:42.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;
+ }
+
+diff -urN kernel-source-2.4.27-8/drivers/media/video/saa7111.c kernel-source-2.4.27-8-arm-1/drivers/media/video/saa7111.c
+--- kernel-source-2.4.27-8/drivers/media/video/saa7111.c 2001-09-30 20:26:06.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/media/video/saa7111.c 2005-02-18 17:48:42.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 <linux/module.h>
++#include <linux/config.h>
+ #include <linux/init.h>
++#include <linux/module.h>
+ #include <linux/delay.h>
+ #include <linux/errno.h>
+ #include <linux/fs.h>
+@@ -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 kernel-source-2.4.27-8/drivers/message/i2o/i2o_core.c kernel-source-2.4.27-8-arm-1/drivers/message/i2o/i2o_core.c
+--- kernel-source-2.4.27-8/drivers/message/i2o/i2o_core.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/message/i2o/i2o_core.c 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/misc/Config.in kernel-source-2.4.27-8-arm-1/drivers/misc/Config.in
+--- kernel-source-2.4.27-8/drivers/misc/Config.in 1999-12-25 23:04:56.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/Config.in 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/misc/Makefile kernel-source-2.4.27-8-arm-1/drivers/misc/Makefile
+--- kernel-source-2.4.27-8/drivers/misc/Makefile 2000-12-29 22:07:22.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/Makefile 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/misc/mcp-core.c kernel-source-2.4.27-8-arm-1/drivers/misc/mcp-core.c
+--- kernel-source-2.4.27-8/drivers/misc/mcp-core.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/mcp-core.c 2005-02-18 17:48:42.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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/smp.h>
++
++#include <asm/dma.h>
++#include <asm/system.h>
++
++#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 <rmk at arm.linux.org.uk>");
++MODULE_DESCRIPTION("Core multimedia communications port driver");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/misc/mcp-sa1100.c kernel-source-2.4.27-8-arm-1/drivers/misc/mcp-sa1100.c
+--- kernel-source-2.4.27-8/drivers/misc/mcp-sa1100.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/mcp-sa1100.c 2005-02-18 17:48:42.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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/spinlock.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/system.h>
++
++#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 <rmk at arm.linux.org.uk>");
++MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/misc/mcp.h kernel-source-2.4.27-8-arm-1/drivers/misc/mcp.h
+--- kernel-source-2.4.27-8/drivers/misc/mcp.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/mcp.h 2005-02-18 17:48:42.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 kernel-source-2.4.27-8/drivers/misc/ucb1x00-audio.c kernel-source-2.4.27-8-arm-1/drivers/misc/ucb1x00-audio.c
+--- kernel-source-2.4.27-8/drivers/misc/ucb1x00-audio.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/ucb1x00-audio.c 2005-02-18 17:48:43.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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/list.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++
++#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 <rmk at arm.linux.org.uk>");
++MODULE_DESCRIPTION("UCB1x00 telecom/audio driver");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/misc/ucb1x00-core.c kernel-source-2.4.27-8-arm-1/drivers/misc/ucb1x00-core.c
+--- kernel-source-2.4.27-8/drivers/misc/ucb1x00-core.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/ucb1x00-core.c 2005-02-18 17:48:43.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 <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/pm.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++#include <asm/arch/shannon.h>
++
++#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 <rmk at arm.linux.org.uk>");
++MODULE_DESCRIPTION("UCB1x00 core driver");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/misc/ucb1x00-ts.c kernel-source-2.4.27-8-arm-1/drivers/misc/ucb1x00-ts.c
+--- kernel-source-2.4.27-8/drivers/misc/ucb1x00-ts.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/ucb1x00-ts.c 2005-02-18 17:48:43.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 <jco at ict.es> :
++ *
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/smp.h>
++#include <linux/smp_lock.h>
++#include <linux/sched.h>
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/pm.h>
++
++#include <asm/dma.h>
++#include <asm/semaphore.h>
++
++#include "ucb1x00.h"
++
++/*
++ * Define this if you want the UCB1x00 stuff to talk to the input layer
++ */
++#undef USE_INPUT
++
++#ifndef USE_INPUT
++
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/poll.h>
++
++/*
++ * 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 <linux/input.h>
++
++#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 <rmk at arm.linux.org.uk>");
++MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/misc/ucb1x00.h kernel-source-2.4.27-8-arm-1/drivers/misc/ucb1x00.h
+--- kernel-source-2.4.27-8/drivers/misc/ucb1x00.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/misc/ucb1x00.h 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/mtd/chips/cfi_probe.c kernel-source-2.4.27-8-arm-1/drivers/mtd/chips/cfi_probe.c
+--- kernel-source-2.4.27-8/drivers/mtd/chips/cfi_probe.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/chips/cfi_probe.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/mtd/chips/jedec_probe.c kernel-source-2.4.27-8-arm-1/drivers/mtd/chips/jedec_probe.c
+--- kernel-source-2.4.27-8/drivers/mtd/chips/jedec_probe.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/chips/jedec_probe.c 2005-02-18 17:48:43.000000000 +0000
+@@ -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,
+@@ -900,6 +920,16 @@
+ NumEraseRegions: 1,
+ regions: {ERASEINFO(0x01000,256),
+ }
++ }, {
++ mfr_id: MANUFACTURER_SST,
++ dev_id: SST39LF160,
++ name: "SST 39LF160",
++ DevSize: SIZE_2MiB,
++ CmdSet: P_ID_AMD_STD,
++ NumEraseRegions: 2,
++ regions: { ERASEINFO(0x1000,256),
++ ERASEINFO(0x1000,256)
++ }
+ }
+ };
+
+@@ -937,7 +967,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 kernel-source-2.4.27-8/drivers/mtd/devices/Config.in kernel-source-2.4.27-8-arm-1/drivers/mtd/devices/Config.in
+--- kernel-source-2.4.27-8/drivers/mtd/devices/Config.in 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/devices/Config.in 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/mtd/devices/Makefile kernel-source-2.4.27-8-arm-1/drivers/mtd/devices/Makefile
+--- kernel-source-2.4.27-8/drivers/mtd/devices/Makefile 2002-11-28 23:53:13.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/devices/Makefile 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/mtd/devices/syncflash.c kernel-source-2.4.27-8-arm-1/drivers/mtd/devices/syncflash.c
+--- kernel-source-2.4.27-8/drivers/mtd/devices/syncflash.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/devices/syncflash.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,615 @@
++/*
++ * MTD driver for Micron SyncFlash flash memory.
++ *
++ * Author: Jon McClintock <jonm at bluemug.com>
++ *
++ * Based loosely upon the LART flash driver, authored by Abraham vd Merwe
++ * <abraham at 2d3d.co.za>.
++ *
++ * 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 <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/version.h>
++#include <linux/errno.h>
++#include <linux/mtd/mtd.h>
++#include <asm/io.h>
++
++/* partition support */
++#define HAVE_PARTITIONS
++#ifdef HAVE_PARTITIONS
++#include <linux/mtd/partitions.h>
++#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 <jonm at bluemug.com>");
++MODULE_DESCRIPTION("MTD driver for Micron MT28S4M16LC SyncFlash on MX1ADS board");
++
++
+diff -urN kernel-source-2.4.27-8/drivers/mtd/maps/Config.in kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/Config.in
+--- kernel-source-2.4.27-8/drivers/mtd/maps/Config.in 2005-01-19 09:57:42.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/Config.in 2005-02-18 17:48:43.000000000 +0000
+@@ -81,10 +81,12 @@
+ 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 ' CFI Flash device mapped on Simtec BAST or Thorcom VR1000' CONFIG_MTD_BAST $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_BAST
++ dep_tristate ' Static RAM mapped on Thorcom VR1000' CONFIG_MTD_VR1000RAM $CONFIG_MACH_VR1000
+ 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 kernel-source-2.4.27-8/drivers/mtd/maps/Makefile kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/Makefile
+--- kernel-source-2.4.27-8/drivers/mtd/maps/Makefile 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/Makefile 2005-02-18 17:48:43.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
+@@ -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
+@@ -61,5 +60,7 @@
+ obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
+ obj-$(CONFIG_MTD_NETtel) += nettel.o
+ obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
++obj-$(CONFIG_MTD_BAST) += bast-flash.o
++obj-$(CONFIG_MTD_VR1000RAM) += vr1000-ram.o
+
+ include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27-8/drivers/mtd/maps/bast-flash.c kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/bast-flash.c
+--- kernel-source-2.4.27-8/drivers/mtd/maps/bast-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/bast-flash.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,196 @@
++/*
++ * Flash memory access on Simtec BAST and Thorcom VR1000
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * Based on neponset-flash, (C) 2000 Nicolas Pitre <nico at cam.org>
++ *
++ * $Id: neponset-flash.c,v 1.18 2001/07/14 00:59:17 thockin Exp $
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++#include <asm/arch/map.h>
++
++
++extern int parse_cmdline_partitions(struct mtd_info *master,
++ struct mtd_partition **pparts,
++ const char *mtd_id);
++
++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
++bastflash_enable_write(struct map_info *map, int to)
++{
++ __u8 val;
++
++ val = __raw_readb(BAST_VA_CTRL3);
++
++ if (to) {
++ val |= BAST_CPLD_CTRL3_ROMWEN;
++ } else {
++ val &= ~BAST_CPLD_CTRL3_ROMWEN;
++ }
++
++ __raw_writeb(val,BAST_VA_CTRL3);
++}
++
++static void write8(struct map_info *map, __u8 d, unsigned long adr)
++{
++ bastflash_enable_write(map, 1);
++ __raw_writeb(d, map->map_priv_1 + adr);
++ bastflash_enable_write(map, 0);
++}
++
++static void write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++ bastflash_enable_write(map, 1);
++ __raw_writew(d, map->map_priv_1 + adr);
++ bastflash_enable_write(map, 0);
++}
++
++static void write32(struct map_info *map, __u32 d, unsigned long adr)
++{
++ bastflash_enable_write(map, 1);
++ __raw_writew(d, map->map_priv_1 + adr);
++ __raw_writew(d>>16, map->map_priv_1 + adr + 2);
++ bastflash_enable_write(map, 0);
++}
++
++static void copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ bastflash_enable_write(map, 1);
++ memcpy_toio(map->map_priv_1 + to, from, len);
++ bastflash_enable_write(map, 0);
++}
++
++#define MAX_SZ (32 * 1024 * 1024)
++
++static struct map_info bast_map = {
++ name: "BAST",
++ size: MAX_SZ,
++ buswidth: 2,
++ read8: read8,
++ read16: read16,
++ read32: read32,
++ copy_from: copy_from,
++ write8: write8,
++ write16: write16,
++ write32: write32,
++ copy_to: copy_to,
++};
++
++#define FLASH_ADDR (0x4000000 + 0x08000000)
++
++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_partition *parsed_parts;
++static struct mtd_info *bast_mtd;
++
++int __init bast_mtd_init(void)
++{
++ struct mtd_partition *parts = NULL;
++ int nb_parts = 0;
++ int parsed_nr_parts = 0;
++ char *part_type = "none";
++
++ if (!machine_is_bast() && !machine_is_vr1000())
++ return -ENODEV;
++
++ printk("BAST Flash driver, (c) 2003 Simtec Electronics\n");
++
++ bast_map.map_priv_1 = (unsigned int)ioremap(FLASH_ADDR, MAX_SZ);
++ if (!bast_map.map_priv_1)
++ return -ENOMEM;
++
++ printk("BAST flash mapped to %08x\n", (int)bast_map.map_priv_1);
++
++ bast_mtd = do_map_probe("jedec_probe", &bast_map);
++ if (!bast_mtd)
++ return -ENXIO;
++
++ bast_mtd->module = THIS_MODULE;
++
++ /*
++ * Dynamic partition selection stuff (might override the static ones)
++ */
++#ifdef CONFIG_MTD_REDBOOT_PARTS
++ if (parsed_nr_parts == 0) {
++ int ret = parse_redboot_partitions(bast_mtd, &parsed_parts);
++
++ if (ret > 0) {
++ part_type = "RedBoot";
++ parsed_nr_parts = ret;
++ }
++ }
++#endif
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ if (parsed_nr_parts == 0) {
++ int ret = parse_cmdline_partitions(bast_mtd, &parsed_parts, "sa1100");
++ if (ret > 0) {
++ part_type = "Command Line";
++ parsed_nr_parts = ret;
++ }
++ }
++#endif
++
++ if (parsed_nr_parts > 0) {
++ parts = parsed_parts;
++ nb_parts = parsed_nr_parts;
++ }
++
++ if (nb_parts == 0) {
++ printk(KERN_NOTICE "BAST Flash: no partition info available, registering whole flash at once\n");
++ add_mtd_device(bast_mtd);
++ } else {
++ printk(KERN_NOTICE "BAST Flash: Using %s partition definition\n", part_type);
++ add_mtd_partitions(bast_mtd, parts, nb_parts);
++ }
++
++ return 0;
++}
++
++static void __exit bast_mtd_cleanup(void)
++{
++ if (bast_mtd)
++ map_destroy(bast_mtd);
++ if (bast_map.map_priv_1)
++ iounmap((void *)bast_map.map_priv_1);
++}
++
++module_init(bast_mtd_init);
++module_exit(bast_mtd_cleanup);
+diff -urN kernel-source-2.4.27-8/drivers/mtd/maps/dc21285.c kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/dc21285.c
+--- kernel-source-2.4.27-8/drivers/mtd/maps/dc21285.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/dc21285.c 2005-02-18 17:48:43.000000000 +0000
+@@ -11,6 +11,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/delay.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -18,9 +19,9 @@
+
+ #include <asm/io.h>
+ #include <asm/hardware/dec21285.h>
++#include <asm/mach-types.h>
+
+-
+-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 kernel-source-2.4.27-8/drivers/mtd/maps/epxa-flash.c kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/epxa-flash.c
+--- kernel-source-2.4.27-8/drivers/mtd/maps/epxa-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/epxa-flash.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,234 @@
++/*
++ * Flash memory access on EPXA based devices
++ *
++ * (C) 2000 Nicolas Pitre <nico at cam.org>
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#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; i<mymtd->numeraseregions;i++){
++ int j;
++ for(j=0;j<mymtd->eraseregions[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 kernel-source-2.4.27-8/drivers/mtd/maps/epxa10db-flash.c kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/epxa10db-flash.c
+--- kernel-source-2.4.27-8/drivers/mtd/maps/epxa10db-flash.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/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 <nico at cam.org>
+- * 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 <linux/config.h>
+-#include <linux/module.h>
+-#include <linux/types.h>
+-#include <linux/kernel.h>
+-#include <asm/io.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/mtd/map.h>
+-#include <linux/mtd/partitions.h>
+-
+-#include <asm/hardware.h>
+-#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; i<mymtd->numeraseregions;i++){
+- int j;
+- for(j=0;j<mymtd->eraseregions[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 kernel-source-2.4.27-8/drivers/mtd/maps/neponset-flash.c kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/neponset-flash.c
+--- kernel-source-2.4.27-8/drivers/mtd/maps/neponset-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/neponset-flash.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,109 @@
++/*
++ * Flash memory access on SA11x0 based devices
++ *
++ * (C) 2000 Nicolas Pitre <nico at cam.org>
++ *
++ * $Id: neponset-flash.c,v 1.18 2001/07/14 00:59:17 thockin Exp $
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/arch/assabet.h>
++
++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 kernel-source-2.4.27-8/drivers/mtd/maps/sa1100-flash.c kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/sa1100-flash.c
+--- kernel-source-2.4.27-8/drivers/mtd/maps/sa1100-flash.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/sa1100-flash.c 2005-02-18 17:48:43.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,6 +1121,8 @@
+ * 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("jedec_probe", &sa1100_map);
++ if (!mymtd)
+ mymtd = do_map_probe("cfi_probe", &sa1100_map);
+ ret = -ENXIO;
+ if (!mymtd)
+diff -urN kernel-source-2.4.27-8/drivers/mtd/maps/vr1000-ram.c kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/vr1000-ram.c
+--- kernel-source-2.4.27-8/drivers/mtd/maps/vr1000-ram.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/maps/vr1000-ram.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,182 @@
++/*
++ * based on physmap.c: $Id: physmap.c,v 1.21 2002/09/05 05:12:54 acurtis Exp $
++ *
++ * Mapping for VR1000 onboard RAM bank
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/config.h>
++
++#ifdef CONFIG_MTD_PARTITIONS
++#include <linux/mtd/partitions.h>
++#endif
++
++#include <asm/arch/map.h>
++
++#define WINDOW_ADDR (VR1000_PA_SRAM)
++#define WINDOW_SIZE (0x100000)
++#define BUSWIDTH 2
++
++static struct mtd_info *mymtd;
++
++static void
++vr1000_enable_write(struct map_info *map, int to)
++{
++ __u8 val;
++
++ val = __raw_readb(BAST_VA_CTRL2);
++
++ if (to) {
++ val |= VR1000_CPLD_CTRL2_RAMWEN;
++ } else {
++ val &= ~VR1000_CPLD_CTRL2_RAMWEN;
++ }
++
++ __raw_writeb(val, BAST_VA_CTRL2);
++}
++
++
++__u8 vr1000_ram_read8(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(map->map_priv_1 + ofs);
++}
++
++__u16 vr1000_ram_read16(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(map->map_priv_1 + ofs);
++}
++
++__u32 vr1000_ram_read32(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(map->map_priv_1 + ofs);
++}
++
++void vr1000_ram_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio(to, map->map_priv_1 + from, len);
++}
++
++void vr1000_ram_write8(struct map_info *map, __u8 d, unsigned long adr)
++{
++ printk("vr1000_ram_write8: map=%p, addr=%08x, %02x\n", map, adr, d);
++ vr1000_enable_write(map, 1);
++ __raw_writeb(d, map->map_priv_1 + adr);
++ vr1000_enable_write(map, 0);
++ mb();
++}
++
++void vr1000_ram_write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++ printk("vr1000_ram_write16: map=%p, addr=%08x, %04x\n", map, adr, d);
++ vr1000_enable_write(map, 1);
++ __raw_writew(d, map->map_priv_1 + adr);
++ vr1000_enable_write(map, 0);
++ mb();
++}
++
++void vr1000_ram_write32(struct map_info *map, __u32 d, unsigned long adr)
++{
++ printk("vr1000_ram_write32: map=%p, addr=%08x, %08x\n", map, adr, d);
++
++ vr1000_enable_write(map, 1);
++ __raw_writel(d, map->map_priv_1 + adr);
++ vr1000_enable_write(map, 0);
++ mb();
++}
++
++void vr1000_ram_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ vr1000_enable_write(map, 1);
++ memcpy_toio(map->map_priv_1 + to, from, len);
++ vr1000_enable_write(map, 0);
++}
++
++struct map_info vr1000_ram_map = {
++ name: "VR1000 RAM",
++ size: WINDOW_SIZE,
++ buswidth: 2,
++ read8: vr1000_ram_read8,
++ read16: vr1000_ram_read16,
++ read32: vr1000_ram_read32,
++ copy_from: vr1000_ram_copy_from,
++ write8: vr1000_ram_write8,
++ write16: vr1000_ram_write16,
++ write32: vr1000_ram_write32,
++ copy_to: vr1000_ram_copy_to
++};
++
++int __init init_vr1000_ram(void)
++{
++ printk(KERN_NOTICE "VR1000 RAM device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
++ vr1000_ram_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++
++ if (!vr1000_ram_map.map_priv_1) {
++ printk("VR1000 RAM: Failed to ioremap\n");
++ return -EIO;
++ }
++
++ printk("VR1000 RAM: Mapping at %08x\n", vr1000_ram_map.map_priv_1);
++
++ mymtd = do_map_probe("map_ram", &vr1000_ram_map);
++
++ if (mymtd) {
++ mymtd->module = THIS_MODULE;
++
++ add_mtd_device(mymtd);
++
++ /* disable any partition probes ftm */
++#if 0
++#ifdef CONFIG_MTD_PARTITIONS
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts,
++ "phys");
++ if (mtd_parts_nb > 0)
++ {
++ printk(KERN_NOTICE
++ "VR1000 RAM: Using command line partition definition\n");
++ add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb);
++ }
++#else
++ if (NUM_PARTITIONS != 0)
++ {
++ printk(KERN_NOTICE
++ "VR1000 RAM: Using partition definition\n");
++ add_mtd_partitions (mymtd, vr1000_ram_partitions, NUM_PARTITIONS);
++ }
++
++#endif
++#endif
++#endif
++ return 0;
++ }
++
++ iounmap((void *)vr1000_ram_map.map_priv_1);
++ return -ENXIO;
++}
++
++static void __exit cleanup_vr1000_ram(void)
++{
++ if (mymtd) {
++ del_mtd_device(mymtd);
++ map_destroy(mymtd);
++ }
++
++ if (vr1000_ram_map.map_priv_1) {
++ iounmap((void *)vr1000_ram_map.map_priv_1);
++ vr1000_ram_map.map_priv_1 = 0;
++ }
++}
++
++module_init(init_vr1000_ram);
++module_exit(cleanup_vr1000_ram);
++
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Ben Dooks <ben at simtec.co.uk>");
++MODULE_AUTHOR("David Woodhouse <dwmw2 at infradead.org>");
++MODULE_DESCRIPTION("VR1000 RAM Map");
+diff -urN kernel-source-2.4.27-8/drivers/mtd/nand/Config.in kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/Config.in
+--- kernel-source-2.4.27-8/drivers/mtd/nand/Config.in 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/Config.in 2005-02-18 17:48:43.000000000 +0000
+@@ -23,6 +23,13 @@
+ dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND
+ fi
+
++if [ "$CONFIG_ARCH_BAST" = "y" ]; then
++ dep_tristate ' NAND Flash device on S3C2410 systems' CONFIG_MTD_NAND_S3C2410 $CONFIG_MTD_NAND
++ dep_tristate ' NAND Flash support on Simtec BAST' CONFIG_MTD_NAND_BAST $CONFIG_MTD_NAND_S3C2410
++fi
++
++
++
+ if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then
+ define_bool CONFIG_MTD_NAND_IDS y
+ fi
+@@ -33,4 +40,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 kernel-source-2.4.27-8/drivers/mtd/nand/Makefile kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/Makefile
+--- kernel-source-2.4.27-8/drivers/mtd/nand/Makefile 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/Makefile 2005-02-18 17:48:43.000000000 +0000
+@@ -12,5 +12,7 @@
+ obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
+ obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
+ obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
++obj-$(CONFIG_MTD_NAND_S3C2410) += nand_s3c2410.o
++obj-$(CONFIG_MTD_NAND_BAST) += bast.o
+
+ include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27-8/drivers/mtd/nand/bast.c kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/bast.c
+--- kernel-source-2.4.27-8/drivers/mtd/nand/bast.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/bast.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,135 @@
++/*
++ * drivers/mtd/nand/bast.c
++ *
++ * Ben Dooks, <ben at simtec.co.uk>, (c) 2004 Simtec Electronics
++ *
++ *
++ *
++ * 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 <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <linux/interrupt.h>
++#include <asm/io.h>
++
++#include <asm/mach-types.h>
++#include <asm/arch-s3c2410/nand.h>
++
++#include <asm/arch/cpld.h>
++#include <asm/arch/map.h>
++
++#include "nand_s3c2410.h"
++
++/* first 32Kbyte is reserved for the boot-agent, of which the first
++ * 4Kbyte is loaded automatically loaded by the CPU at startup (when
++ * configured for NF boot). The boot-agent is then allowed to download
++ * the next pages/block to provide more facilities, if needed.
++ *
++ * the next 480Kbyte is reserved for ABLE to download as the next stage
++ * boot.
++ *
++ * We round to 2Mbyte with an 1.5Mbyte area for the kernel
++*/
++
++const static struct mtd_partition partition_info[] = {
++ { name: "NAND BootAgent",
++ offset: 0,
++ size: 32*1024 },
++ { name: "ABLE BootLoader",
++ offset: 32*1024,
++ size: 512*1024 - (32*1024) },
++ { name: "Linux Kernel",
++ offset: 512*1024,
++ size: 2*1024*1024 - (512*1024) },
++ { name: "Root",
++ offset: 2*1024*1024,
++ size: 14*1024*1024 },
++ { name: "/home",
++ offset: 16*1024*1024,
++ size: 16*1024*1024 },
++ { name: "/usr/bin",
++ offset: 32*1024*1024,
++ size: 8*1024*1024 },
++ { name: "/data",
++ offset: 40*1024*1024,
++ size: 24*1024*1024 }
++};
++
++static void
++bast_nand_select_slot(int slot)
++{
++ unsigned int tmp;
++ unsigned long reg = BAST_VA_CTRL2;
++
++ printk("bast_nand: selecting slot %d\n", slot);
++
++ slot &= ~3;
++
++ tmp = __raw_readb(reg);
++ tmp &= BAST_CPLD_CTLR2_IDERST;
++ tmp |= slot;
++ tmp |= BAST_CPLD_CTRL2_WNAND;
++
++ printk("bast_nand: ctrl2 now %02x\n", tmp);
++
++ __raw_writeb(tmp, reg);
++}
++
++#define array_sz(x) (sizeof(x) / sizeof(x[0]))
++
++#define NFCONF (S3C2410_NFCONF_EN | \
++ S3C2410_NFCONF_TACLS(6) | \
++ S3C2410_NFCONF_TWRPH0(6) | \
++ S3C2410_NFCONF_TWRPH1(6))
++
++int __init bast_nand_init (void)
++{
++ if (!machine_is_bast())
++ return 0;
++
++ printk("BAST NANDFlash driver, (c) 2004 Simtec Electronics\n");
++
++ bast_nand_select_slot(0);
++
++ s3c2410_attach_nand(partition_info, array_sz(partition_info), NFCONF);
++
++ return 0;
++}
++
++static void __exit bast_nand_exit(void)
++{
++ unsigned long reg = BAST_VA_CTRL2;
++ unsigned int tmp;
++
++ printk("bast_nand: removing WriteEnable from flash\n");
++
++ /* remove write-enable */
++
++ tmp = __raw_readb(reg);
++ tmp &= ~3;
++ tmp &= ~BAST_CPLD_CTRL2_WNAND;
++
++ __raw_writeb(tmp, reg);
++
++
++}
++
++module_init(bast_nand_init);
++modile_exit(bast_nand_exit);
++
++
++MODULE_LICENSE ("GPL");
++MODULE_AUTHOR ("Ben Dooks <ben at simtec.co.uk>");
++MODULE_DESCRIPTION ("Generic NAND flash driver code");
++
++
+diff -urN kernel-source-2.4.27-8/drivers/mtd/nand/nand.c kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/nand.c
+--- kernel-source-2.4.27-8/drivers/mtd/nand/nand.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/nand.c 2005-02-18 17:48:43.000000000 +0000
+@@ -1286,6 +1286,9 @@
+ nand_maf_id = readb (this->IO_ADDR_R);
+ nand_dev_id = readb (this->IO_ADDR_R);
+
++ printk("nand_maf_id = %02x, nand_dev_id = %02x\n",
++ nand_maf_id, nand_dev_id);
++
+ /* Print and store flash device information */
+ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+ if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) {
+diff -urN kernel-source-2.4.27-8/drivers/mtd/nand/nand_s3c2410.c kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/nand_s3c2410.c
+--- kernel-source-2.4.27-8/drivers/mtd/nand/nand_s3c2410.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/nand_s3c2410.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,408 @@
++/*
++ * drivers/mtd/nand/nand_s3c2410.o
++ *
++ * Ben Dooks, <ben at simtec.co.uk>, (c) 2004 Simtec Electronics
++ *
++ *
++ *
++ * 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 <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <linux/interrupt.h>
++#include <asm/io.h>
++
++#include <asm/arch-s3c2410/nand.h>
++
++/* we should be able to support the following:
++ * NAND_ECC_SOFT
++ * NAND_ECC_HW3_512
++ */
++
++/* mtd->priv points to struct nand_chip */
++
++static void *s3c2410_nfio; /* io-remapped area */
++
++#define s3c_nfaddr(addr) (((unsigned long)s3c2410_nfio) + (addr))
++
++#define rd_nfconf() readl(s3c_nfaddr(S3C2410_NFCONF))
++#define rd_nfstat() readb(s3c_nfaddr(S3C2410_NFSTAT))
++#define rd_nfecc(x) readb(s3c_nfaddr(S3C2410_NFECC + (x)))
++
++#define wr_nfconf(val) writel(val, s3c_nfaddr(S3C2410_NFCONF))
++
++#define nf_addr(val) writeb(val, s3c_nfaddr(S3C2410_NFADDR))
++#define nf_cmd(val) writeb(val, s3c_nfaddr(S3C2410_NFCMD))
++
++/* debugging */
++
++#define DEBUG_LEVEL 2
++
++#define DBG_PFX KERN_DEBUG
++
++#define dbf(lev, msg...) if (lev < DEBUG_LEVEL) printk(DBG_PFX "nand_s3c2410: " ## msg)
++
++
++
++static void s3c2410_nand_hwcontrol(int cmd)
++{
++ unsigned long cur;
++
++ switch (cmd) {
++ case NAND_CTL_SETNCE:
++ /* set the chip-enable */
++
++ cur = rd_nfconf();
++ cur &= ~S3C2410_NFCONF_nFCE;
++
++ dbf(6, "s3c2410_nand_hwcontrol(SETNCE) -> nfconf %08x\n", cur);
++
++ wr_nfconf(cur);
++
++ /* the upper layer will issue the delay */
++ break;
++
++ case NAND_CTL_CLRNCE:
++ cur = rd_nfconf();
++ cur |= S3C2410_NFCONF_nFCE;
++
++ dbf(6, "s3c2410_nand_hwcontrol(CLRNCE) -> nfconf %08x\n", cur);
++ wr_nfconf(cur);
++ break;
++
++ /* it seems the nand layer is insistent on calling these
++ * functions anyway, so we can happily ignore them */
++
++ case NAND_CTL_SETCLE:
++ dbf(4, "s3c2410_nand_hwcontrol(NAND_CTL_SETCLE)\n");
++ break;
++
++ case NAND_CTL_CLRCLE:
++ dbf(4, "s3c2410_nand_hwcontrol(NAND_CTL_CLRCLE)\n");
++ break;
++
++ case NAND_CTL_SETALE:
++ dbf(4, "s3c2410_nand_hwcontrol(NAND_CTL_SETALE)\n");
++ break;
++
++ case NAND_CTL_CLRALE:
++ dbf(4, "s3c2410_nand_hwcontrol(NAND_CTL_CLRCLE)\n");
++ break;
++
++ default:
++ printk("s3c2410_nand_hwcontrol: unknown cmd %d\n", cmd);
++ break;
++ }
++}
++
++/* s3c2410_nand_ready()
++ *
++ * returns 0 if the nand is busy, 1 if it is ready
++*/
++
++static int s3c2410_nand_ready(void)
++{
++ return rd_nfstat() & S3C2410_NFSTAT_BUSY;
++}
++
++
++static void s3c2410_nand_enable_ecc(int mode)
++{
++ unsigned long cur;
++
++ dbf(1, "s3c2410_nand_enable_ecc(%d)\n", mode);
++
++ /* called with either NAND_ECC_READ or NAND_ECC_WRITE */
++
++ /* the manual does not make it clear wether or not we need to
++ * clear the initialised flag after using it, but the swl5 patch
++ * only sets it.
++ */
++
++ cur = rd_nfconf();
++ cur |= S3C2410_NFCONF_INITECC;
++ wr_nfconf(cur);
++}
++
++static void s3c2410_nand_readecc(u_char *dat, u_char *ecc_code)
++{
++ /* first argument is unused */
++
++ /* TODO: check ordering into the buffer */
++ ecc_code[0] = rd_nfecc(0);
++ ecc_code[1] = rd_nfecc(1);
++ ecc_code[2] = rd_nfecc(2);
++
++ dbf(1, "s3c2410_read_ecc: return %02x, %02x, %02x\n",
++ ecc_code[0], ecc_code[1], ecc_code[2]);
++}
++
++/* s3c2410_nand_correctecc
++ *
++ *
++ * This code is from original s3c2410 patches by SW Lee,
++ * <hitchcar at sec.samsung.com>
++ */
++
++static int s3c2410_nand_correctecc(u_char *dat, u_char *read_ecc,
++ u_char *calc_ecc)
++{
++ u_char d1, d2, d3;
++ u_char ColECC;
++ unsigned int RowECC,i;
++ int ColNum=0,RowNum=0;
++ u_char CorrectData;
++
++ dbf(1, "s3c2410_nand_correctecc: dat=%p, read_ecc=%p, calc_ecc=%p\n",
++ dat, read_ecc, calc_ecc);
++
++ /* Do error detection */
++ d1 = calc_ecc[0] ^ read_ecc[0];
++ d2 = calc_ecc[1] ^ read_ecc[1];
++ d3 = calc_ecc[2] ^ read_ecc[2];
++
++ dbf(1, "s3c2410_nand_correctecc: d1=%02x, d2=%02x, d3=%02x\n", d1, d2, d3);
++
++ if ((d1 | d2 | d3) == 0) {
++ /* No errors */
++ return 0;
++ }
++
++
++ ColECC = d3 >> 2;
++
++ for ( i = 0; i < 3; i++) {
++ if( ColECC&(0x3<<(i*2))) return -1;
++ ColNum |= ColECC&(0x10<<(i*2))? 1<<i:0;
++ }
++
++ RowECC = ((d3&0x3)<<16)|(d2<<8)|(d1<<0);
++
++ for ( i = 0; i < 9; i++) {
++ if( RowECC&(0x3<<(i*2))) return -1;
++ RowNum |= ColECC&(0x10<<(i*2))? 1<<i:0;
++ }
++
++ CorrectData = dat[RowNum];
++ dat[RowNum] = (CorrectData&~(1<<ColNum))|
++ /* Extract bit */
++ (/**/(CorrectData & (1<<ColNum))/**/^(1<<ColNum));
++
++ return 1;
++}
++
++
++
++/* s3c2410_nand_command
++ *
++ * send the specified command to the device, and any address information
++ * if needed. Some commands will expect the handler to wait for them to
++ * complete
++*/
++
++static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command,
++ int column, int page_addr)
++{
++ register struct nand_chip *this = mtd->priv;
++
++ dbf(2, "s3c2410_nand_command(%p, %d, %d, %d)\n",
++ mtd, command, column, page_addr);
++
++ /* first, we do the command */
++
++ if (command != NAND_CMD_SEQIN) {
++ nf_cmd(command);
++ } else {
++ dbf(2, "s3c2410_nand_command: SEQIN: ootblock=%d\n",
++ mtd->oobblock);
++
++ if (mtd->oobblock == 256 && column >= 256) {
++ column -= 256;
++ nf_cmd(NAND_CMD_READOOB);
++ nf_cmd(NAND_CMD_SEQIN);
++ } else if (mtd->oobblock == 512 && column >= 256) {
++ if (column < 512) {
++ column -= 256;
++ nf_cmd(NAND_CMD_READ1);
++ nf_cmd(NAND_CMD_SEQIN);
++ } else {
++ column -= 512;
++ nf_cmd(NAND_CMD_READOOB);
++ nf_cmd(NAND_CMD_SEQIN);
++ }
++ } else {
++ nf_cmd(NAND_CMD_READ0);
++ nf_cmd(NAND_CMD_SEQIN);
++ }
++ }
++
++ /* ok, we've finished issuing the command */
++
++ if (column != -1 || page_addr != -1) {
++ /* Serially input address */
++ if (column != -1)
++ nf_addr (column);
++
++ if (page_addr != -1) {
++ nf_addr(page_addr & 0xff);
++ nf_addr(page_addr >> 8);
++
++ /* One more address cycle for higher density devices */
++ if (mtd->size & 0x0c000000)
++ nf_addr(page_addr >> 16);
++ }
++
++ /* ok, we've done the address */
++ }
++
++ /*
++ * program and erase have their own busy handlers
++ * status and sequential in needs no delay
++ */
++ switch (command) {
++
++ case NAND_CMD_PAGEPROG:
++ case NAND_CMD_ERASE1:
++ case NAND_CMD_ERASE2:
++ case NAND_CMD_SEQIN:
++ case NAND_CMD_STATUS:
++ return;
++
++ case NAND_CMD_RESET:
++ /* fall through to the dev_ready() call to wait for
++ * flash ready */
++ break;
++
++ /* This applies to read commands */
++ default:
++ /* fall thorugh to dev_ready() test */
++ }
++
++ dbf(6, "s3c2410_nand_command: waiting for ready (%ld)\n", jiffies);
++
++ /* wait until command is processed */
++ while (!this->dev_ready());
++
++ dbf(6, "s3c2410_nand_command: got ready (%ld)\n", jiffies);
++}
++
++
++
++/* call nand scan */
++
++#define PFX "S3C-NAND: "
++
++int
++s3c2410_attach_nand(struct mtd_partition *defparts,
++ int defpart_nos,
++ unsigned int nfconf)
++{
++ struct mtd_info *s3c_mtd;
++ struct nand_chip *this;
++ int sz;
++
++ dbf(0, "s3c2410_attach_nand: parts=%p, no=%d, nfconf=%08x\n",
++ defparts, defpart_nos, nfconf);
++
++ if (s3c2410_nfio == NULL)
++ s3c2410_nfio = ioremap(S3C2410_PA_NAND, SZ_1K);
++
++ dbf(0, "s3c2410_nfio = %p\n", s3c2410_nfio);
++
++ if (s3c2410_nfio == NULL) {
++ printk(PFX "cannot ioremap() device\n");
++ return -EIO;
++ }
++
++ nfconf |= S3C2410_NFCONF_EN;
++
++ writel(nfconf, s3c2410_nfio);
++
++ sz = sizeof(struct mtd_info) + sizeof(struct nand_chip);
++ s3c_mtd = kmalloc(sz, GFP_KERNEL);
++
++ if (s3c_mtd == NULL) {
++ printk(PFX "No memory for NAND MTD Device\n");
++ return -ENOMEM;
++ }
++
++ memset(s3c_mtd, 0, sz);
++
++ printk(PFX "IO %p, allocated MTD at %p\n", s3c2410_nfio, s3c_mtd);
++
++ this = (struct nand_chip *) (&s3c_mtd[1]);
++
++ s3c_mtd->priv = this;
++
++ this->IO_ADDR_R = ((unsigned long)s3c2410_nfio) + S3C2410_NFDATA;
++ this->IO_ADDR_W = this->IO_ADDR_R;
++
++ dbf(0, "this->IO_ADDR_R = 0x%08lx\n", this->IO_ADDR_R);
++ dbf(0, "this->IO_ADDR_W = 0x%08lx\n", this->IO_ADDR_W);
++
++ this->hwcontrol = s3c2410_nand_hwcontrol;
++ this->dev_ready = s3c2410_nand_ready;
++ this->cmdfunc = s3c2410_nand_command;
++ this->waitfunc = NULL;
++ this->calculate_ecc = s3c2410_nand_readecc;
++ this->correct_data = s3c2410_nand_correctecc;
++ this->enable_hwecc = s3c2410_nand_enable_ecc;
++ this->eccmode = NAND_ECC_HW3_512;
++ this->eccsize = 512; /* correct? */
++ this->chip_delay = 200; /* 200us delay time */
++
++ dbf(0, "s3c2410_attach_nand: scanning NAND devices\n");
++
++ if (nand_scan(s3c_mtd)) {
++ iounmap(s3c2410_nfio);
++ s3c2410_nfio = NULL;
++
++ kfree(s3c_mtd);
++ return -ENXIO;
++ }
++
++ dbf(0, "s3c2410_attach_nand: allocating memory buffers\n");
++
++ /* Allocate memory for internal data buffer */
++ sz = sizeof(u_char) * (s3c_mtd->oobblock + s3c_mtd->oobsize);
++
++ dbf(0, "allocating %d for data buffer\n", sz);
++
++ this->data_buf = kmalloc (sz, GFP_KERNEL);
++ if (!this->data_buf) {
++ printk (PFX "Unable to allocate NAND data buffer\n");
++ kfree (s3c_mtd);
++ return -ENOMEM;
++ }
++
++ /* Allocate memory for internal data buffer */
++ sz = sizeof(u_char) * (s3c_mtd->oobblock + s3c_mtd->oobsize);
++
++ dbf(0, "allocating %d for cache\n", sz);
++
++ this->data_cache = kmalloc (sz, GFP_KERNEL);
++ if (!this->data_cache) {
++ printk (PFX "Unable to allocate NAND data cache\n");
++ kfree (this->data_buf);
++ kfree (s3c_mtd);
++ return -ENOMEM;
++ }
++ this->cache_page = -1;
++
++ /* Register the partitions */
++ add_mtd_partitions(s3c_mtd, defparts, defpart_nos);
++
++ dbf(0, "s3c2410_attach_nand: done\n");
++
++ return 0;
++}
+diff -urN kernel-source-2.4.27-8/drivers/mtd/nand/nand_s3c2410.h kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/nand_s3c2410.h
+--- kernel-source-2.4.27-8/drivers/mtd/nand/nand_s3c2410.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/nand_s3c2410.h 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,6 @@
++
++
++extern int s3c2410_attach_nand(struct mtd_partition *defparts,
++ int defpart_nos,
++ unsigned int nfconf);
++
+diff -urN kernel-source-2.4.27-8/drivers/mtd/nand/s3c2410nand.c kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/s3c2410nand.c
+--- kernel-source-2.4.27-8/drivers/mtd/nand/s3c2410nand.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/s3c2410nand.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,1282 @@
++/*
++ * drivers/mtd/nand.c
++ *
++ * Copyright (C) 2000 Steven J. Hill (sjhill at cotw.com)
++ * Modified for S3C2410 SW.LEE <hitchcar at sec.samsung.com>
++ * This file related to S3C2410 NAND Controller included in S3C2410 CPU
++ *
++ */
++
++/*
++ * S3C2410 generate H/W ECC but ECC Correction must be processed by S/W
++ * S/W ECC Correction function
++ * "s3c2410_nand_correct_data"
++ * leave mush to be desired
++ */
++
++
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ids.h>
++#include <linux/interrupt.h>
++#include <asm/io.h>
++#include <asm/arch/hardware.h>
++
++#ifdef CONFIG_MTD_NAND_ECC
++#include <linux/mtd/nand_ecc.h>
++#endif
++
++/*
++ * Macros for low-level register control
++ */
++#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); \
++ udelay (this->chip_delay);
++
++#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);
++
++/*
++ * Definition of the out of band configuration structure
++ */
++struct nand_oob_config {
++ int ecc_pos[6]; /* position of ECC bytes inside oob */
++ int badblock_pos; /* position of bad block flag inside oob -1 = inactive */
++ int eccvalid_pos; /* position of ECC valid flag inside oob -1 = inactive */
++};
++
++/*
++* Constants for oob configuration
++*/
++#define NAND_NOOB_ECCPOS0 0
++#define NAND_NOOB_ECCPOS1 1
++#define NAND_NOOB_ECCPOS2 2
++#define NAND_NOOB_ECCPOS3 3
++#define NAND_NOOB_ECCPOS4 4
++#define NAND_NOOB_ECCPOS5 5
++#define NAND_NOOB_BADBPOS -1
++#define NAND_NOOB_ECCVPOS -1
++
++#define NAND_JFFS2_OOB_ECCPOS0 0
++#define NAND_JFFS2_OOB_ECCPOS1 1
++#define NAND_JFFS2_OOB_ECCPOS2 2
++#define NAND_JFFS2_OOB_ECCPOS3 3
++#define NAND_JFFS2_OOB_ECCPOS4 6
++#define NAND_JFFS2_OOB_ECCPOS5 7
++#define NAND_JFFS2_OOB_BADBPOS 5
++#define NAND_JFFS2_OOB_ECCVPOS 4
++
++static struct nand_oob_config oob_config;
++/*
++ * NAND low-level MTD interface functions
++ */
++static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf);
++static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf, u_char *ecc_code);
++static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf);
++static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf);
++static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf,
++ u_char *ecc_code);
++static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf);
++static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen);
++static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
++static void nand_sync (struct mtd_info *mtd);
++static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this,
++ int page, int col, int last, u_char *ecc_code);
++
++
++/*
++ * Detect and correct a 1 bit error for 512 byte block
++ */
++static int s3c2410_nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
++{
++ u_char d1, d2, d3;
++ u_char ColECC;
++ unsigned int RowECC,i;
++ int ColNum=0,RowNum=0;
++ u_char CorrectData;
++
++ /* Do error detection */
++ d1 = calc_ecc[0] ^ read_ecc[0];
++ d2 = calc_ecc[1] ^ read_ecc[1];
++ d3 = calc_ecc[2] ^ read_ecc[2];
++
++ if ((d1 | d2 | d3) == 0) {
++ /* No errors */
++ return 0;
++ }
++ else {
++
++
++ ColECC = d3 >> 2;
++
++ for ( i = 0; i < 3; i++) {
++ if( ColECC&(0x3<<(i*2))) return -1;
++ ColNum |= ColECC&(0x10<<(i*2))? 1<<i:0;
++ }
++
++ RowECC = ((d3&0x3)<<16)|(d2<<8)|(d1<<0);
++
++ for ( i = 0; i < 9; i++) {
++ if( RowECC&(0x3<<(i*2))) return -1;
++ RowNum |= ColECC&(0x10<<(i*2))? 1<<i:0;
++ }
++
++ CorrectData = dat[RowNum];
++ dat[RowNum] = (CorrectData&~(1<<ColNum))|
++ /* Extract bit */
++ (/**/(CorrectData & (1<<ColNum))/**/^(1<<ColNum));
++ return 1;
++
++ }
++
++}
++
++
++/*
++ * Send command to NAND device
++ */
++static void nand_command (struct mtd_info *mtd, unsigned command,
++ int column, int page_addr)
++{
++ register struct nand_chip *this = mtd->priv;
++
++ /*
++ * Write out the command to the device.
++ */
++ if (command != NAND_CMD_SEQIN){
++ NF_CMD(command);
++ }
++ /* Page Program
++ * Page Program Dummy
++ * Page Porgram Multi Block
++ */
++ else {
++ if (mtd->oobblock == 256 && column >= 256) {
++ column -= 256;
++ NF_CMD(NAND_CMD_READOOB);
++ NF_CMD(NAND_CMD_SEQIN);
++ }
++ else if (mtd->oobblock == 512 && column >= 256) {
++ if (column < 512) {
++ column -= 256;
++ NF_CMD(NAND_CMD_READ1);
++ NF_CMD(NAND_CMD_SEQIN);
++ }
++ else {
++ column -= 512;
++ NF_CMD(NAND_CMD_READOOB);
++ NF_CMD(NAND_CMD_SEQIN);
++ }
++ }
++ else {
++ NF_CMD(NAND_CMD_READ0);
++ NF_CMD(NAND_CMD_SEQIN);
++ }
++ }
++
++ if (column != -1) {
++ NF_ADDR (column);
++ }
++
++ if (page_addr != -1) {
++ NF_ADDR ((unsigned char) (page_addr & 0xff));
++ NF_ADDR ((unsigned char) ((page_addr >> 8) & 0xff));
++ /* One more address cycle for higher density devices */
++ if (mtd->size & 0x0c000000) {
++ NF_ADDR ((unsigned char) ((page_addr >> 16) & 0x0f));
++ }
++ }
++
++ udelay (this->chip_delay);
++ this->hwcontrol(NAND_CTL_S3C_WAIT);
++}
++
++/*
++ * Get chip for selected access
++ */
++static inline void nand_get_chip(struct nand_chip *this,int new_state, int *erase_state) {
++
++ DECLARE_WAITQUEUE(wait, current);
++
++ /* Grab the lock and see if the device is available */
++retry:
++ spin_lock_bh (&this->chip_lock);
++
++ switch (this->state) {
++ case FL_READY:
++ this->state = new_state;
++ if (new_state != FL_ERASING)
++ spin_unlock_bh (&this->chip_lock);
++ break;
++
++ case FL_ERASING:
++ if (new_state == FL_READING) {
++ this->state = FL_READING;
++ *erase_state = 1;
++ spin_unlock_bh (&this->chip_lock);
++ break;
++ }
++
++ default:
++ set_current_state (TASK_UNINTERRUPTIBLE);
++ add_wait_queue (&this->wq, &wait);
++ spin_unlock_bh (&this->chip_lock);
++ schedule();
++
++ remove_wait_queue (&this->wq, &wait);
++ goto retry;
++ };
++}
++
++/*
++ * Nand_page_program function is used for write and writev !
++ */
++static int nand_write_page(struct mtd_info *mtd, struct nand_chip *this,
++ int page,int col, int last, u_char *ecc_code) {
++
++ int i, status;
++#ifdef CONFIG_MTD_NAND_ECC
++ int ecc_bytes = 3; /* NAND FLASH ECC REGISTER [0:23] */
++#endif
++ /* pad oob area */
++ for(i=mtd->oobblock;i < mtd->oobblock+mtd->oobsize; i++)
++ this->data_buf[i] = 0xff;
++
++ /* Prepad for partial page programming !!! */
++ for (i=0 ; i < col ; i++)
++ this->data_buf[i] = 0xff;
++
++ /* Postpad for partial page programming !!! oob is already padded */
++ for (i=last ; i < mtd->oobblock ; i++)
++ this->data_buf[i] = 0xff;
++
++ /* Send command to begin auto page programming */
++ nand_command (mtd, NAND_CMD_SEQIN, 0x00, page);
++
++#ifdef CONFIG_MTD_NAND_ECC /* only for s3c2410 */
++ NF_RSTECC();
++ for (i=0 ; i < (mtd->oobblock ) ; i++) {
++ NF_WRDATA(this->data_buf[i]);
++ }
++
++ this->data_buf[(mtd->oobblock + oob_config.ecc_pos[0])] = ecc_code[0]=rNFECC0;
++ this->data_buf[(mtd->oobblock + oob_config.ecc_pos[1])] = ecc_code[1]=rNFECC1;
++ this->data_buf[(mtd->oobblock + oob_config.ecc_pos[2])] = ecc_code[2]=rNFECC2;
++
++ if (oob_config.eccvalid_pos != -1)
++ this->data_buf[mtd->oobblock + oob_config.eccvalid_pos] = 0xf0;
++
++ for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++) {
++ NF_WRDATA(this->data_buf[i]);
++ }
++
++#else
++ /* Write out complete page of data */
++ for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++) {
++ NF_WRDATA(this->data_buf[i]);
++ }
++#endif
++
++ /* Send command to actually program the data */
++ nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++ /*
++ * Wait for program operation to complete. This could
++ * take up to 3000us (3ms) on some devices, so we try
++ * and exit as quickly as possible.
++ */
++ status = 0;
++ for (i=0 ; i<24 ; i++) {
++ /* Delay for 125us */
++ udelay (125);
++ /* Check the status */
++ if (this->dev_ready)
++ if (!this->dev_ready())
++ continue;
++
++ nand_command (mtd, NAND_CMD_STATUS, -1, -1);
++ status = NF_RDDATA();
++ if (status & 0x40)
++ break;
++ }
++ /* See if device thinks it succeeded */
++ if (status & 0x01) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_ecc: " \
++ "Failed write, page 0x%08x, " \
++ " bytes were succesful\n", page);//, *retlen);
++ return -EIO;
++ }
++
++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* NOT TESTED IN S3C2410 */
++ /*
++ * The NAND device assumes that it is always writing to
++ * a cleanly erased page. Hence, it performs its internal
++ * write verification only on bits that transitioned from
++ * 1 to 0. The device does NOT verify the whole page on a
++ * byte by byte basis. It is possible that the page was
++ * not completely erased or the page is becoming unusable
++ * due to wear. The read with ECC would catch the error
++ * later when the ECC page check fails, but we would rather
++ * catch it early in the page write stage. Better to write
++ * no data than invalid data.
++ */
++
++ /* Send command to read back the page */
++ if (col < mtd->eccsize)
++ nand_command (mtd, NAND_CMD_READ0, col, page);
++ else
++ nand_command (mtd, NAND_CMD_READ1, col - 256, page);
++
++ /* Loop through and verify the data */
++ for (i=col ; i < last ; i++) {
++ if (this->data_buf[i] != NF_RDDATA()) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_ecc: " \
++ "Failed write verify, page 0x%08x, " \
++ " bytes were succesful\n",
++ page);//, *retlen);
++ return -EIO;
++ }
++ }
++
++#ifdef CONFIG_MTD_NAND_ECC
++ /*
++ * We also want to check that the ECC bytes wrote
++ * correctly for the same reasons stated above.
++ */
++ nand_command (mtd, NAND_CMD_READOOB, 0x00, page);
++ for (i=0 ; i < mtd->oobsize ; i++)
++ this->data_buf[i] = NF_RDDATA();
++ for (i=0 ; i < ecc_bytes ; i++) {
++ if ( (this->data_buf[(oob_config.ecc_pos[i])] != ecc_code[i]) && ecc_code[i]) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_ecc: Failed ECC write " \
++ "verify, page 0x%08x, " \
++ "%6i bytes were succesful\n",
++ page, i);
++ return -EIO;
++ }
++ }
++#endif
++#endif
++ return 0;
++}
++
++/*
++ * NAND read
++ */
++static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++
++#ifdef CONFIG_MTD_NAND_ECC
++ struct nand_chip *this = mtd->priv;
++
++ return nand_read_ecc (mtd, from, len, retlen, buf, this->ecc_code_buf);
++#else
++ return nand_read_ecc (mtd, from, len, retlen, buf, NULL);
++#endif
++}
++
++/*
++ * NAND read with ECC
++ */
++static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf, u_char *ecc_code)
++{
++ int j, col, page;
++ int erase_state = 0;
++ int ecc_status = 0, ecc_failed = 0;
++ struct nand_chip *this = mtd->priv;
++ u_char *data_poi;
++#ifdef CONFIG_MTD_NAND_ECC
++ u_char ecc_calc[3];
++
++#endif
++
++ DEBUG (MTD_DEBUG_LEVEL3,
++ "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from,
++ (int) len);
++
++ /* Do not allow reads past end of device */
++ if ((from + len) > mtd->size) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_read_ecc: Attempt read beyond end of device\n");
++ *retlen = 0;
++ return -EINVAL;
++ }
++
++ /* Grab the lock and see if the device is available */
++ nand_get_chip(this,FL_READING,&erase_state);
++
++ /* First we calculate the starting page */
++ page = from >> this->page_shift;
++
++ /* Get raw starting column */
++ col = from & (mtd->oobblock - 1);
++
++ /* Initialize return value */
++ *retlen = 0;
++
++ /* Select the NAND device */
++ nand_select ();
++
++ /* Loop until all data read */
++ while (*retlen < len) {
++
++#ifdef CONFIG_MTD_NAND_ECC
++
++ /* Do we have this page in cache ? */
++ if (this->cache_page == page)
++ goto readdata;
++ NF_RSTECC();
++ /* Send the read command */
++ nand_command (mtd, NAND_CMD_READ0, 0x00, page);
++ /* Read in a page + oob data*/
++ for (j=0 ; j < mtd->oobblock + mtd->oobsize ; j++)
++ this->data_buf[j] = NF_RDDATA();
++
++ nand_command (mtd, NAND_CMD_READ0, 0x00, page);
++
++ /* copy data into cache, for read out of cache and if ecc fails */
++ if (this->data_cache)
++ memcpy(this->data_cache,this->data_buf,mtd->oobblock+mtd->oobsize);
++
++ /* Pick the ECC bytes out of the oob data*/
++ for (j=0 ; j < 6 ; j++)
++ ecc_code[j] = this->data_buf[(mtd->oobblock + oob_config.ecc_pos[j])];
++
++ /* Calculate the ECC and verify it */
++ /* If block was not written with ECC, skip ECC */
++ if (oob_config.eccvalid_pos != -1 &&
++ (this->data_buf[mtd->oobblock+oob_config.eccvalid_pos] & 0x0f) != 0x0f ){
++ ecc_calc[0] = rNFECC0;
++ ecc_calc[1] = rNFECC1;
++ ecc_calc[2] = rNFECC2;
++
++ switch (s3c2410_nand_correct_data(&this->data_buf[0],&ecc_code[0], &ecc_calc[0])){
++ case -1:
++ DEBUG (MTD_DEBUG_LEVEL0,"nand_read_ecc: " \
++ "Failed ECC read, page 0x%08x\n", page);
++ ecc_failed++;
++ break;
++ case 1:
++ case 2:/* transfer ECC corrected data to cache */
++ memcpy(this->data_cache,this->data_buf,512);
++ break;
++ }
++ }
++
++readdata:
++ /* Read the data from ECC data buffer into return buffer */
++ data_poi = (this->data_cache) ? this->data_cache : this->data_buf;
++ data_poi += col;
++ if ((*retlen + (mtd->oobblock - col)) >= len) {
++ while (*retlen < len)
++ buf[(*retlen)++] = *data_poi++;
++ }
++ else {
++ for (j=col ; j < mtd->oobblock ; j++)
++ buf[(*retlen)++] = *data_poi++;
++ }
++ /* Set cache page address, invalidate, if ecc_failed */
++ this->cache_page = (this->data_cache && !ecc_failed) ? page : -1;
++
++ ecc_status += ecc_failed;
++ ecc_failed = 0;
++
++#else
++ /* Send the read command */
++ nand_command (mtd, NAND_CMD_READ0, col, page);
++
++ /* Read the data directly into the return buffer */
++ if ((*retlen + (mtd->oobblock - col)) >= len) {
++ while (*retlen < len)
++ buf[(*retlen)++] = NF_RDDATA();
++ /* We're done */
++ continue;
++ }
++ else
++ for (j=col ; j < mtd->oobblock; j++)
++ buf[(*retlen)++] = NF_RDDATA();
++#endif
++
++#ifdef SWL_DEBUG_NAND_READ
++ {
++ int i =0;
++ printk("\n--------READING DATA-------\n");
++ for (i = 0 ;i< (*retlen) ; i++ ) {
++ printk("%02X ",buf[i]);
++ if (( i%16)== 0) printk("\n");
++ }
++
++ }
++#endif
++ /* For subsequent reads align to page boundary. */
++ col = 0;
++ /* Increment page address */
++ page++;
++ }
++
++ /* De-select the NAND device */
++ nand_deselect ();
++
++ /* Wake up anyone waiting on the device */
++ spin_lock_bh (&this->chip_lock);
++ if (erase_state)
++ this->state = FL_ERASING;
++ else
++ this->state = FL_READY;
++ wake_up (&this->wq);
++ spin_unlock_bh (&this->chip_lock);
++
++ /*
++ * Return success, if no ECC failures, else -EIO
++ * fs driver will take care of that, because
++ * retlen == desired len and result == -EIO
++ */
++ return ecc_status ? -EIO : 0;
++}
++
++/*
++ * NAND read out-of-band
++ */
++static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ int i, col, page;
++ int erase_state = 0;
++ struct nand_chip *this = mtd->priv;
++
++ DEBUG (MTD_DEBUG_LEVEL3,
++ "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from,
++ (int) len);
++
++ /* Shift to get page */
++ page = ((int) from) >> this->page_shift;
++
++ /* Mask to get column */
++ col = from & 0x0f;
++
++ /* Initialize return length value */
++ *retlen = 0;
++
++ /* Do not allow reads past end of device */
++ if ((from + len) > mtd->size) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_read_oob: Attempt read beyond end of device\n");
++ *retlen = 0;
++ return -EINVAL;
++ }
++
++ /* Grab the lock and see if the device is available */
++ nand_get_chip(this,FL_READING,&erase_state);
++
++ /* can we read out of cache ? */
++ if (this->cache_page == page && (col+len <= mtd->oobsize)) {
++ /* Read the data */
++ memcpy(buf,&this->data_cache[mtd->oobblock+col],len);
++ } else {
++
++ /* Select the NAND device */
++ nand_select ();
++
++ /* Send the read command */
++ nand_command (mtd, NAND_CMD_READOOB, col, page);
++ /*
++ * Read the data, if we read more than one page
++ * oob data, let the device transfer the data !
++ */
++ for (i = 0 ; i < len ; i++) {
++ buf[i] = NF_RDDATA();
++ if ( (col++ & (mtd->oobsize-1)) == (mtd->oobsize-1) )
++ udelay(this->chip_delay);
++ }
++ /* De-select the NAND device */
++ nand_deselect ();
++ }
++
++ /* Wake up anyone waiting on the device */
++ spin_lock_bh (&this->chip_lock);
++ if (erase_state)
++ this->state = FL_ERASING;
++ else
++ this->state = FL_READY;
++ wake_up (&this->wq);
++ spin_unlock_bh (&this->chip_lock);
++
++ /* Return happy */
++ *retlen = len;
++ return 0;
++}
++
++/*
++ * NAND write
++ */
++static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++#ifdef CONFIG_MTD_NAND_ECC
++ struct nand_chip *this = mtd->priv;
++
++ return nand_write_ecc (mtd, to, len, retlen, buf, this->ecc_code_buf);
++#else
++ return nand_write_ecc (mtd, to, len, retlen, buf, NULL);
++#endif
++}
++
++/*
++ * NAND write with ECC
++ */
++static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf,
++ u_char *ecc_code)
++{
++ int i, page, col, cnt, ret = 0;
++ struct nand_chip *this = mtd->priv;
++
++ DEBUG (MTD_DEBUG_LEVEL3,
++ "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to,
++ (int) len);
++
++ /* Do not allow write past end of device */
++ if ((to + len) > mtd->size) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_oob: Attempt to write past end of page\n");
++ return -EINVAL;
++ }
++
++ /* Grab the lock and see if the device is available */
++ nand_get_chip(this,FL_WRITING,NULL);
++
++ /* Shift to get page */
++ page = ((int) to) >> this->page_shift;
++
++ /* Get the starting column */
++ col = to & (mtd->oobblock - 1);
++
++ /* Initialize return length value */
++ *retlen = 0;
++
++ /* Select the NAND device */
++ nand_select ();
++
++ /* Check the WP bit */
++ nand_command (mtd, NAND_CMD_STATUS, -1, -1);
++ if (!(NF_RDDATA()& 0x80)) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_ecc: Device is write protected!!!\n");
++ ret = -EIO;
++ goto out;
++ }
++
++ /* Loop until all data is written */
++ while (*retlen < len) {
++ /* Invalidate cache, if we write to this page */
++ if (this->cache_page == page)
++ this->cache_page = -1;
++
++ /* Write data into buffer */
++ if ((col + len) >= mtd->oobblock)
++ for(i=col, cnt=0 ; i < mtd->oobblock ; i++, cnt++)
++ this->data_buf[i] = buf[(*retlen + cnt)];
++ else
++ for(i=col, cnt=0 ; cnt < (len - *retlen) ; i++, cnt++)
++ this->data_buf[i] = buf[(*retlen + cnt)];
++
++ /* We use the same function for write and writev !) */
++ ret = nand_write_page(mtd,this,page,col,i,ecc_code);
++ if (ret)
++ goto out;
++
++ /* Next data start at page boundary */
++ col = 0;
++
++ /* Update written bytes count */
++ *retlen += cnt;
++
++ /* Increment page address */
++ page++;
++ }
++
++ /* Return happy */
++ *retlen = len;
++
++out:
++ /* De-select the NAND device */
++ nand_deselect ();
++
++ /* Wake up anyone waiting on the device */
++ spin_lock_bh (&this->chip_lock);
++ this->state = FL_READY;
++ wake_up (&this->wq);
++ spin_unlock_bh (&this->chip_lock);
++
++ return ret;
++}
++
++/*
++ * NAND write out-of-band
++ */
++static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ int i, column, page, status, ret = 0;
++ struct nand_chip *this = mtd->priv;
++
++ DEBUG (MTD_DEBUG_LEVEL3,
++ "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to,
++ (int) len);
++
++ /* Shift to get page */
++ page = ((int) to) >> this->page_shift;
++
++ /* Mask to get column */
++ column = to & 0x1f;
++
++ /* Initialize return length value */
++ *retlen = 0;
++
++ /* Do not allow write past end of page */
++ if ((column + len) > mtd->oobsize) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_oob: Attempt to write past end of page\n");
++ return -EINVAL;
++ }
++
++ /* Grab the lock and see if the device is available */
++ nand_get_chip(this,FL_WRITING,NULL);
++
++ /* Select the NAND device */
++ nand_select ();
++
++ /* Check the WP bit */
++ nand_command (mtd, NAND_CMD_STATUS, -1, -1);
++ if (!(NF_RDDATA()& 0x80)) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_oob: Device is write protected!!!\n");
++ ret = -EIO;
++ goto out;
++ }
++
++ /* Invalidate cache, if we write to this page */
++ if (this->cache_page == page)
++ this->cache_page = -1;
++
++ /* Write ones for partial page programming */
++ for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++)
++ this->data_buf[i] = 0xff;
++
++ /* Transfer data */
++ for (i = 0; i< len; i++)
++ this->data_buf[i+mtd->oobblock+column] = buf[i];
++
++ /* Write out desired data */
++ nand_command (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);
++ for (i=0 ; i<mtd->oobsize ; i++)
++ writeb (this->data_buf[i+mtd->oobblock], this->IO_ADDR_W);
++
++ /* Send command to program the OOB data */
++ nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++ /*
++ * Wait for program operation to complete. This could
++ * take up to 3000us (3ms) on some devices, so we try
++ * and exit as quickly as possible.
++ */
++ status = 0;
++ for (i=0 ; i<24 ; i++) {
++ /* Delay for 125us */
++ udelay (125);
++ /* Check the status */
++ if (this->dev_ready)
++ if (!this->dev_ready())
++ continue;
++
++ /* Check the status */
++ nand_command (mtd, NAND_CMD_STATUS, -1, -1);
++ status = (int) NF_RDDATA();
++ if (status & 0x40)
++ break;
++ }
++
++ /* See if device thinks it succeeded */
++ if (status & 0x01) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_oob: " \
++ "Failed write, page 0x%08x\n", page);
++ ret = -EIO;
++ goto out;
++ }
++
++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
++ /* Send command to read back the data */
++ nand_command (mtd, NAND_CMD_READOOB, column, page);
++
++ /* Loop through and verify the data */
++ for (i=0 ; i<len ; i++) {
++ if (buf[i] != NF_RDDATA()) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_write_oob: " \
++ "Failed write verify, page 0x%08x\n", page);
++ ret = -EIO;
++ goto out;
++ }
++ }
++#endif
++ /* Return happy */
++ *retlen = len;
++
++out:
++ /* De-select the NAND device */
++ nand_deselect ();
++
++ /* Wake up anyone waiting on the device */
++ spin_lock_bh (&this->chip_lock);
++ this->state = FL_READY;
++ wake_up (&this->wq);
++ spin_unlock_bh (&this->chip_lock);
++
++ return ret;
++}
++
++/*
++ * NAND write with iovec
++ */
++static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen)
++{
++ int i, page, col, cnt, len, total_len, ret = 0;
++ struct nand_chip *this = mtd->priv;
++
++ /* Calculate total length of data */
++ total_len = 0;
++ for (i=0 ; i < count ; i++)
++ total_len += (int) vecs[i].iov_len;
++
++ DEBUG (MTD_DEBUG_LEVEL3,
++ "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to,
++ (unsigned int) total_len, count);
++
++ /* Do not allow write past end of page */
++ if ((to + total_len) > mtd->size) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_writev: Attempted write past end of device\n");
++ return -EINVAL;
++ }
++
++ /* Grab the lock and see if the device is available */
++ nand_get_chip(this,FL_WRITING,NULL);
++
++ /* Shift to get page */
++ page = ((int) to) >> this->page_shift;
++
++ /* Get the starting column */
++ col = to & (mtd->oobblock - 1);
++
++ /* Initialize return length value */
++ *retlen = 0;
++
++ /* Select the NAND device */
++ nand_select ();
++
++ /* Check the WP bit */
++ nand_command (mtd, NAND_CMD_STATUS, -1, -1);
++ if (!(NF_RDDATA() & 0x80)) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_writev: Device is write protected!!!\n");
++ ret = -EIO;
++ goto out;
++ }
++
++ /* Loop until all iovecs' data has been written */
++ cnt = col;
++ len = 0;
++ while (count) {
++ /* Invalidate cache, if we write to this page */
++ if (this->cache_page == page)
++ this->cache_page = -1;
++
++ /* Do any need pre-fill for partial page programming */
++ for (i=0 ; i < cnt ; i++)
++ this->data_buf[i] = 0xff;
++
++ /*
++ * Read data out of each tuple until we have a full page
++ * to write or we've read all the tuples.
++ */
++
++ while ((cnt < mtd->oobblock) && count) {
++ if (vecs->iov_base!=NULL && vecs->iov_len) {
++ this->data_buf[cnt++] =
++ ((u_char *) vecs->iov_base)[len++];
++ }
++ if (len >= (int) vecs->iov_len) {
++ vecs++;
++ len = 0;
++ count--;
++ }
++ }
++
++ /* We use the same function for write and writev !) */
++
++#ifdef CONFIG_MTD_NAND_ECC /* Added by SW.LEE */
++ ret = nand_write_page(mtd,this,page,col,cnt,this->ecc_code_buf);
++#else
++ ret = nand_write_page(mtd,this,page,col,cnt,NULL);
++#endif
++ if (ret)
++ goto out;
++
++ /* Update written bytes count */
++ *retlen += (cnt - col);
++
++ /* Reset written byte counter and column */
++ col = cnt = 0;
++
++ /* Increment page address */
++ page++;
++ }
++
++out:
++ /* De-select the NAND device */
++ nand_deselect ();
++
++ /* Wake up anyone waiting on the device */
++ spin_lock_bh (&this->chip_lock);
++ this->state = FL_READY;
++ wake_up (&this->wq);
++ spin_unlock_bh (&this->chip_lock);
++
++ /* Return happy */
++ return ret;
++}
++
++/*
++ * NAND erase a block
++ */
++static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
++{
++ int i, page, len, status, pages_per_block, ret;
++ struct nand_chip *this = mtd->priv;
++ DECLARE_WAITQUEUE(wait, current);
++
++ DEBUG (MTD_DEBUG_LEVEL3,
++ "nand_erase: start = 0x%08x, len = %i\n",
++ (unsigned int) instr->addr, (unsigned int) instr->len);
++
++ /* Start address must align on block boundary */
++ if (instr->addr & (mtd->erasesize - 1)) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_erase: Unaligned address\n");
++ return -EINVAL;
++ }
++
++ /* Length must align on block boundary */
++ if (instr->len & (mtd->erasesize - 1)) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_erase: Length not block aligned\n");
++ return -EINVAL;
++ }
++
++ /* Do not allow erase past end of device */
++ if ((instr->len + instr->addr) > mtd->size) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_erase: Erase past end of device\n");
++ return -EINVAL;
++ }
++
++ /* Grab the lock and see if the device is available */
++ nand_get_chip(this,FL_ERASING,NULL);
++
++ /* Shift to get first page */
++ page = (int) (instr->addr >> this->page_shift);
++
++ /* Calculate pages in each block */
++ pages_per_block = mtd->erasesize / mtd->oobblock;
++
++ /* Select the NAND device */
++ nand_select ();
++
++ /* Check the WP bit */
++ nand_command (mtd, NAND_CMD_STATUS, -1, -1);
++ if (!(NF_RDDATA()& 0x80)) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_erase: Device is write protected!!!\n");
++ nand_deselect ();
++ this->state = FL_READY;
++ spin_unlock_bh (&this->chip_lock);
++ return -EIO;
++ }
++
++ /* Loop through the pages */
++ len = instr->len;
++
++ instr->state = MTD_ERASING;
++
++ while (len) {
++ if (oob_config.badblock_pos != -1) {
++ /* Check if we have a bad block, we do not erase bad blocks !*/
++ nand_command (mtd, NAND_CMD_READOOB, oob_config.badblock_pos, page);
++ if ( NF_RDDATA()!= 0xff) {
++ printk(KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n",page);
++ instr->state = MTD_ERASE_FAILED;
++ goto erase_exit;
++ }
++ }
++
++ /* Send commands to erase a page */
++ nand_command(mtd, NAND_CMD_ERASE1, -1, page);
++ nand_command(mtd, NAND_CMD_ERASE2, -1, -1);
++
++ /*
++ * Wait for program operation to complete. This could
++ * take up to 4000us (4ms) on some devices, so we try
++ * and exit as quickly as possible.
++ */
++ status = 0;
++ for (i=0 ; i<32 ; i++) {
++ /* Delay for 125us */
++ udelay (125);
++ /* Check the status */
++ if (this->dev_ready)
++ if (!this->dev_ready())
++ continue;
++ /* Check the status */
++ nand_command (mtd, NAND_CMD_STATUS, -1, -1);
++ status = (int) NF_RDDATA();
++ if (status & 0x40)
++ break;
++ }
++
++ /* See if block erase succeeded */
++ if (status & 0x01) {
++ DEBUG (MTD_DEBUG_LEVEL0,
++ "nand_erase: " \
++ "Failed erase, page 0x%08x\n", page);
++ instr->state = MTD_ERASE_FAILED;
++ goto erase_exit;
++ }
++
++ /* Invalidate cache, if last_page is inside erase-block */
++ if (this->cache_page >= page && this->cache_page < (page + pages_per_block) )
++ this->cache_page = -1;
++
++ /* Increment page address and decrement length */
++ len -= mtd->erasesize;
++ page += pages_per_block;
++
++ /* Release the spin lock */
++ spin_unlock_bh (&this->chip_lock);
++
++erase_retry:
++ /* Check the state and sleep if it changed */
++ spin_lock_bh (&this->chip_lock);
++ if (this->state == FL_ERASING) {
++ /* Select the NAND device again, if we were interrupted*/
++ nand_select ();
++ continue;
++ }
++ else {
++ set_current_state (TASK_UNINTERRUPTIBLE);
++ add_wait_queue (&this->wq, &wait);
++ spin_unlock_bh (&this->chip_lock);
++ schedule();
++
++ remove_wait_queue (&this->wq, &wait);
++ goto erase_retry;
++ }
++ }
++ instr->state = MTD_ERASE_DONE;
++
++erase_exit:
++ spin_unlock_bh (&this->chip_lock);
++
++ /* De-select the NAND device */
++ nand_deselect ();
++
++ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;
++ /* Do call back function */
++ if (!ret && instr->callback)
++ instr->callback (instr);
++
++ /* The device is ready */
++ spin_lock_bh (&this->chip_lock);
++ this->state = FL_READY;
++ spin_unlock_bh (&this->chip_lock);
++
++ /* Return more or less happy */
++ return ret;
++}
++
++/*
++ * NAND sync
++ */
++static void nand_sync (struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ DECLARE_WAITQUEUE(wait, current);
++
++ DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
++
++retry:
++ /* Grab the spinlock */
++ spin_lock_bh(&this->chip_lock);
++
++ /* See what's going on */
++ switch(this->state) {
++ case FL_READY:
++ case FL_SYNCING:
++ this->state = FL_SYNCING;
++ spin_unlock_bh (&this->chip_lock);
++ break;
++
++ default:
++ /* Not an idle state */
++ add_wait_queue (&this->wq, &wait);
++ spin_unlock_bh (&this->chip_lock);
++ schedule ();
++
++ remove_wait_queue (&this->wq, &wait);
++ goto retry;
++ }
++
++ /* Lock the device */
++ spin_lock_bh (&this->chip_lock);
++
++ /* Set the device to be ready again */
++ if (this->state == FL_SYNCING) {
++ this->state = FL_READY;
++ wake_up (&this->wq);
++ }
++
++ /* Unlock the device */
++ spin_unlock_bh (&this->chip_lock);
++}
++
++/*
++ * Scan for the NAND device
++ * S3C2410 generates 512 Byte ECC
++ *
++ */
++#define S3C2410_ECCSIZE 512
++
++int nand_scan (struct mtd_info *mtd)
++{
++ int i, nand_maf_id, nand_dev_id;
++ struct nand_chip *this = mtd->priv;
++
++ /* Select the device */
++ nand_select ();
++
++ /* Send the command for reading device ID */
++ nand_command (mtd, NAND_CMD_READID, 0x00, -1);
++
++ /* Read manufacturer and device IDs */
++ nand_maf_id = NF_RDDATA();
++ nand_dev_id = NF_RDDATA();
++
++ /* Print and store flash device information */
++ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
++ if (nand_maf_id == nand_flash_ids[i].manufacture_id &&
++ nand_dev_id == nand_flash_ids[i].model_id) {
++ if (!mtd->size) {
++ mtd->name = nand_flash_ids[i].name;
++ mtd->erasesize = nand_flash_ids[i].erasesize;
++ mtd->size = (1 << nand_flash_ids[i].chipshift);
++ mtd->eccsize = S3C2410_ECCSIZE;
++ if (nand_flash_ids[i].page256) {
++ mtd->oobblock = 256;
++ mtd->oobsize = 8;
++ this->page_shift = 8;
++ }
++ else {
++ mtd->oobblock = 512;
++ mtd->oobsize = 16;
++ this->page_shift = 9;
++ }
++ }
++ printk (KERN_INFO "NAND device: Manufacture ID:" \
++ " 0x%02x, Chip ID: 0x%02x (%s)\n",
++ nand_maf_id, nand_dev_id, mtd->name);
++ break;
++ }
++ }
++
++ /* Initialize state, waitqueue and spinlock */
++ this->state = FL_READY;
++ init_waitqueue_head(&this->wq);
++ spin_lock_init(&this->chip_lock);
++
++ /* De-select the device */
++ nand_deselect ();
++ /*
++ * Preset out of band configuration
++ */
++#ifdef CONFIG_MTD_NAND_ECC_JFFS2
++ oob_config.ecc_pos[0] = NAND_JFFS2_OOB_ECCPOS0;
++ oob_config.ecc_pos[1] = NAND_JFFS2_OOB_ECCPOS1;
++ oob_config.ecc_pos[2] = NAND_JFFS2_OOB_ECCPOS2;
++ oob_config.ecc_pos[3] = NAND_JFFS2_OOB_ECCPOS3;
++ oob_config.ecc_pos[4] = NAND_JFFS2_OOB_ECCPOS4;
++ oob_config.ecc_pos[5] = NAND_JFFS2_OOB_ECCPOS5;
++ oob_config.badblock_pos = 5;
++ oob_config.eccvalid_pos = 4;
++#else
++ oob_config.ecc_pos[0] = NAND_NOOB_ECCPOS0;
++ oob_config.ecc_pos[1] = NAND_NOOB_ECCPOS1;
++ oob_config.ecc_pos[2] = NAND_NOOB_ECCPOS2;
++ oob_config.ecc_pos[3] = NAND_NOOB_ECCPOS3;
++ oob_config.ecc_pos[4] = NAND_NOOB_ECCPOS4;
++ oob_config.ecc_pos[5] = NAND_NOOB_ECCPOS5;
++ oob_config.badblock_pos = NAND_NOOB_BADBPOS;
++ oob_config.eccvalid_pos = NAND_NOOB_ECCVPOS;
++#endif
++
++ /* Print warning message for no device */
++ if (!mtd->size) {
++ printk (KERN_WARNING "No NAND device found!!!\n");
++ return 1;
++ }
++
++ /* Fill in remaining MTD driver data */
++ mtd->type = MTD_NANDFLASH;
++ mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
++ mtd->module = THIS_MODULE;
++ mtd->ecctype = MTD_ECC_SW;
++ mtd->erase = nand_erase;
++ mtd->point = NULL;
++ mtd->unpoint = NULL;
++ mtd->read = nand_read;
++ mtd->write = nand_write;
++ mtd->read_ecc = nand_read_ecc;
++ mtd->write_ecc = nand_write_ecc;
++ mtd->read_oob = nand_read_oob;
++ mtd->write_oob = nand_write_oob;
++ mtd->readv = NULL;
++ mtd->writev = nand_writev;
++ mtd->sync = nand_sync;
++ mtd->lock = NULL;
++ mtd->unlock = NULL;
++ mtd->suspend = NULL;
++ mtd->resume = NULL;
++
++ /* Return happy */
++ return 0;
++}
++
++EXPORT_SYMBOL(nand_scan);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Steven J. Hill <sjhill at cotw.com");
++MODULE_DESCRIPTION("Generic NAND flash driver code");
+diff -urN kernel-source-2.4.27-8/drivers/mtd/nand/smdk2410.c kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/smdk2410.c
+--- kernel-source-2.4.27-8/drivers/mtd/nand/smdk2410.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/mtd/nand/smdk2410.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,288 @@
++/*
++ * drivers/mtd/s3c2410_nand.c
++ *
++ * Copyright (c) 2002 SAMSUNG ELECTRONICS
++ * SW.LEE <hitchcar at sec.samsung.com>
++ *
++ * Derived from drivers/mtd/spia.c,autcpu12.c
++ * Copyright (C) 2000 Steven J. Hill (sjhill at 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.
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device found on the
++ * autronix s3c2410 board, which is a SmartMediaCard. It supports
++ * 16MB, 32MB and 64MB cards.
++ */
++
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/arch/hardware.h>
++#include <asm/sizes.h>
++
++/*
++ * MTD structure for S3C2410 board
++ */
++static struct mtd_info *s3c2410_mtd = NULL;
++
++/*
++ * Module stuff
++ */
++#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
++#define s3c2410_nand_init init_module
++#define s3c2410_cleanup cleanup_module
++#endif
++
++
++#ifdef MODULE
++MODULE_PARM(s3c2410_temp, "i");
++__setup("s3c2410_temp=",s3c2410_temp);
++#endif
++
++
++/*
++ * Define partitions for flash devices
++ */
++
++
++/*
++ * THIS IS ONLY PARTITION FOR S3C2410 NAND CONTROLLER
++ *
++ * This partition drives from IPAQ Linux Partition
++ *
++ * HAVE THE BELOW PARTITION IN YOUR MIND
++ */
++
++static struct mtd_partition partition_info64k[] = {
++ /* BOOTLOADER + KERNEL */
++ { name: "S3C2410 flash partition 0",
++ offset: 0,
++ size: 2 * SZ_1M},
++
++ { name: "S3C2410 flash partition 1",
++ offset: 2 * SZ_1M,
++ size: 2 * SZ_1M},
++
++ { name: "S3C2410 flash partition 2",
++ offset: 4 * SZ_1M,
++ size: 4 * SZ_1M},
++
++ /* INIT CRAMFS */
++ { name: "S3C2410 flash partition 3",
++ offset: 8 * SZ_1M, /* Block Number 8 * 64 = 512 */
++ size: 2 * SZ_1M},
++
++ /* ROOT CRAMFS */
++ { name: "S3C2410 flash partition 4",
++ offset: 10 * SZ_1M, /* Block Number 10 * 64 = 640 */
++ size: 4 * SZ_1M},
++
++ /* USR CRAMFS */
++ { name: "S3C2410 flash partition 5",
++ offset: 14 * SZ_1M, /* Block number 14 * 64 = 896 */
++ size: 10 * SZ_1M},
++
++ /* EXT2 FILE SYSTEM */
++ { name: "S3C2410 flash partition 6",
++ offset: 24 * SZ_1M, /* Block number 24 * 64 = 1536 */
++ size: 24 * SZ_1M},
++
++ /* JFFS FILE SYSTEM */
++ { name: "S3C2410 flash partition 7",
++ offset: 48 * SZ_1M, /* Block number 48 * 64 = 3072 */
++ size: 16 * SZ_1M},
++
++};
++
++
++#define NUM_PARTITIONS64K 8
++
++/*
++ * hardware specific access to control-lines
++ */
++
++void s3c2410_hwcontrol(int cmd)
++{
++ switch(cmd){
++ /* SAMSUNG S3C2410 NAND CONTROLLER HARDWARE AUTOMATIC */
++ case NAND_CTL_SETCLE:break;
++ case NAND_CTL_CLRCLE:break;
++ case NAND_CTL_SETALE:break;
++ case NAND_CTL_CLRALE:break;
++ /* NAND FLASH MEMORY CHIP ENABLE -- active low */
++ case NAND_CTL_SETNCE: rNFCONF= (rNFCONF&~(1<<11))|(0<<11); break;
++ case NAND_CTL_CLRNCE: rNFCONF= (rNFCONF&~(1<<11))|(1<<11); break;
++ case NAND_CTL_S3C_WAIT: NF_WAITRB(); break;
++ }
++}
++
++/*
++* read device ready pin
++*/
++int s3c2410_device_ready(void)
++{
++ return (rNFSTAT&1) ? 1 : 0;
++}
++
++
++void NF_Reset(void)
++{
++ int i;
++
++
++ NF_nFCE_L();
++
++ NF_CMD(0xFF); //reset command
++
++ for(i=0;i<10;i++); //tWB = 100ns.
++
++ NF_WAITRB(); //wait 200~500us;
++
++ NF_nFCE_H();
++}
++
++#if 1
++#define TACLS 0
++#define TWRPH0 3
++#define TWRPH1 0
++#endif
++
++
++void NF_Init(void)
++{
++ rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
++ // 1 1 1 1, 1 xxx, r xxx, r xxx
++ // En 512B 4step ECCR nFCE=H tACLS tWRPH0 tWRPH1
++
++ NF_Reset();
++}
++
++
++/*
++ * Main initialization routine
++ */
++int __init s3c2410_nand_init (void)
++{
++ struct nand_chip *this;
++ int err = 0;
++
++ /* Allocate memory for MTD device structure and private data */
++ s3c2410_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
++ GFP_KERNEL);
++ if (!s3c2410_mtd) {
++ printk ("Unable to allocate S3C2410 NAND MTD device structure.\n");
++ err = -ENOMEM;
++ goto out;
++ }
++
++ /* map physical adress */
++ /*
++ s3c2410_fio_base=(unsigned long)ioremap(s3c2410_fio_pbase,SZ_1K);
++ if(!s3c2410_fio_base){
++ printk("Ioremap s3c2410 SmartMedia Card failed\n");
++ err = -EIO;
++ goto out_mtd;
++ }
++ */
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *) (&s3c2410_mtd[1]);
++
++ /* Initialize structures */
++ memset((char *) s3c2410_mtd, 0, sizeof(struct mtd_info));
++ memset((char *) this, 0, sizeof(struct nand_chip));
++
++ /* Link the private data with the MTD structure */
++ s3c2410_mtd->priv = this;
++
++ /* Set address of NAND IO lines */
++ this->hwcontrol = s3c2410_hwcontrol;
++ this->dev_ready = s3c2410_device_ready;
++ /* 200 us command delay time */
++ this->chip_delay = 1;
++
++ NF_Init(); /* S3C2410 NAND FLASH INIT */
++ /* Scan to find existance of the device */
++ if (nand_scan (s3c2410_mtd)) {/* see include/linux/mtd/nand_ids.h */
++ err = -ENXIO;
++ goto out_ior;
++ }
++
++ /* Allocate memory for internal data buffer */
++ this->data_buf = kmalloc (sizeof(u_char) * (s3c2410_mtd->oobblock + s3c2410_mtd->oobsize), GFP_KERNEL);
++ if (!this->data_buf) {
++ printk ("Unable to allocate NAND data buffer for S3C2410.\n");
++ err = -ENOMEM;
++ goto out_ior;
++ }
++
++ /* Allocate memory for internal data buffer */
++ this->data_cache = kmalloc (sizeof(u_char) * (s3c2410_mtd->oobblock + s3c2410_mtd->oobsize), GFP_KERNEL);
++ if (!this->data_cache) {
++ printk ("Unable to allocate NAND data cache for S3C2410.\n");
++ err = -ENOMEM;
++ goto out_buf;
++ }
++ this->cache_page = -1;
++
++ /* Register the partitions */
++ switch(s3c2410_mtd->size){
++ case SZ_64M: add_mtd_partitions(s3c2410_mtd, partition_info64k, NUM_PARTITIONS64K); break;
++ default:
++ printk("-------------------------------------------- \n");
++ printk("YOU MUST SEE nand_ids.h file \n");
++ printk("and must register your NAND SMC Device \n");
++ printk("Unsupported SmartMedia device\n");
++ printk (" Only test in MAF 0xEC, DEV 0x76 \n");
++ err = -ENXIO;
++ goto out_cac;
++
++ }
++ goto out;
++
++out_cac:
++ kfree (this->data_cache);
++out_buf:
++ kfree (this->data_buf);
++out_ior:
++out_mtd:
++ kfree (s3c2410_mtd);
++out:
++ return err;
++}
++
++module_init(s3c2410_nand_init);
++
++/*
++ * Clean up routine
++ */
++#ifdef MODULE
++static void __exit s3c2410_cleanup (void)
++{
++ struct nand_chip *this = (struct nand_chip *) &s3c2410_mtd[1];
++
++ /* Unregister partitions */
++ del_mtd_partitions(s3c2410_mtd);
++
++ /* Unregister the device */
++ del_mtd_device (s3c2410_mtd);
++
++ /* Free internal data buffers */
++ kfree (this->data_buf);
++ kfree (this->data_cache);
++
++ /* Free the MTD device structure */
++ kfree (s3c2410_mtd);
++}
++module_exit(s3c2410_cleanup);
++#endif
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("SW.LEE <hitchcar at sec.samsung.com>");
++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on s3c2410");
+diff -urN kernel-source-2.4.27-8/drivers/net/8390-bast.c kernel-source-2.4.27-8-arm-1/drivers/net/8390-bast.c
+--- kernel-source-2.4.27-8/drivers/net/8390-bast.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/8390-bast.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,26 @@
++/* linux/drivers/net/bast-8390.c
++ *
++ * (c) 2003 Simtec Electronics, <linux at simtec.co.uk>
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * Build 8390 core for BAST ASIX driver
++ *
++ * 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:
++ * 27-May-2003 BJD Created file
++ * 20-Aug-2003 BJD Tidied header file
++*/
++
++/* we need to build an version of the 8390 core suitably configured
++ * for the ASIX on the BAST (EB110ITX) board. This has a slightly different
++ * bus configuration, and we improve the access functions as we know
++ * where it is.
++*/
++
++#define COMPILE_BAST
++
++#include "8390-bast.h"
++#include "8390.c"
+diff -urN kernel-source-2.4.27-8/drivers/net/8390-bast.h kernel-source-2.4.27-8-arm-1/drivers/net/8390-bast.h
+--- kernel-source-2.4.27-8/drivers/net/8390-bast.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/8390-bast.h 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,27 @@
++/* linux/drivers/net/8390-bast.h
++ *
++ * Copyright (c) 2003 Simtec Electronics, <linux at simtec.co.uk>
++ * Ben Dooks <ben at simtec.co.uk
++ *
++ * 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:
++ * 27-May-2003 BJD Created file
++ * 20-Aug-2003 BJD Tidied header files
++*/
++
++
++/* re-define some of the routines for the 8390 core so that
++ * we can build it for bast without clashing with a standard ISA
++ * implementation.
++*/
++
++#define alloc_ei_netdev bast_alloc_ei_netdev
++#define ei_open bast_ei_open
++#define ei_close bast_ei_close
++#define ei_tx_timeout bast_ei_tx_timeout
++#define ei_interrupt bast_ei_interrupt
++#define ethdev_init bast_ethdev_init
++#define NS8390_init bast_NS8390_init
+diff -urN kernel-source-2.4.27-8/drivers/net/8390.c kernel-source-2.4.27-8-arm-1/drivers/net/8390.c
+--- kernel-source-2.4.27-8/drivers/net/8390.c 2004-08-08 00:26:04.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/8390.c 2005-02-18 17:48:43.000000000 +0000
+@@ -1110,7 +1110,7 @@
+ {
+ outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+ if (ei_debug > 1 && inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i])
+- printk(KERN_ERR "Hw. address read/write mismap %d\n",i);
++ printk(KERN_ERR __FUNCTION__ ": Hw. address read/write mismap %d (%02x vs %02x) %08x %08x %0x\n",i,inb_p(e8390_base + EN1_PHYS_SHIFT(i)), dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+ }
+
+ outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+diff -urN kernel-source-2.4.27-8/drivers/net/8390.h kernel-source-2.4.27-8-arm-1/drivers/net/8390.h
+--- kernel-source-2.4.27-8/drivers/net/8390.h 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/net/8390.h 2005-02-18 17:48:43.000000000 +0000
+@@ -4,6 +4,10 @@
+ Some of these names and comments originated from the Crynwr
+ packet drivers, which are distributed under the GPL. */
+
++/* bast asix ethernet support, (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++*/
++
+ #ifndef _8390_h
+ #define _8390_h
+
+@@ -95,8 +99,15 @@
+ /* Some generic ethernet register configurations. */
+ #define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
+ #define E8390_RX_IRQ_MASK 0x5
++#ifdef COMPILE_BAST
++#define E8390_RXCONFIG 0x44 /* EN0_RXCR: broadcasts, no multicast,errors, irq active high */
++#define E8390_RXOFF 0x60 /* EN0_RXCR: Accept no packets */
++#else
+ #define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
+ #define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
++#endif
++
++
+ #define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
+ #define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
+
+@@ -132,6 +143,22 @@
+
+ #elif defined(CONFIG_ARM_ETHERH) || defined(CONFIG_ARM_ETHERH_MODULE)
+ #define EI_SHIFT(x) (ei_local->reg_offset[x])
++
++#elif defined(COMPILE_BAST)
++#define EI_SHIFT(x) ((x)<<5)
++
++#if 1
++#undef inb
++#undef inb_p
++#undef outb
++#undef outb_p
++
++#define inb(port) __raw_readb(port)
++#define outb(val,port) __raw_writeb(val, port)
++#define inb_p(port) __raw_readb(port)
++#define outb_p(val,port) __raw_writeb(val, port)
++#endif
++
+ #else
+ #define EI_SHIFT(x) (x)
+ #endif
+diff -urN kernel-source-2.4.27-8/drivers/net/Config.in kernel-source-2.4.27-8-arm-1/drivers/net/Config.in
+--- kernel-source-2.4.27-8/drivers/net/Config.in 2005-01-19 09:57:48.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/net/Config.in 2005-02-18 17:48:43.000000000 +0000
+@@ -27,9 +27,22 @@
+ if [ "$CONFIG_ARM" = "y" ]; then
+ dep_bool ' ARM EBSA110 AM79C961A support' CONFIG_ARM_AM79C961A $CONFIG_ARCH_EBSA110
+ tristate ' Cirrus Logic CS8900A support' CONFIG_ARM_CIRRUS
++ if [ "$CONFIG_ARCH_RISCSTATION" = "y" ]; then
++ tristate ' RiscStation Ether support' CONFIG_ARM_ETHERS
++ fi
+ 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
++ if [ "$CONFIG_ARCH_BAST" = "y" ]; then
++ tristate ' BAST Onboard ASIX (NE2000) Ethernet support' CONFIG_NE2K_BAST
++ tristate ' BAST Onboard DM9000 Ethernet support' CONFIG_DM9000_BAST
++ fi
+ fi
+ if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then
+ tristate ' Altera Ether00 support' CONFIG_ETHER00
+diff -urN kernel-source-2.4.27-8/drivers/net/Makefile kernel-source-2.4.27-8-arm-1/drivers/net/Makefile
+--- kernel-source-2.4.27-8/drivers/net/Makefile 2004-08-08 00:26:04.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/Makefile 2005-02-18 17:48:43.000000000 +0000
+@@ -15,9 +15,9 @@
+ # All of the (potential) objects that export symbols.
+ # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
+
+-export-objs := 8390.o arlan.o aironet4500_core.o aironet4500_card.o \
+- ppp_async.o ppp_generic.o slhc.o pppox.o auto_irq.o \
+- net_init.o mii.o
++export-objs := 8390.o 8390-bast.o arlan.o aironet4500_core.o \
++ aironet4500_card.o ppp_async.o ppp_generic.o slhc.o \
++ pppox.o auto_irq.o net_init.o mii.o
+ list-multi := rcpci.o
+ rcpci-objs := rcpci45.o rclanmtl.o
+
+@@ -80,6 +80,8 @@
+ obj-$(CONFIG_VORTEX) += 3c59x.o mii.o
+ obj-$(CONFIG_TYPHOON) += typhoon.o
+ obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
++obj-$(CONFIG_NE2K_BAST) += ne2k-bast.o 8390-bast.o
++obj-$(CONFIG_DM9000_BAST) += dm9000-bast.o
+ obj-$(CONFIG_PCNET32) += pcnet32.o mii.o
+ obj-$(CONFIG_EEPRO100) += eepro100.o mii.o
+ obj-$(CONFIG_TLAN) += tlan.o
+@@ -244,6 +246,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
+
+ obj-$(CONFIG_IBMVETH) += ibmveth.o
+
+@@ -253,6 +256,12 @@
+ obj-y += ../acorn/net/acorn-net.o
+ endif
+
++ifeq ($(CONFIG_ARCH_RISCSTATION),y)
++mod-subdirs += ../acorn/net
++subdir-y += ../acorn/net
++obj-y += ../acorn/net/acorn-net.o
++endif
++
+ #
+ # HIPPI adapters
+ #
+diff -urN kernel-source-2.4.27-8/drivers/net/Space.c kernel-source-2.4.27-8-arm-1/drivers/net/Space.c
+--- kernel-source-2.4.27-8/drivers/net/Space.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/net/Space.c 2005-02-18 17:48:43.000000000 +0000
+@@ -101,6 +101,8 @@
+ extern int mac8390_probe(struct net_device *dev);
+ extern int mac89x0_probe(struct net_device *dev);
+ extern int mc32_probe(struct net_device *dev);
++extern int dmfe_bast_probe(struct net_device *dev);
++extern int ne_bast_probe(struct net_device *dev);
+
+ /* Detachable devices ("pocket adaptors") */
+ extern int de600_probe(struct net_device *);
+@@ -375,6 +377,19 @@
+ {NULL, 0},
+ };
+
++#ifdef CONFIG_ARM
++#ifdef CONFIG_ETHERS
++extern int ethers_probe(struct net_device *);
++#endif
++
++static struct devprobe arm_probes[] __initdata = {
++#ifdef CONFIG_ETHERS
++ { ethers_probe, 0 },
++#endif
++ { NULL, 0 }
++};
++#endif /* CONFIG_ARM */
++
+ /*
+ * Unified ethernet device probe, segmented per architecture and
+ * per bus interface. This drives the legacy devices only for now.
+@@ -395,6 +410,12 @@
+ * The arch specific probes are 1st so that any on-board ethernet
+ * will be probed before other ISA/EISA/MCA/PCI bus cards.
+ */
++
++#ifdef CONFIG_ARM
++ if (probe_list(dev, arm_probes) == 0)
++ return 0;
++#endif
++
+ if (probe_list(dev, m68k_probes) == 0)
+ return 0;
+ if (probe_list(dev, mips_probes) == 0)
+diff -urN kernel-source-2.4.27-8/drivers/net/am79c961a.c kernel-source-2.4.27-8-arm-1/drivers/net/am79c961a.c
+--- kernel-source-2.4.27-8/drivers/net/am79c961a.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/am79c961a.c 2005-02-18 17:48:43.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,15 +102,15 @@
+ }
+ 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)
++ __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;
+ }
+@@ -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)
++ __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);
+@@ -414,15 +451,6 @@
+ 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);
+ bufaddr = priv->txbuffer[head];
+@@ -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,10 +598,13 @@
+ {
+ 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;
+
++ do {
+ status = read_rreg(dev->base_addr, CSR0);
+- write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA));
++ 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);
+@@ -576,6 +612,9 @@
+ 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 kernel-source-2.4.27-8/drivers/net/am79c961a.h kernel-source-2.4.27-8-arm-1/drivers/net/am79c961a.h
+--- kernel-source-2.4.27-8/drivers/net/am79c961a.h 2000-09-18 23:15:22.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/am79c961a.h 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/net/cirrus.c kernel-source-2.4.27-8-arm-1/drivers/net/cirrus.c
+--- kernel-source-2.4.27-8/drivers/net/cirrus.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/cirrus.c 2005-02-18 17:48:43.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;
+ }
+- /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */
++ }
++ /* Wakeup only for tx events ! */
++ if ((RegContent (status) & (TxUnderrun | Rdy4Tx))) {
+ 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 kernel-source-2.4.27-8/drivers/net/cs89x0.c kernel-source-2.4.27-8-arm-1/drivers/net/cs89x0.c
+--- kernel-source-2.4.27-8/drivers/net/cs89x0.c 2003-08-25 12:44:42.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/cs89x0.c 2005-02-18 17:48:43.000000000 +0000
+@@ -115,6 +115,7 @@
+
+ */
+
++#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+diff -urN kernel-source-2.4.27-8/drivers/net/dm9000-bast.c kernel-source-2.4.27-8-arm-1/drivers/net/dm9000-bast.c
+--- kernel-source-2.4.27-8/drivers/net/dm9000-bast.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/dm9000-bast.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,1187 @@
++/*
++ dm9000.c: Version 0.11 06/20/2001
++
++ A Davicom DM9000 ISA NIC fast Ethernet driver for Linux.
++ Copyright (C) 1997 Sten Wang
++ Copyright (C) 2003 Simtec Electronics <ben at simtec.co.uk>
++
++ 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.
++
++ Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang at davicom.com.tw
++ Date: 10/28,1998
++
++ BAST / Linux 2.4 modifications by Ben Dooks, (C) 2003 Simtec Electronics
++
++ (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
++
++
++V0.12 25 May 2004 Ben Dooks
++ - removed build warnings
++ - removed default mac address
++ - probe for both interfaces on vr1000
++
++V0.11 06/20/2001 REG_0A bit3=1, default enable BP with DA match
++ 06/22/2001 Support DM9801 progrmming
++ E3: R25 = ((R24 + NF) & 0x00ff) | 0xf000
++ E4: R25 = ((R24 + NF) & 0x00ff) | 0xc200
++ R17 = (R17 & 0xfff0) | NF + 3
++ E5: R25 = ((R24 + NF - 3) & 0x00ff) | 0xc200
++ R17 = (R17 & 0xfff0) | NF
++*/
++#if defined(MODVERSIONS)
++#include <linux/modversions.h>
++#endif
++
++#include <linux/module.h>
++#include <linux/ioport.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/version.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <asm/dma.h>
++#include <asm/arch/map.h>
++
++#include <asm/mach-types.h>
++
++/* Board/System/Debug information/definition ---------------- */
++
++#define DM9000_ID 0x90000A46
++
++#define DM9000_REG00 0x00
++#define DM9000_REG05 0x30 /* SKIP_CRC/SKIP_LONG */
++#define DM9000_REG08 0x27
++#define DM9000_REG09 0x38
++#define DM9000_REG0A 0x08
++#define DM9000_REGFF 0x83 /* IMR */
++
++#define DM9000_PHY 0x40 /* PHY address 0x01 */
++#define DM9000_PKT_MAX 1536 /* Received packet max size */
++#define DM9000_PKT_RDY 0x01 /* Packet ready to receive */
++#define DM9000_MIN_IO 0x300
++#define DM9000_MAX_IO 0x370
++#define DM9000_INT_MII 0x00
++#define DM9000_EXT_MII 0x80
++
++#define DM9000_VID_L 0x28
++#define DM9000_VID_H 0x29
++#define DM9000_PID_L 0x2A
++#define DM9000_PID_H 0x2B
++
++#define DM9801_NOISE_FLOOR 0x08
++#define DM9802_NOISE_FLOOR 0x05
++
++#define DMFE_SUCC 0
++#define MAX_PACKET_SIZE 1514
++#define DMFE_MAX_MULTICAST 14
++
++#define DMFE_TIMER_WUT jiffies+(HZ*2) /* timer wakeup time : 2 second */
++#define DMFE_TX_TIMEOUT (HZ*2) /* tx packet time-out time 1.5 s" */
++
++#if defined(DM9000_DEBUG)
++#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk(KERN_ERR "dmfe: %s %x\n", msg, vaule)
++#else
++#define DMFE_DBUG(dbug_now, msg, vaule)
++#endif
++
++#if LINUX_VERSION_CODE < 0x20300
++#define DEVICE device
++#else
++#define DEVICE net_device
++#endif
++
++enum DM9000_PHY_mode {
++ DM9000_10MHD = 0, DM9000_100MHD = 1, DM9000_10MFD = 4,
++ DM9000_100MFD = 5, DM9000_AUTO = 8, DM9000_1M_HPNA =0x10 };
++
++enum DM9000_NIC_TYPE {
++ FASTETHER_NIC = 0, HOMERUN_NIC = 1, LONGRUN_NIC = 2 };
++
++#if 1
++#define enet_statistics net_device_stats
++#endif
++
++/* Structure/enum declaration ------------------------------- */
++typedef struct board_info {
++ struct DEVICE *next_dev; /* next device */
++
++ u32 runt_length_counter; /* counter: RX length < 64byte */
++ u32 long_length_counter; /* counter: RX length > 1514byte */
++ u32 reset_counter; /* counter: RESET */
++ u32 reset_tx_timeout; /* RESET caused by TX Timeout */
++ u32 reset_rx_status; /* RESET caused by RX Statsus wrong */
++
++ void (*inblk)(unsigned long ioaddr, unsigned short *dp, u32 count);
++ void (*outblk)(unsigned long ioaddr, unsigned short *dp, u32 count);
++
++ u32 ioaddr; /* Register I/O base address */
++ u32 io_data; /* Data I/O address */
++ u16 irq; /* IRQ */
++
++ u16 tx_pkt_cnt;
++ u16 queue_pkt_len;
++ u16 queue_start_addr;
++ u16 dbug_cnt;
++ u8 reg0, reg5, reg8, reg9, rega; /* registers saved */
++ u8 op_mode; /* PHY operation mode */
++ u8 io_mode; /* 0:word, 2:byte */
++ u8 phy_addr;
++ u8 link_failed; /* Ever link failed */
++ u8 device_wait_reset; /* device state */
++ u8 nic_type; /* NIC type */
++ struct timer_list timer;
++ struct enet_statistics stats; /* statistic counter */
++ unsigned char srom[128];
++} board_info_t;
++
++/* Global variable declaration ----------------------------- */
++static int dmfe_debug = 0;
++static struct DEVICE * dmfe_root_dev = NULL; /* First device */
++
++/* For module input parameter */
++static int debug = 0;
++static int media_mode = DM9000_AUTO;
++static u8 reg5 = DM9000_REG05;
++static u8 reg8 = DM9000_REG08;
++static u8 reg9 = DM9000_REG09;
++static u8 rega = DM9000_REG0A;
++static u8 nfloor = 0;
++
++#if 0
++static int mode = DM9000_AUTO;
++#endif
++
++unsigned long CrcTable[256] = {
++ 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
++ 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
++ 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
++ 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
++ 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
++ 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
++ 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
++ 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
++ 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
++ 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
++ 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
++ 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
++ 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
++ 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
++ 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
++ 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
++ 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
++ 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
++ 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
++ 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
++ 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
++ 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
++ 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
++ 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
++ 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
++ 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
++ 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
++ 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
++ 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
++ 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
++ 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
++ 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
++ 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
++ 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
++ 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
++ 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
++ 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
++ 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
++ 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
++ 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
++ 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
++ 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
++ 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
++ 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
++ 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
++ 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
++ 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
++ 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
++ 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
++ 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
++ 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
++ 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
++ 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
++ 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
++ 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
++ 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
++ 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
++ 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
++ 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
++ 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
++ 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
++ 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
++ 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
++ 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
++};
++
++/* function declaration ------------------------------------- */
++int dmfe_probe(struct DEVICE *);
++static int dmfe_open(struct DEVICE *);
++static int dmfe_start_xmit(struct sk_buff *, struct DEVICE *);
++static int dmfe_stop(struct DEVICE *);
++static struct enet_statistics * dmfe_get_stats(struct DEVICE *);
++static int dmfe_do_ioctl(struct DEVICE *, struct ifreq *, int);
++static void dmfe_interrupt(int , void *, struct pt_regs *);
++static void dmfe_timer(unsigned long);
++static void dmfe_init_dm9000(struct DEVICE *);
++static unsigned long cal_CRC(unsigned char *, unsigned int, u8);
++static u8 ior(board_info_t *, int);
++static void iow(board_info_t *, int, u8);
++static u16 phy_read(board_info_t *, int);
++static void phy_write(board_info_t *, int, u16);
++static u16 read_srom_word(board_info_t *, int);
++static void dmfe_packet_receive(struct DEVICE *, board_info_t *);
++static void dm9000_hash_table(struct DEVICE *);
++
++/* DM9000 network baord routine ---------------------------- */
++
++#include <asm/arch/irqs.h>
++#include <asm/io.h>
++#include <asm/arch/map.h>
++
++
++/*
++ Read a byte from I/O port
++*/
++static inline u8 ior(board_info_t *db, int reg)
++{
++ __raw_writeb(reg, db->ioaddr);
++ return __raw_readb(db->io_data);
++}
++
++/*
++ Write a byte to I/O port
++*/
++static inline void iow(board_info_t *db, int reg, u8 value)
++{
++ __raw_writeb(reg, db->ioaddr);
++ __raw_writeb(value, db->io_data);
++}
++
++/* get/put blocks to chip */
++
++/* these routines seem to allow an ftp-get at about 1.95MB/sec at 226MHz */
++
++#if 0
++static void dm_bast_outblk(unsigned long addr, unsigned short *dp, u32 len)
++{
++ __raw_writesw(addr, dp, (len+1)/2);
++}
++
++static void dm_bast_inblk(unsigned long addr, unsigned short *dp, u32 len)
++{
++ __raw_readsw(addr, dp, (len+1)/2);
++}
++#endif
++
++extern void __raw_m8_readsl(void *, void *, int);
++extern void __raw_m8_writesl(void *, void *, int);
++
++#define is_aligned(ptr) ((((unsigned long)(ptr)) & 3) == 0)
++
++static void dm_bast_outblk_f(unsigned long addr, unsigned short *dp, u32 len)
++{
++ if (len & 1) /* round up packet length to half-word*/
++ len++;
++
++ if (!is_aligned(dp)) {
++ __raw_writew(*dp, addr);
++ dp++;
++ len -= 2;
++ }
++
++ __raw_m8_writesl((void *)addr, dp, len >> 2);
++
++ /* if we have 2 bytes left, then move them */
++ if ((len & 3) > 1) {
++ dp += (len / 2);
++ __raw_writew(dp[-1], addr);
++ }
++}
++
++static void dm_bast_inblk_f(unsigned long addr, unsigned short *dp, u32 len)
++{
++ if (len & 1)
++ len++;
++
++ if (!is_aligned(dp)) {
++ *dp++ = __raw_readw(addr);
++ len -= 2;
++ }
++
++ __raw_m8_readsl((void *)addr, dp, len >> 2);
++
++ if (len & 2) {
++ dp += len/2;
++ dp[-1] = __raw_readw(addr);
++ }
++}
++
++
++
++
++
++/*
++ Search DM9000 board, allocate space and register it
++*/
++int dmfe_bast_probe(struct DEVICE *dev, unsigned int offset)
++{
++ struct board_info *db; /* Point a board information structure */
++ u32 id_val;
++ u32 addr, data;
++ u16 i, dm9000_count = 0;
++ u8 irqline;
++
++ DMFE_DBUG(0, "dmfe_probe()",0);
++
++ /* Search All DM9000 NIC */
++ do {
++ addr = BAST_DM9000_CS + offset + BAST_VA_DM9000;
++ data = BAST_DM9000_CS + offset + BAST_VA_DM9000 + 0x40;
++
++ __raw_writeb(DM9000_VID_L, addr);
++ id_val = __raw_readb(data);
++ __raw_writeb(DM9000_VID_H, addr);
++ id_val |= __raw_readb(data) << 8;
++ __raw_writeb(DM9000_PID_L, addr);
++ id_val |= __raw_readb(data) << 16;
++ __raw_writeb(DM9000_PID_H, addr);
++ id_val |= __raw_readb(data) << 24;
++
++ if (id_val == DM9000_ID) {
++ dm9000_count++;
++
++ /* Init network device */
++ dev = init_etherdev(dev, 0);
++
++ /* Allocated board information structure */
++ if (offset == 0)
++ irqline = IRQ_DM9000;
++ else
++ irqline = IRQ_ISA;
++
++ db = (void *)(kmalloc(sizeof(*db), GFP_KERNEL|GFP_DMA));
++ memset(db, 0, sizeof(*db));
++ dev->priv = db; /* link device and board info */
++ db->next_dev = dmfe_root_dev;
++ dmfe_root_dev = dev;
++ db->ioaddr = addr;
++ db->io_data = data;
++
++ db->outblk = dm_bast_outblk_f;
++ db->inblk = dm_bast_inblk_f;
++
++ /* driver system function */
++ dev->base_addr = addr;
++ dev->irq = irqline;
++ dev->open = &dmfe_open;
++ dev->hard_start_xmit = &dmfe_start_xmit;
++ dev->stop = &dmfe_stop;
++ dev->get_stats = &dmfe_get_stats;
++ dev->set_multicast_list = &dm9000_hash_table;
++ dev->do_ioctl = &dmfe_do_ioctl;
++
++ /* Read SROM content */
++ for (i=0; i<64; i++)
++ ((u16 *)db->srom)[i] = read_srom_word(db, i);
++
++ /* Set Node Address */
++ for (i=0; i<6; i++)
++ dev->dev_addr[i] = db->srom[i];
++
++ /* Request IO from system */
++ request_region(addr, 0x40, dev->name);
++ request_region(data, 0x40, dev->name);
++
++ /* Announce found device */
++ printk("%s: bast-dm9000 found at %#lx, IRQ %d, ",
++ dev->name, (unsigned long)addr, (int)dev->irq);
++
++ for(i = 0; i < 6; i++) {
++ printk("%2.2X%s", dev->dev_addr[i], i == 5 ? ".\n": ":");
++ }
++
++ dev = 0; /* NULL device */
++
++ } else {
++ printk("dm9000: %08x: read ID %08x\n", addr, id_val);
++ }
++ } while(0);
++
++ return dm9000_count ? 0:-ENODEV;
++}
++
++/*
++ Open the interface.
++ The interface is opened whenever "ifconfig" actives it.
++*/
++static int dmfe_open(struct DEVICE *dev)
++{
++ board_info_t * db = (board_info_t *)dev->priv;
++ int i;
++
++ DMFE_DBUG(0, "dmfe_open", 0);
++
++ if (request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev))
++ return -EAGAIN;
++
++ /* Initilize DM910X board */
++ dmfe_init_dm9000(dev);
++
++ /* Init driver variable */
++ db->dbug_cnt = 0;
++ db->runt_length_counter = 0;
++ db->long_length_counter = 0;
++ db->reset_counter = 0;
++
++ /* Active System Interface */
++#if 0
++ dev->tbusy = 0; /* Can transmit packet */
++ dev->start = 1; /* interface ready */
++#endif
++ MOD_INC_USE_COUNT;
++
++ /* write mac address */
++
++ for (i = 0; i < 6; i++)
++ iow(db, 0x10+i, dev->dev_addr[i]);
++
++ /* set and active a timer process */
++ init_timer(&db->timer);
++ db->timer.expires = DMFE_TIMER_WUT * 2;
++ db->timer.data = (unsigned long)dev;
++ db->timer.function = &dmfe_timer;
++ add_timer(&db->timer);
++
++ return 0;
++}
++
++/* Set PHY operationg mode
++*/
++static void set_PHY_mode(board_info_t *db)
++{
++ u16 phy_reg4 = 0x01e1, phy_reg0=0x1000;
++
++ if ( !(db->op_mode & DM9000_AUTO) ) {
++ switch(db->op_mode) {
++ case DM9000_10MHD: phy_reg4 = 0x21; phy_reg0 = 0x0000; break;
++ case DM9000_10MFD: phy_reg4 = 0x41; phy_reg0 = 0x0100; break;
++ case DM9000_100MHD: phy_reg4 = 0x81; phy_reg0 = 0x2000; break;
++ case DM9000_100MFD: phy_reg4 = 0x101; phy_reg0 = 0x2100; break;
++ }
++ phy_write(db, 4, phy_reg4); /* Set PHY media mode */
++ phy_write(db, 0, phy_reg0); /* Tmp */
++ }
++
++ iow(db, 0x1e, 0x01); /* Let GPIO0 output */
++ iow(db, 0x1f, 0x00); /* Enable PHY */
++}
++
++/*
++ Init HomeRun DM9801
++*/
++static void program_dm9801(board_info_t *db, u16 HPNA_rev)
++{
++ __u16 reg16, reg17, reg24, reg25;
++
++ if ( !nfloor ) nfloor = DM9801_NOISE_FLOOR;
++
++ reg16 = phy_read(db, 16);
++ reg17 = phy_read(db, 17);
++ reg24 = phy_read(db, 24);
++ reg25 = phy_read(db, 25);
++
++ switch(HPNA_rev) {
++ case 0xb900: /* DM9801 E3 */
++ reg16 |= 0x1000;
++ reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xf000;
++ break;
++ case 0xb901: /* DM9801 E4 */
++ reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xc200;
++ reg17 = (reg17 & 0xfff0) + nfloor + 3;
++ break;
++ case 0xb902: /* DM9801 E5 */
++ case 0xb903: /* DM9801 E6 */
++ default:
++ reg16 |= 0x1000;
++ reg25 = ( (reg24 + nfloor - 3) & 0x00ff) | 0xc200;
++ reg17 = (reg17 & 0xfff0) + nfloor;
++ break;
++ }
++
++ phy_write(db, 16, reg16);
++ phy_write(db, 17, reg17);
++ phy_write(db, 25, reg25);
++}
++
++/*
++ Init LongRun DM9802
++*/
++static void program_dm9802(board_info_t *db)
++{
++ __u16 reg25;
++
++ if ( !nfloor ) nfloor = DM9802_NOISE_FLOOR;
++
++ reg25 = phy_read(db, 25);
++ reg25 = (reg25 & 0xff00) + nfloor;
++ phy_write(db, 25, reg25);
++}
++
++/* Identify NIC type
++*/
++static void identify_nic(board_info_t *db)
++{
++ u16 phy_reg3;
++
++ iow(db, 0, DM9000_EXT_MII);
++ phy_reg3 = phy_read(db, 3);
++ switch(phy_reg3 & 0xfff0) {
++ case 0xb900:
++ if (phy_read(db, 31) == 0x4404) {
++ db->nic_type = HOMERUN_NIC;
++ program_dm9801(db, phy_reg3);
++ } else {
++ db->nic_type = LONGRUN_NIC;
++ program_dm9802(db);
++ }
++ break;
++ default: db->nic_type = FASTETHER_NIC; break;
++ }
++ iow(db, 0, DM9000_INT_MII);
++}
++
++/* Initilize dm9000 board
++*/
++static void dmfe_init_dm9000(struct DEVICE *dev)
++{
++ board_info_t *db = (board_info_t *)dev->priv;
++
++ DMFE_DBUG(0, "dmfe_init_dm9000()", 0);
++
++ /* RESET device */
++ iow(db, 0, 1);
++ udelay(100); /* delay 100us */
++
++ /* I/O mode */
++ db->io_mode = ior(db, 0xfe) >> 6; /* ISR bit7:6 keeps I/O mode */
++
++ /* NIC Type: FASTETHER, HOMERUN, LONGRUN */
++ identify_nic(db);
++
++ /* Set PHY */
++ db->op_mode = media_mode;
++ set_PHY_mode(db);
++
++ /* Init needed register value */
++ db->reg0 = DM9000_REG00;
++ if ( (db->nic_type != FASTETHER_NIC) && (db->op_mode & DM9000_1M_HPNA) )
++ db->reg0 |= DM9000_EXT_MII;
++
++ /* User passed argument */
++ db->reg5 = reg5;
++ db->reg8 = reg8;
++ db->reg9 = reg9;
++ db->rega = rega;
++
++ /* Program operating register */
++ iow(db, 0x00, db->reg0);
++ iow(db, 0x02, 0); /* TX Polling clear */
++ iow(db, 0x08, 0x3f); /* Less 3Kb, 200us */
++ iow(db, 0x09, db->reg9);/* Flow Control : High/Low Water */
++ iow(db, 0x0a, db->rega);/* Flow Control */
++ iow(db, 0x2f, 0); /* Special Mode */
++ iow(db, 0x01, 0x2c); /* clear TX status */
++ iow(db, 0xfe, 0x0f); /* Clear interrupt status */
++
++ /* Set address filter table */
++ dm9000_hash_table(dev);
++
++ /* Activate DM9000 */
++ iow(db, 0x05, db->reg5 | 1); /* RX enable */
++ iow(db, 0xff, DM9000_REGFF); /* Enable TX/RX interrupt mask */
++
++ /* Init Driver variable */
++ db->link_failed = 1;
++ db->tx_pkt_cnt = 0;
++ db->queue_pkt_len = 0;
++ dev->trans_start = 0;
++}
++
++/*
++ Hardware start transmission.
++ Send a packet to media from the upper layer.
++*/
++static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev)
++{
++ board_info_t *db = (board_info_t *)dev->priv;
++
++ DMFE_DBUG(0, "dmfe_start_xmit", 0);
++
++ /* Resource flag check */
++#if 0
++ if ( test_and_set_bit(0, (void *)&dev->tbusy) || (db->tx_pkt_cnt > 1) )
++ return 1;
++#else
++ if (db->tx_pkt_cnt > 1)
++ return 1;
++#endif
++
++ /* Disable all interrupt */
++ iow(db, 0xff, 0x80);
++
++ /* Move data to DM9000 TX RAM */
++ __raw_writeb(0xf8, db->ioaddr);
++
++ (db->outblk)(db->io_data, (unsigned short *)skb->data, skb->len);
++
++ /* TX control: First packet immediately send, second packet queue */
++ if (db->tx_pkt_cnt == 0) {
++ /* First Packet */
++ db->tx_pkt_cnt++;
++
++ /* Set TX length to DM9000 */
++ iow(db, 0xfc, skb->len & 0xff);
++ iow(db, 0xfd, (skb->len >> 8) & 0xff);
++
++ /* Issue TX polling command */
++ iow(db, 0x2, 0x1); /* Cleared after TX complete */
++
++ dev->trans_start = jiffies; /* saved the time stamp */
++ } else {
++ /* Second packet */
++ db->tx_pkt_cnt++;
++ db->queue_pkt_len = skb->len;
++ }
++
++ /* free this SKB */
++ dev_kfree_skb(skb);
++
++#if 0
++ /* Re-enable resource check */
++ if (db->tx_pkt_cnt == 1)
++ clear_bit(0, (void*)&dev->tbusy);
++#else
++ /* we don't need to do anything with this for 2.4 */
++#endif
++
++ /* Re-enable interrupt mask */
++ iow(db, 0xff, 0x83);
++
++ return 0;
++}
++
++/*
++ Stop the interface.
++ The interface is stopped when it is brought.
++*/
++static int dmfe_stop(struct DEVICE *dev)
++{
++ board_info_t *db = (board_info_t *)dev->priv;
++
++ DMFE_DBUG(0, "dmfe_stop", 0);
++
++ /* deleted timer */
++ del_timer(&db->timer);
++
++ /* disable system */
++#if 0
++ dev->start = 0; /* interface disable */
++ dev->tbusy = 1; /* can't transmit */
++#else
++ netif_stop_queue(dev);
++#endif
++
++ /* free interrupt */
++ free_irq(dev->irq, dev);
++
++ /* RESET devie */
++ phy_write(db, 0x00, 0x8000); /* PHY RESET */
++ iow(db, 0x1f, 0x01); /* Power-Down PHY */
++ iow(db, 0xff, 0x80); /* Disable all interrupt */
++ iow(db, 0x05, 0x00); /* Disable RX */
++
++ MOD_DEC_USE_COUNT;
++
++ /* Dump Statistic counter */
++#if 1
++ printk("\nRX FIFO OVERFLOW %lx\n", db->stats.rx_fifo_errors++);
++ printk("RX CRC %lx\n", db->stats.rx_crc_errors++);
++ printk("RX LEN Err %lx\n", db->stats.rx_length_errors++);
++ printk("RX LEN<64byte %x\n", db->runt_length_counter);
++ printk("RX LEN>1514byte %x\n", db->long_length_counter);
++ printk("RESET %x\n", db->reset_counter);
++ printk("RESET: TX Timeout %x\n", db->reset_tx_timeout);
++ printk("RESET: RX Status Wrong %x\n", db->reset_rx_status);
++#endif
++
++ return 0;
++}
++
++/*
++ DM9102 insterrupt handler
++ receive the packet to upper layer, free the transmitted packet
++*/
++static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++ struct DEVICE *dev = dev_id;
++
++ board_info_t *db;
++ int int_status, tx_status;
++ u8 reg_save;
++
++ DMFE_DBUG(0, "dmfe_interrupt()", 0);
++
++ if (!dev) {
++ DMFE_DBUG(1, "dmfe_interrupt() without DEVICE arg", 0);
++ return;
++ }
++
++#if 0
++ if (dev->interrupt) {
++ DMFE_DBUG(1, "dmfe_interrupt() re-entry ", 0);
++ return;
++ }
++
++ /* A real interrupt coming */
++ dev->interrupt=1; /* Lock interrupt */
++#endif
++
++ db = (board_info_t *)dev->priv;
++
++ /* Save previous register address */
++ reg_save = __raw_readb(db->ioaddr);
++
++ /* Disable all interrupt */
++ iow(db, 0xff, 0x80);
++
++ /* Got DM9000 interrupt status */
++ int_status = ior(db, 0xfe); /* Got ISR */
++ iow(db, 0xfe, int_status); /* Clear ISR status */
++ //printk("I%x ", int_status);
++
++ /* Received the coming packet */
++ if (int_status & 1)
++ dmfe_packet_receive(dev, db);
++
++ /* Trnasmit Interrupt check */
++ if (int_status & 2) {
++ tx_status = ior(db, 0x01); /* Got TX status */
++ if (tx_status & 0xc) {
++ /* One packet sent complete */
++ db->tx_pkt_cnt--;
++ dev->trans_start = 0;
++ db->stats.tx_packets++;
++
++ /* Queue packet check & send */
++ if (db->tx_pkt_cnt > 0) {
++ iow(db, 0xfc, db->queue_pkt_len & 0xff);
++ iow(db, 0xfd, (db->queue_pkt_len >> 8) & 0xff);
++ iow(db, 0x2, 0x1);
++ dev->trans_start = jiffies;
++ }
++
++#if 0
++ dev->tbusy = 0; /* Active upper layer, send again */
++ mark_bh(NET_BH);
++#else
++ netif_wake_queue(dev);
++#endif
++
++ }
++ }
++
++ /* Re-enable interrupt mask */
++ iow(db, 0xff, 0x83);
++
++ /* Restore previous register address */
++ __raw_writeb(reg_save, db->ioaddr);
++
++#if 0
++ dev->interrupt=0; /* release interrupt lock */
++#endif
++}
++
++/*
++ Get statistics from driver.
++*/
++static struct enet_statistics * dmfe_get_stats(struct DEVICE *dev)
++{
++ board_info_t *db = (board_info_t *)dev->priv;
++
++ DMFE_DBUG(0, "dmfe_get_stats", 0);
++ return &db->stats;
++}
++
++/*
++ Process the upper socket ioctl command
++*/
++static int dmfe_do_ioctl(struct DEVICE *dev, struct ifreq *ifr, int cmd)
++{
++ DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
++ return 0;
++}
++
++/*
++ A periodic timer routine
++ Dynamic media sense, allocated Rx buffer...
++*/
++static void dmfe_timer(unsigned long data)
++{
++ struct DEVICE *dev = (struct DEVICE *)data;
++ board_info_t *db = (board_info_t *)dev->priv;
++ u8 reg_save, tmp_reg;
++
++ DMFE_DBUG(0, "dmfe_timer()", 0);
++
++ /* Save previous register address */
++ reg_save = __raw_readb(db->ioaddr);
++
++ /* TX timeout check */
++ if (dev->trans_start && ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) {
++ db->device_wait_reset = 1;
++ db->reset_tx_timeout++;
++ }
++
++ /* DM9000 dynamic RESET check and do */
++ if (db->device_wait_reset) {
++#if 0
++ dev->tbusy = 1;
++#else
++ netif_stop_queue(dev);
++#endif
++ db->reset_counter++;
++ db->device_wait_reset = 0;
++ dev->trans_start = 0;
++ dmfe_init_dm9000(dev);
++
++#if 0
++ dev->tbusy = 0; /* Active upper layer, send again */
++ mark_bh(NET_BH);
++#else
++ netif_start_queue(dev);
++#endif
++ }
++
++ /* Auto Sense Media mode policy:
++ FastEthernet NIC: don't need to do anything.
++ Media Force mode: don't need to do anything.
++ HomeRun/LongRun NIC and AUTO_Mode:
++ INT_MII not link, select EXT_MII
++ EXT_MII not link, select INT_MII
++ */
++ if ( (db->nic_type != FASTETHER_NIC) && (db->op_mode & DM9000_AUTO) ) {
++ tmp_reg = ior(db, 0x01); /* Got link status */
++ if ( !(tmp_reg & 0x40) ) { /* not link */
++ db->reg0 ^= 0x80;
++ iow(db, 0x00, db->reg0);
++ }
++ }
++
++ /* Restore previous register address */
++ __raw_writeb(reg_save, db->ioaddr);
++
++ /* Set timer again */
++ db->timer.expires = DMFE_TIMER_WUT;
++ add_timer(&db->timer);
++}
++
++/*
++ Received a packet and pass to upper layer
++*/
++static void dmfe_packet_receive(struct DEVICE *dev, board_info_t *db)
++{
++ struct sk_buff *skb;
++ u8 rxbyte;
++ u8 *rdptr;
++ u16 i, RxStatus, RxLen, GoodPacket, tmplen;
++
++ /* Check packet ready or not */
++ do {
++ rxbyte = ior(db, 0xf0); /* Dummy read */
++ rxbyte = ior(db, 0xf0); /* Got most updated data */
++
++ /* Status check: this byte must be 0 or 1 */
++ if (rxbyte > DM9000_PKT_RDY) {
++ iow(db, 0x05, 0x00); /* Stop Device */
++ iow(db, 0xfe, 0x80); /* Stop INT request */
++ db->device_wait_reset = 1;
++ db->reset_rx_status++;
++ }
++
++ /* packet ready to receive check */
++ if (rxbyte == DM9000_PKT_RDY) {
++ /* A packet ready now & Get status/length */
++ GoodPacket = 1;
++ if (db->io_mode == 2) {
++ /* Byte mode */
++ RxStatus = ior(db, 0xf2) + (ior(db, 0xf2) << 8);
++ RxLen = ior(db, 0xf2) + (ior(db, 0xf2) << 8);
++ } else {
++ /* Word mode */
++ __raw_writeb(0xf2, db->ioaddr);
++ RxStatus = __raw_readw(db->io_data);
++ RxLen = __raw_readw(db->io_data);
++ }
++
++ /* Packet Status check */
++ if (RxLen < 0x40) {
++ GoodPacket = 0;
++ db->runt_length_counter++;
++ }
++ if (RxLen > DM9000_PKT_MAX) {
++ printk("<DM9000> RST: RX Len:%x\n", RxLen);
++ db->device_wait_reset = 1;
++ db->long_length_counter++;
++ }
++ if (RxStatus & 0xbf00) {
++ GoodPacket = 0;
++ if (RxStatus & 0x100) db->stats.rx_fifo_errors++;
++ if (RxStatus & 0x200) db->stats.rx_crc_errors++;
++ if (RxStatus & 0x8000) db->stats.rx_length_errors++;
++ }
++
++ /* Move data from DM9000 */
++ if (!db->device_wait_reset) {
++ if ( GoodPacket && ((skb = dev_alloc_skb(RxLen + 4)) != NULL ) ) {
++ skb->dev = dev;
++ skb_reserve(skb, 2);
++ rdptr = (u8 *) skb_put(skb, RxLen - 4);
++
++ /* Read received packet from RX SARM */
++ (db->inblk)(db->io_data, (unsigned short *)rdptr, RxLen);
++
++ /* Pass to upper layer */
++ skb->protocol = eth_type_trans(skb,dev);
++ netif_rx(skb);
++ db->stats.rx_packets++;
++ } else {
++ /* Without buffer or error packet */
++ if (db->io_mode == 2) {
++ /* Byte mode */
++ for (i = 0; i < RxLen; i++)
++ __raw_readb(db->io_data);
++ } else {
++ /* Word mode */
++ tmplen = (RxLen + 1) / 2;
++ for (i = 0; i < tmplen; i++)
++ __raw_readw(db->io_data);
++ }
++ }
++ }
++ }
++ }while( rxbyte && !db->device_wait_reset);
++}
++
++/*
++ Read a word data from SROM
++*/
++static u16 read_srom_word(board_info_t *db, int offset)
++{
++ iow(db, 0xc, offset);
++ iow(db, 0xb, 0x4);
++ udelay(200);
++ iow(db, 0xb, 0x0);
++ return (ior(db, 0xd) + (ior(db, 0xe) << 8) );
++}
++
++/*
++ Set DM9000 multicast address
++*/
++static void dm9000_hash_table(struct DEVICE *dev)
++{
++ board_info_t *db = (board_info_t *)dev->priv;
++ struct dev_mc_list *mcptr = dev->mc_list;
++ int mc_cnt = dev->mc_count;
++ u32 hash_val;
++ u16 i, oft, hash_table[4];
++
++ DMFE_DBUG(0, "dm9000_hash_table()", 0);
++
++ /* Set Node address */
++ for (i = 0, oft = 0x10; i < 6; i++, oft++)
++ iow(db, oft, dev->dev_addr[i]);
++
++ /* Clear Hash Table */
++ for (i = 0; i < 4; i++)
++ hash_table[i] = 0x0;
++
++ /* broadcast address */
++ hash_table[3] = 0x8000;
++
++ /* the multicast address in Hash Table : 64 bits */
++ for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
++ hash_val = cal_CRC((char *)mcptr->dmi_addr, 6, 0) & 0x3f;
++ hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
++ }
++
++ /* Write the hash table to MAC MD table */
++ for (i = 0, oft = 0x16; i < 4; i++) {
++ iow(db, oft++, hash_table[i] & 0xff);
++ iow(db, oft++, (hash_table[i] >> 8) & 0xff);
++ }
++}
++
++/*
++ Calculate the CRC valude of the Rx packet
++ flag = 1 : return the reverse CRC (for the received packet CRC)
++ 0 : return the normal CRC (for Hash Table index)
++*/
++static unsigned long cal_CRC(unsigned char * Data, unsigned int Len, u8 flag)
++{
++ unsigned long Crc = 0xffffffff;
++
++ while (Len--) {
++ Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
++ }
++
++ if (flag) return ~Crc;
++ else return Crc;
++}
++
++/*
++ Read a word from phyxcer
++*/
++static u16 phy_read(board_info_t *db, int reg)
++{
++ /* Fill the phyxcer register into REG_0C */
++ iow(db, 0xc, DM9000_PHY | reg);
++
++ iow(db, 0xb, 0xc); /* Issue phyxcer read command */
++ udelay(100); /* Wait read complete */
++ iow(db, 0xb, 0x0); /* Clear phyxcer read command */
++
++ /* The read data keeps on REG_0D & REG_0E */
++ return ( ior(db, 0xe) << 8 ) | ior(db, 0xd);
++}
++
++/*
++ Write a word to phyxcer
++*/
++static void phy_write(board_info_t *db, int reg, u16 value)
++{
++ /* Fill the phyxcer register into REG_0C */
++ iow(db, 0xc, DM9000_PHY | reg);
++
++ /* Fill the written data into REG_0D & REG_0E */
++ iow(db, 0xd, (value & 0xff));
++ iow(db, 0xe, ( (value >> 8) & 0xff));
++
++ iow(db, 0xb, 0xa); /* Issue phyxcer write command */
++ udelay(500); /* Wait write complete */
++ iow(db, 0xb, 0x0); /* Clear phyxcer write command */
++}
++
++#ifdef MODULE
++
++ MODULE_AUTHOR("Sten Wang, sten_wang at davicom.com.tw");
++ MODULE_DESCRIPTION("BAST: Davicom DM9000 Fast Ethernet Driver");
++ MODULE_PARM(debug, "i");
++ MODULE_PARM(mode, "i");
++ MODULE_PARM(reg5, "i");
++ MODULE_PARM(reg9, "i");
++ MODULE_PARM(rega, "i");
++ MODULE_PARM(nfloor, "i");
++
++/* Description:
++ when user used insmod to add module, system invoked init_module()
++ to initilize and register.
++*/
++int init_module(void)
++{
++ DMFE_DBUG(0, "init_module() ", debug);
++
++ if (debug) dmfe_debug = debug; /* set debug flag */
++
++ if (dmfe_debug)
++ printk("dm9000: debug configured on\n");
++
++
++ switch(mode) {
++ case DM9000_10MHD:
++ case DM9000_100MHD:
++ case DM9000_10MFD:
++ case DM9000_100MFD:
++ case DM9000_1M_HPNA:
++ media_mode = mode;
++ break;
++ default:
++ media_mode = DM9000_AUTO;
++ }
++
++ nfloor = (nfloor > 15) ? 0:nfloor;
++
++ return dmfe_bast_probe(0); /* search board and register */
++}
++
++
++
++/* Description:
++ when user used rmmod to delete module, system invoked clean_module()
++ to un-register DEVICE.
++*/
++void cleanup_module(void)
++{
++ struct DEVICE *next_dev;
++ board_info_t * db;
++
++ DMFE_DBUG(0, "clean_module()", 0);
++
++ while (dmfe_root_dev) {
++ next_dev = ((board_info_t *)dmfe_root_dev->priv)->next_dev;
++ unregister_netdev(dmfe_root_dev);
++ db = (board_info_t *)dmfe_root_dev->priv;
++ release_region(dmfe_root_dev->base_addr, 2);
++ kfree(db); /* free board information */
++ kfree(dmfe_root_dev); /* free device structure */
++ dmfe_root_dev = next_dev;
++ }
++
++ DMFE_DBUG(0, "clean_module() exit", 0);
++}
++
++#else
++
++static int __init
++dmfe_bast_init(void)
++{
++ printk("Simtec DM9000 Ethernet Driver, (c) 2003 Simtec Electronics\n");
++
++ if (debug)
++ printk("dm9000: debug is set, value %d\n", dmfe_debug);
++
++ dmfe_bast_probe(NULL, 0x00);
++
++ if (machine_is_vr1000()) {
++ dmfe_bast_probe(NULL, 0x80);
++ }
++
++ return 0;
++}
++
++module_init(dmfe_bast_init);
++
++#endif /* MODULE */
++/*
++ * Local variables:
++ * version-control: t
++ * kept-new-versions: 5
++ * c-indent-level: 8
++ * tab-width: 8
++ * End:
++ *
++ */
+diff -urN kernel-source-2.4.27-8/drivers/net/ether00.c kernel-source-2.4.27-8-arm-1/drivers/net/ether00.c
+--- kernel-source-2.4.27-8/drivers/net/ether00.c 2003-06-13 15:51:34.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/ether00.c 2005-02-18 17:48:43.000000000 +0000
+@@ -38,6 +38,7 @@
+ #include <asm/arch/ether00.h>
+ #include <asm/arch/tdkphy.h>
+
++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))
++ 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;i<MAX_MTD_DEVICES;i++){
+- mymtd=get_mtd_device(NULL,i);
+- if(!mymtd||!strcmp(mymtd->name,"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 kernel-source-2.4.27-8/drivers/net/irda/Config.in kernel-source-2.4.27-8-arm-1/drivers/net/irda/Config.in
+--- kernel-source-2.4.27-8/drivers/net/irda/Config.in 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/net/irda/Config.in 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/net/irda/nsc-ircc.c kernel-source-2.4.27-8-arm-1/drivers/net/irda/nsc-ircc.c
+--- kernel-source-2.4.27-8/drivers/net/irda/nsc-ircc.c 2005-01-19 09:57:53.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/net/irda/nsc-ircc.c 2005-02-18 17:48:43.000000000 +0000
+@@ -57,6 +57,11 @@
+ #include <asm/dma.h>
+ #include <asm/byteorder.h>
+
++#ifdef CONFIG_ARCH_BAST
++#include <asm/arch/hardware.h>
++#include <asm/arch/irqs.h>
++#endif
++
+ #include <linux/pm.h>
+
+ #include <net/irda/wrapper.h>
+@@ -91,6 +96,10 @@
+ /* These are the known NSC chips */
+ static nsc_chip_t chips[] = {
+ /* Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */
++#ifdef CONFIG_ARCH_BAST
++ { "PC87338", { BAST_PCSIO+0x298, 0, 0 }, 0x08, 0xb0, 0xf8,
++ nsc_ircc_probe_338, nsc_ircc_init_338 },
++#endif
+ { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0,
+ nsc_ircc_probe_108, nsc_ircc_init_108 },
+ { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8,
+@@ -677,6 +686,15 @@
+ }
+ }
+ }
++
++
++#ifdef CONFIG_ARCH_BAST
++ /* bast PCSIO lives in high memory space... */
++ if (cfg_base > 0x10000) {
++ info->fir_base += BAST_PCSIO;
++ }
++#endif
++
+ info->sir_base = info->fir_base;
+
+ /* Read PnP register 1 (PNP1) */
+@@ -691,6 +709,14 @@
+
+ info->dma = (reg & 0x07) - 1;
+
++#ifdef CONFIG_ARCH_BAST
++ /* we don't have a standard PC serial irq atm (fixup?) */
++ if (cfg_base > 0x10000) {
++ info->irq = IRQ_PCSERIAL2;
++ info->dma = -1;
++ }
++#endif
++
+ /* Read power and test register (PTR) */
+ outb(CFG_338_PTR, cfg_base);
+ reg = inb(cfg_base+1);
+diff -urN kernel-source-2.4.27-8/drivers/net/irda/nsc-ircc.c.orig kernel-source-2.4.27-8-arm-1/drivers/net/irda/nsc-ircc.c.orig
+--- kernel-source-2.4.27-8/drivers/net/irda/nsc-ircc.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/irda/nsc-ircc.c.orig 2005-01-19 09:57:53.000000000 +0000
+@@ -0,0 +1,2211 @@
++/*********************************************************************
++ *
++ * Filename: nsc-ircc.c
++ * Version: 1.0
++ * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets
++ * Status: Stable.
++ * Author: Dag Brattli <dagb at cs.uit.no>
++ * Created at: Sat Nov 7 21:43:15 1998
++ * Modified at: Sat Aug 14 04:14:57 2004
++ * Modified by: Maik Broemme <mbroemme at plusserver.de>
++ *
++ * Copyright (c) 1998-2000 Dag Brattli <dagb at cs.uit.no>
++ * Copyright (c) 1998 Lichen Wang, <lwang at actisys.com>
++ * Copyright (c) 1998 Actisys Corp., www.actisys.com
++ * 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, or (at your option) any later version.
++ *
++ * Neither Dag Brattli nor University of Tromsø admit liability nor
++ * provide warranty for any of this software. This material is
++ * provided "AS-IS" and at no charge.
++ *
++ * Notice that all functions that needs to access the chip in _any_
++ * way, must save BSR register on entry, and restore it on exit.
++ * It is _very_ important to follow this policy!
++ *
++ * __u8 bank;
++ *
++ * bank = inb(iobase+BSR);
++ *
++ * do_your_stuff_here();
++ *
++ * outb(bank, iobase+BSR);
++ *
++ * If you find bugs in this file, its very likely that the same bug
++ * will also be in w83977af_ir.c since the implementations are quite
++ * similar.
++ *
++ ********************************************************************/
++
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <linux/ioport.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/rtnetlink.h>
++
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/byteorder.h>
++
++#include <linux/pm.h>
++
++#include <net/irda/wrapper.h>
++#include <net/irda/irda.h>
++#include <net/irda/irmod.h>
++#include <net/irda/irlap_frame.h>
++#include <net/irda/irda_device.h>
++
++#include <net/irda/nsc-ircc.h>
++
++#define CHIP_IO_EXTENT 8
++#define BROKEN_DONGLE_ID
++
++static char *driver_name = "nsc-ircc";
++
++/* Module parameters */
++static int qos_mtt_bits = 0x07; /* 1 ms or more */
++static int dongle_id;
++
++/* Use BIOS settions by default, but user may supply module parameters */
++static unsigned int io[] = { ~0, ~0, ~0, ~0 };
++static unsigned int irq[] = { 0, 0, 0, 0, 0 };
++static unsigned int dma[] = { 0, 0, 0, 0, 0 };
++
++static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info);
++static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info);
++static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info);
++static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info);
++static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info);
++static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info);
++
++/* These are the known NSC chips */
++static nsc_chip_t chips[] = {
++/* Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */
++ { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0,
++ nsc_ircc_probe_108, nsc_ircc_init_108 },
++ { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8,
++ nsc_ircc_probe_338, nsc_ircc_init_338 },
++ /* Contributed by Jan Frey - IBM A30/A31 */
++ { "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff,
++ nsc_ircc_probe_39x, nsc_ircc_init_39x },
++ { NULL }
++#if 0
++ /* Probably bogus, "PC8739x" should be the real thing. Jean II */
++ /* Contributed by Kevin Thayer - OmniBook 6100 */
++ { "PC87338?", { 0x2e, 0x15c, 0x398 }, 0x08, 0x00, 0xf8,
++ nsc_ircc_probe_338, nsc_ircc_init_338 },
++#endif
++};
++
++/* Max 4 instances for now */
++static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL };
++
++static char *dongle_types[] = {
++ "Differential serial interface",
++ "Differential serial interface",
++ "Reserved",
++ "Reserved",
++ "Sharp RY5HD01",
++ "Reserved",
++ "Single-ended serial interface",
++ "Consumer-IR only",
++ "HP HSDL-2300, HP HSDL-3600/HSDL-3610",
++ "IBM31T1100 or Temic TFDS6000/TFDS6500",
++ "Reserved",
++ "Reserved",
++ "HP HSDL-1100/HSDL-2100",
++ "HP HSDL-1100/HSDL-2100",
++ "Supports SIR Mode only",
++ "No dongle connected",
++};
++
++/* Some prototypes */
++static int nsc_ircc_open(int i, chipio_t *info);
++#ifdef MODULE
++static int nsc_ircc_close(struct nsc_ircc_cb *self);
++#endif /* MODULE */
++static int nsc_ircc_setup(chipio_t *info);
++static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self);
++static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self);
++static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase);
++static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev);
++static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev);
++static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size);
++static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase);
++static void nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud);
++static void nsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
++static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self);
++static int nsc_ircc_read_dongle_id (int iobase);
++static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id);
++
++static int nsc_ircc_net_init(struct net_device *dev);
++static int nsc_ircc_net_open(struct net_device *dev);
++static int nsc_ircc_net_close(struct net_device *dev);
++static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
++static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev);
++static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);
++
++/*
++ * Function nsc_ircc_init ()
++ *
++ * Initialize chip. Just try to find out how many chips we are dealing with
++ * and where they are
++ */
++int __init nsc_ircc_init(void)
++{
++ chipio_t info;
++ nsc_chip_t *chip;
++ int ret = -ENODEV;
++ int cfg_base;
++ int cfg, id;
++ int reg;
++ int i = 0;
++
++ /* Probe for all the NSC chipsets we know about */
++ for (chip=chips; chip->name ; chip++) {
++ IRDA_DEBUG(2, "%s(), Probing for %s ...\n",
++ __FUNCTION__, chip->name);
++
++ /* Try all config registers for this chip */
++ for (cfg=0; cfg<3; cfg++) {
++ cfg_base = chip->cfg[cfg];
++ if (!cfg_base)
++ continue;
++
++ memset(&info, 0, sizeof(chipio_t));
++ info.cfg_base = cfg_base;
++ info.fir_base = io[i];
++ info.dma = dma[i];
++ info.irq = irq[i];
++
++ /* Read index register */
++ reg = inb(cfg_base);
++ if (reg == 0xff) {
++ IRDA_DEBUG(2, "%s() no chip at 0x%03x\n",
++ __FUNCTION__, cfg_base);
++ continue;
++ }
++
++ /* Read chip identification register */
++ outb(chip->cid_index, cfg_base);
++ id = inb(cfg_base+1);
++ if ((id & chip->cid_mask) == chip->cid_value) {
++ IRDA_DEBUG(2, "%s() Found %s chip, revision=%d\n",
++ __FUNCTION__, chip->name, id & ~chip->cid_mask);
++ /*
++ * If the user supplies the base address, then
++ * we init the chip, if not we probe the values
++ * set by the BIOS
++ */
++ if (io[i] < 0x2000) {
++ chip->init(chip, &info);
++ } else
++ chip->probe(chip, &info);
++
++ if (nsc_ircc_open(i, &info) == 0)
++ ret = 0;
++ i++;
++ } else {
++ IRDA_DEBUG(2, "%s(), Wrong chip id=0x%02x\n", __FUNCTION__, id);
++ }
++ }
++
++ }
++
++ return ret;
++}
++
++/*
++ * Function nsc_ircc_cleanup ()
++ *
++ * Close all configured chips
++ *
++ */
++#ifdef MODULE
++static void nsc_ircc_cleanup(void)
++{
++ int i;
++
++ pm_unregister_all(nsc_ircc_pmproc);
++
++ for (i=0; i < 4; i++) {
++ if (dev_self[i])
++ nsc_ircc_close(dev_self[i]);
++ }
++}
++#endif /* MODULE */
++
++/*
++ * Function nsc_ircc_open (iobase, irq)
++ *
++ * Open driver instance
++ *
++ */
++static int nsc_ircc_open(int i, chipio_t *info)
++{
++ struct net_device *dev;
++ struct nsc_ircc_cb *self;
++ struct pm_dev *pmdev;
++ void *ret;
++ int err;
++
++ IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
++
++ MESSAGE("%s, Found chip at base=0x%03x\n", driver_name,
++ info->cfg_base);
++
++ if ((nsc_ircc_setup(info)) == -1)
++ return -1;
++
++ MESSAGE("%s, driver loaded (Dag Brattli)\n", driver_name);
++
++ /* Allocate new instance of the driver */
++ self = kmalloc(sizeof(struct nsc_ircc_cb), GFP_KERNEL);
++ if (self == NULL) {
++ ERROR("%s(), can't allocate memory for "
++ "control block!\n", __FUNCTION__);
++ return -ENOMEM;
++ }
++ memset(self, 0, sizeof(struct nsc_ircc_cb));
++ spin_lock_init(&self->lock);
++
++ /* Need to store self somewhere */
++ dev_self[i] = self;
++ self->index = i;
++
++ /* Initialize IO */
++ self->io.cfg_base = info->cfg_base;
++ self->io.fir_base = info->fir_base;
++ self->io.irq = info->irq;
++ self->io.fir_ext = CHIP_IO_EXTENT;
++ self->io.dma = info->dma;
++ self->io.fifo_size = 32;
++
++ /* Reserve the ioports that we need */
++ ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);
++ if (!ret) {
++ WARNING("%s(), can't get iobase of 0x%03x\n",
++ __FUNCTION__, self->io.fir_base);
++ dev_self[i] = NULL;
++ kfree(self);
++ return -ENODEV;
++ }
++
++ /* Initialize QoS for this device */
++ irda_init_max_qos_capabilies(&self->qos);
++
++ /* The only value we must override it the baudrate */
++ self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
++ IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8);
++
++ self->qos.min_turn_time.bits = qos_mtt_bits;
++ irda_qos_bits_to_value(&self->qos);
++
++ self->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO|IFF_DONGLE;
++
++ /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
++ self->rx_buff.truesize = 14384;
++ self->tx_buff.truesize = 14384;
++
++ /* Allocate memory if needed */
++ self->rx_buff.head = (__u8 *) kmalloc(self->rx_buff.truesize,
++ GFP_KERNEL|GFP_DMA);
++ if (self->rx_buff.head == NULL) {
++ kfree(self);
++ return -ENOMEM;
++ }
++ memset(self->rx_buff.head, 0, self->rx_buff.truesize);
++
++ self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize,
++ GFP_KERNEL|GFP_DMA);
++ if (self->tx_buff.head == NULL) {
++ kfree(self->rx_buff.head);
++ kfree(self);
++ return -ENOMEM;
++ }
++ memset(self->tx_buff.head, 0, self->tx_buff.truesize);
++
++ self->rx_buff.in_frame = FALSE;
++ self->rx_buff.state = OUTSIDE_FRAME;
++ self->tx_buff.data = self->tx_buff.head;
++ self->rx_buff.data = self->rx_buff.head;
++
++ /* Reset Tx queue info */
++ self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
++ self->tx_fifo.tail = self->tx_buff.head;
++
++ if (!(dev = dev_alloc("irda%d", &err))) {
++ ERROR("%s(), dev_alloc() failed!\n", __FUNCTION__);
++ return -ENOMEM;
++ }
++
++ dev->priv = (void *) self;
++ self->netdev = dev;
++
++ /* Override the network functions we need to use */
++ dev->init = nsc_ircc_net_init;
++ dev->hard_start_xmit = nsc_ircc_hard_xmit_sir;
++ dev->open = nsc_ircc_net_open;
++ dev->stop = nsc_ircc_net_close;
++ dev->do_ioctl = nsc_ircc_net_ioctl;
++ dev->get_stats = nsc_ircc_net_get_stats;
++
++ rtnl_lock();
++ err = register_netdevice(dev);
++ rtnl_unlock();
++ if (err) {
++ ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);
++ return -1;
++ }
++ MESSAGE("IrDA: Registered device %s\n", dev->name);
++
++ /* Check if user has supplied the dongle id and if it is in the range of available ids or not. */
++ if (!dongle_id) {
++ dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base);
++
++ MESSAGE("%s, Found dongle: %s\n", driver_name,
++ dongle_types[dongle_id]);
++ } else {
++ if (dongle_id < sizeof(dongle_types) / sizeof(dongle_types[0])) {
++ MESSAGE("%s, Using dongle: %s\n", driver_name,
++ dongle_types[dongle_id]);
++ } else {
++ MESSAGE("%s, dongle id %i out of range, start autodetect.\n", driver_name, dongle_id);
++ dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base);
++ MESSAGE("%s, Found dongle: %s\n", driver_name,
++ dongle_types[dongle_id]);
++ }
++ }
++
++ self->io.dongle_id = dongle_id;
++ nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id);
++
++ pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, nsc_ircc_pmproc);
++ if (pmdev)
++ pmdev->data = self;
++
++ return 0;
++}
++
++#ifdef MODULE
++/*
++ * Function nsc_ircc_close (self)
++ *
++ * Close driver instance
++ *
++ */
++static int nsc_ircc_close(struct nsc_ircc_cb *self)
++{
++ int iobase;
++
++ IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
++
++ ASSERT(self != NULL, return -1;);
++
++ iobase = self->io.fir_base;
++
++ /* Remove netdevice */
++ if (self->netdev) {
++ rtnl_lock();
++ unregister_netdevice(self->netdev);
++ rtnl_unlock();
++ }
++
++ /* Release the PORT that this driver is using */
++ IRDA_DEBUG(4, "%s(), Releasing Region %03x\n",
++ __FUNCTION__, self->io.fir_base);
++ release_region(self->io.fir_base, self->io.fir_ext);
++
++ if (self->tx_buff.head)
++ kfree(self->tx_buff.head);
++
++ if (self->rx_buff.head)
++ kfree(self->rx_buff.head);
++
++ dev_self[self->index] = NULL;
++ kfree(self);
++
++ return 0;
++}
++#endif /* MODULE */
++
++/*
++ * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma)
++ *
++ * Initialize the NSC '108 chip
++ *
++ */
++static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info)
++{
++ int cfg_base = info->cfg_base;
++ __u8 temp=0;
++
++ outb(2, cfg_base); /* Mode Control Register (MCTL) */
++ outb(0x00, cfg_base+1); /* Disable device */
++
++ /* Base Address and Interrupt Control Register (BAIC) */
++ outb(CFG_108_BAIC, cfg_base);
++ switch (info->fir_base) {
++ case 0x3e8: outb(0x14, cfg_base+1); break;
++ case 0x2e8: outb(0x15, cfg_base+1); break;
++ case 0x3f8: outb(0x16, cfg_base+1); break;
++ case 0x2f8: outb(0x17, cfg_base+1); break;
++ default: ERROR("%s(), invalid base_address", __FUNCTION__);
++ }
++
++ /* Control Signal Routing Register (CSRT) */
++ switch (info->irq) {
++ case 3: temp = 0x01; break;
++ case 4: temp = 0x02; break;
++ case 5: temp = 0x03; break;
++ case 7: temp = 0x04; break;
++ case 9: temp = 0x05; break;
++ case 11: temp = 0x06; break;
++ case 15: temp = 0x07; break;
++ default: ERROR("%s(), invalid irq", __FUNCTION__);
++ }
++ outb(CFG_108_CSRT, cfg_base);
++
++ switch (info->dma) {
++ case 0: outb(0x08+temp, cfg_base+1); break;
++ case 1: outb(0x10+temp, cfg_base+1); break;
++ case 3: outb(0x18+temp, cfg_base+1); break;
++ default: ERROR("%s(), invalid dma", __FUNCTION__);
++ }
++
++ outb(CFG_108_MCTL, cfg_base); /* Mode Control Register (MCTL) */
++ outb(0x03, cfg_base+1); /* Enable device */
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_probe_108 (chip, info)
++ *
++ *
++ *
++ */
++static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info)
++{
++ int cfg_base = info->cfg_base;
++ int reg;
++
++ /* Read address and interrupt control register (BAIC) */
++ outb(CFG_108_BAIC, cfg_base);
++ reg = inb(cfg_base+1);
++
++ switch (reg & 0x03) {
++ case 0:
++ info->fir_base = 0x3e8;
++ break;
++ case 1:
++ info->fir_base = 0x2e8;
++ break;
++ case 2:
++ info->fir_base = 0x3f8;
++ break;
++ case 3:
++ info->fir_base = 0x2f8;
++ break;
++ }
++ info->sir_base = info->fir_base;
++ IRDA_DEBUG(2, "%s(), probing fir_base=0x%03x\n",
++ __FUNCTION__, info->fir_base);
++
++ /* Read control signals routing register (CSRT) */
++ outb(CFG_108_CSRT, cfg_base);
++ reg = inb(cfg_base+1);
++
++ switch (reg & 0x07) {
++ case 0:
++ info->irq = -1;
++ break;
++ case 1:
++ info->irq = 3;
++ break;
++ case 2:
++ info->irq = 4;
++ break;
++ case 3:
++ info->irq = 5;
++ break;
++ case 4:
++ info->irq = 7;
++ break;
++ case 5:
++ info->irq = 9;
++ break;
++ case 6:
++ info->irq = 11;
++ break;
++ case 7:
++ info->irq = 15;
++ break;
++ }
++ IRDA_DEBUG(2, "%s(), probing irq=%d\n", __FUNCTION__, info->irq);
++
++ /* Currently we only read Rx DMA but it will also be used for Tx */
++ switch ((reg >> 3) & 0x03) {
++ case 0:
++ info->dma = -1;
++ break;
++ case 1:
++ info->dma = 0;
++ break;
++ case 2:
++ info->dma = 1;
++ break;
++ case 3:
++ info->dma = 3;
++ break;
++ }
++ IRDA_DEBUG(2, "%s(), probing dma=%d\n", __FUNCTION__, info->dma);
++
++ /* Read mode control register (MCTL) */
++ outb(CFG_108_MCTL, cfg_base);
++ reg = inb(cfg_base+1);
++
++ info->enabled = reg & 0x01;
++ info->suspended = !((reg >> 1) & 0x01);
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_init_338 (chip, info)
++ *
++ * Initialize the NSC '338 chip. Remember that the 87338 needs two
++ * consecutive writes to the data registers while CPU interrupts are
++ * disabled. The 97338 does not require this, but shouldn't be any
++ * harm if we do it anyway.
++ */
++static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info)
++{
++ /* No init yet */
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_probe_338 (chip, info)
++ *
++ *
++ *
++ */
++static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info)
++{
++ int cfg_base = info->cfg_base;
++ int reg, com = 0;
++ int pnp;
++
++ /* Read funtion enable register (FER) */
++ outb(CFG_338_FER, cfg_base);
++ reg = inb(cfg_base+1);
++
++ info->enabled = (reg >> 2) & 0x01;
++
++ /* Check if we are in Legacy or PnP mode */
++ outb(CFG_338_PNP0, cfg_base);
++ reg = inb(cfg_base+1);
++
++ pnp = (reg >> 3) & 0x01;
++ if (pnp) {
++ IRDA_DEBUG(2, "(), Chip is in PnP mode\n");
++ outb(0x46, cfg_base);
++ reg = (inb(cfg_base+1) & 0xfe) << 2;
++
++ outb(0x47, cfg_base);
++ reg |= ((inb(cfg_base+1) & 0xfc) << 8);
++
++ info->fir_base = reg;
++ } else {
++ /* Read function address register (FAR) */
++ outb(CFG_338_FAR, cfg_base);
++ reg = inb(cfg_base+1);
++
++ switch ((reg >> 4) & 0x03) {
++ case 0:
++ info->fir_base = 0x3f8;
++ break;
++ case 1:
++ info->fir_base = 0x2f8;
++ break;
++ case 2:
++ com = 3;
++ break;
++ case 3:
++ com = 4;
++ break;
++ }
++
++ if (com) {
++ switch ((reg >> 6) & 0x03) {
++ case 0:
++ if (com == 3)
++ info->fir_base = 0x3e8;
++ else
++ info->fir_base = 0x2e8;
++ break;
++ case 1:
++ if (com == 3)
++ info->fir_base = 0x338;
++ else
++ info->fir_base = 0x238;
++ break;
++ case 2:
++ if (com == 3)
++ info->fir_base = 0x2e8;
++ else
++ info->fir_base = 0x2e0;
++ break;
++ case 3:
++ if (com == 3)
++ info->fir_base = 0x220;
++ else
++ info->fir_base = 0x228;
++ break;
++ }
++ }
++ }
++ info->sir_base = info->fir_base;
++
++ /* Read PnP register 1 (PNP1) */
++ outb(CFG_338_PNP1, cfg_base);
++ reg = inb(cfg_base+1);
++
++ info->irq = reg >> 4;
++
++ /* Read PnP register 3 (PNP3) */
++ outb(CFG_338_PNP3, cfg_base);
++ reg = inb(cfg_base+1);
++
++ info->dma = (reg & 0x07) - 1;
++
++ /* Read power and test register (PTR) */
++ outb(CFG_338_PTR, cfg_base);
++ reg = inb(cfg_base+1);
++
++ info->suspended = reg & 0x01;
++
++ return 0;
++}
++
++
++/*
++ * Function nsc_ircc_init_39x (chip, info)
++ *
++ * Now that we know it's a '39x (see probe below), we need to
++ * configure it so we can use it.
++ *
++ * The NSC '338 chip is a Super I/O chip with a "bank" architecture,
++ * the configuration of the different functionality (serial, parallel,
++ * floppy...) are each in a different bank (Logical Device Number).
++ * The base address, irq and dma configuration registers are common
++ * to all functionalities (index 0x30 to 0x7F).
++ * There is only one configuration register specific to the
++ * serial port, CFG_39X_SPC.
++ * JeanII
++ *
++ * Note : this code was written by Jan Frey <janfrey at web.de>
++ */
++static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info)
++{
++ int cfg_base = info->cfg_base;
++ int enabled;
++
++ /* User is shure about his config... accept it. */
++ IRDA_DEBUG(2, "%s(): nsc_ircc_init_39x (user settings): "
++ "io=0x%04x, irq=%d, dma=%d\n",
++ __FUNCTION__, info->fir_base, info->irq, info->dma);
++
++ /* Access bank for SP2 */
++ outb(CFG_39X_LDN, cfg_base);
++ outb(0x02, cfg_base+1);
++
++ /* Configure SP2 */
++
++ /* We want to enable the device if not enabled */
++ outb(CFG_39X_ACT, cfg_base);
++ enabled = inb(cfg_base+1) & 0x01;
++
++ if (!enabled) {
++ /* Enable the device */
++ outb(CFG_39X_SIOCF1, cfg_base);
++ outb(0x01, cfg_base+1);
++ /* May want to update info->enabled. Jean II */
++ }
++
++ /* Enable UART bank switching (bit 7) ; Sets the chip to normal
++ * power mode (wake up from sleep mode) (bit 1) */
++ outb(CFG_39X_SPC, cfg_base);
++ outb(0x82, cfg_base+1);
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_probe_39x (chip, info)
++ *
++ * Test if we really have a '39x chip at the given address
++ *
++ * Note : this code was written by Jan Frey <janfrey at web.de>
++ */
++static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info)
++{
++ int cfg_base = info->cfg_base;
++ int reg1, reg2, irq, irqt, dma1, dma2;
++ int enabled, susp;
++
++ IRDA_DEBUG(2, "%s(), nsc_ircc_probe_39x, base=%d\n",
++ __FUNCTION__, cfg_base);
++
++ /* This function should be executed with irq off to avoid
++ * another driver messing with the Super I/O bank - Jean II */
++
++ /* Access bank for SP2 */
++ outb(CFG_39X_LDN, cfg_base);
++ outb(0x02, cfg_base+1);
++
++ /* Read infos about SP2 ; store in info struct */
++ outb(CFG_39X_BASEH, cfg_base);
++ reg1 = inb(cfg_base+1);
++ outb(CFG_39X_BASEL, cfg_base);
++ reg2 = inb(cfg_base+1);
++ info->fir_base = (reg1 << 8) | reg2;
++
++ outb(CFG_39X_IRQNUM, cfg_base);
++ irq = inb(cfg_base+1);
++ outb(CFG_39X_IRQSEL, cfg_base);
++ irqt = inb(cfg_base+1);
++ info->irq = irq;
++
++ outb(CFG_39X_DMA0, cfg_base);
++ dma1 = inb(cfg_base+1);
++ outb(CFG_39X_DMA1, cfg_base);
++ dma2 = inb(cfg_base+1);
++ info->dma = dma1 -1;
++
++ outb(CFG_39X_ACT, cfg_base);
++ info->enabled = enabled = inb(cfg_base+1) & 0x01;
++
++ outb(CFG_39X_SPC, cfg_base);
++ susp = 1 - ((inb(cfg_base+1) & 0x02) >> 1);
++
++ IRDA_DEBUG(2, "%s(): io=0x%02x%02x, irq=%d (type %d), rxdma=%d, txdma=%d, enabled=%d (suspended=%d)\n", __FUNCTION__, reg1,reg2,irq,irqt,dma1,dma2,enabled,susp);
++
++ /* Configure SP2 */
++
++ /* We want to enable the device if not enabled */
++ outb(CFG_39X_ACT, cfg_base);
++ enabled = inb(cfg_base+1) & 0x01;
++
++ if (!enabled) {
++ /* Enable the device */
++ outb(CFG_39X_SIOCF1, cfg_base);
++ outb(0x01, cfg_base+1);
++ /* May want to update info->enabled. Jean II */
++ }
++
++ /* Enable UART bank switching (bit 7) ; Sets the chip to normal
++ * power mode (wake up from sleep mode) (bit 1) */
++ outb(CFG_39X_SPC, cfg_base);
++ outb(0x82, cfg_base+1);
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_setup (info)
++ *
++ * Returns non-negative on success.
++ *
++ */
++static int nsc_ircc_setup(chipio_t *info)
++{
++ int version;
++ int iobase = info->fir_base;
++
++ /* Read the Module ID */
++ switch_bank(iobase, BANK3);
++ version = inb(iobase+MID);
++
++ IRDA_DEBUG(2, "%s() Driver %s Found chip version %02x\n", __FUNCTION__,
++ driver_name, version);
++
++ /* Should be 0x2? */
++ if (0x20 != (version & 0xf0)) {
++ ERROR("%s, Wrong chip version %02x\n", driver_name, version);
++ return -1;
++ }
++
++ /* Switch to advanced mode */
++ switch_bank(iobase, BANK2);
++ outb(ECR1_EXT_SL, iobase+ECR1);
++ switch_bank(iobase, BANK0);
++
++ /* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */
++ switch_bank(iobase, BANK0);
++ outb(FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
++
++ outb(0x03, iobase+LCR); /* 8 bit word length */
++ outb(MCR_SIR, iobase+MCR); /* Start at SIR-mode, also clears LSR*/
++
++ /* Set FIFO size to 32 */
++ switch_bank(iobase, BANK2);
++ outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);
++
++ /* IRCR2: FEND_MD is not set */
++ switch_bank(iobase, BANK5);
++ outb(0x02, iobase+4);
++
++ /* Make sure that some defaults are OK */
++ switch_bank(iobase, BANK6);
++ outb(0x20, iobase+0); /* Set 32 bits FIR CRC */
++ outb(0x0a, iobase+1); /* Set MIR pulse width */
++ outb(0x0d, iobase+2); /* Set SIR pulse width to 1.6us */
++ outb(0x2a, iobase+4); /* Set beginning frag, and preamble length */
++
++ /* Enable receive interrupts */
++ switch_bank(iobase, BANK0);
++ outb(IER_RXHDL_IE, iobase+IER);
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_read_dongle_id (void)
++ *
++ * Try to read dongle indentification. This procedure needs to be executed
++ * once after power-on/reset. It also needs to be used whenever you suspect
++ * that the user may have plugged/unplugged the IrDA Dongle.
++ */
++static int nsc_ircc_read_dongle_id (int iobase)
++{
++ int dongle_id;
++ __u8 bank;
++
++ bank = inb(iobase+BSR);
++
++ /* Select Bank 7 */
++ switch_bank(iobase, BANK7);
++
++ /* IRCFG4: IRSL0_DS and IRSL21_DS are cleared */
++ outb(0x00, iobase+7);
++
++ /* ID0, 1, and 2 are pulled up/down very slowly */
++ udelay(50);
++
++ /* IRCFG1: read the ID bits */
++ dongle_id = inb(iobase+4) & 0x0f;
++
++#ifdef BROKEN_DONGLE_ID
++ if (dongle_id == 0x0a)
++ dongle_id = 0x09;
++#endif
++ /* Go back to bank 0 before returning */
++ switch_bank(iobase, BANK0);
++
++ outb(bank, iobase+BSR);
++
++ return dongle_id;
++}
++
++/*
++ * Function nsc_ircc_init_dongle_interface (iobase, dongle_id)
++ *
++ * This function initializes the dongle for the transceiver that is
++ * used. This procedure needs to be executed once after
++ * power-on/reset. It also needs to be used whenever you suspect that
++ * the dongle is changed.
++ */
++static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id)
++{
++ int bank;
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ /* Select Bank 7 */
++ switch_bank(iobase, BANK7);
++
++ /* IRCFG4: set according to dongle_id */
++ switch (dongle_id) {
++ case 0x00: /* same as */
++ case 0x01: /* Differential serial interface */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x02: /* same as */
++ case 0x03: /* Reserved */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x04: /* Sharp RY5HD01 */
++ break;
++ case 0x05: /* Reserved, but this is what the Thinkpad reports */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x06: /* Single-ended serial interface */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x07: /* Consumer-IR only */
++ IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
++ IRDA_DEBUG(0, "%s(), %s\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
++ outb(0x28, iobase+7); /* Set irsl[0-2] as output */
++ break;
++ case 0x0A: /* same as */
++ case 0x0B: /* Reserved */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x0C: /* same as */
++ case 0x0D: /* HP HSDL-1100/HSDL-2100 */
++ /*
++ * Set irsl0 as input, irsl[1-2] as output, and separate
++ * inputs are used for SIR and MIR/FIR
++ */
++ outb(0x48, iobase+7);
++ break;
++ case 0x0E: /* Supports SIR Mode only */
++ outb(0x28, iobase+7); /* Set irsl[0-2] as output */
++ break;
++ case 0x0F: /* No dongle connected */
++ IRDA_DEBUG(0, "%s(), %s\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++
++ switch_bank(iobase, BANK0);
++ outb(0x62, iobase+MCR);
++ break;
++ default:
++ IRDA_DEBUG(0, "%s(), invalid dongle_id %#x",
++ __FUNCTION__, dongle_id);
++ }
++
++ /* IRCFG1: IRSL1 and 2 are set to IrDA mode */
++ outb(0x00, iobase+4);
++
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++
++} /* set_up_dongle_interface */
++
++/*
++ * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id)
++ *
++ * Change speed of the attach dongle
++ *
++ */
++static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id)
++{
++ unsigned long flags;
++ __u8 bank;
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ /* Select Bank 7 */
++ switch_bank(iobase, BANK7);
++
++ /* IRCFG1: set according to dongle_id */
++ switch (dongle_id) {
++ case 0x00: /* same as */
++ case 0x01: /* Differential serial interface */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x02: /* same as */
++ case 0x03: /* Reserved */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x04: /* Sharp RY5HD01 */
++ break;
++ case 0x05: /* Reserved */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x06: /* Single-ended serial interface */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x07: /* Consumer-IR only */
++ IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
++ IRDA_DEBUG(0, "%s(), %s\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ outb(0x00, iobase+4);
++ if (speed > 115200)
++ outb(0x01, iobase+4);
++ break;
++ case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
++ outb(0x01, iobase+4);
++
++ if (speed == 4000000) {
++ save_flags(flags);
++ cli();
++ outb(0x81, iobase+4);
++ outb(0x80, iobase+4);
++ restore_flags(flags);
++ } else
++ outb(0x00, iobase+4);
++ break;
++ case 0x0A: /* same as */
++ case 0x0B: /* Reserved */
++ IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++ break;
++ case 0x0C: /* same as */
++ case 0x0D: /* HP HSDL-1100/HSDL-2100 */
++ break;
++ case 0x0E: /* Supports SIR Mode only */
++ break;
++ case 0x0F: /* No dongle connected */
++ IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n",
++ __FUNCTION__, dongle_types[dongle_id]);
++
++ switch_bank(iobase, BANK0);
++ outb(0x62, iobase+MCR);
++ break;
++ default:
++ IRDA_DEBUG(0, "%s(), invalid data_rate\n", __FUNCTION__);
++ }
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++}
++
++/*
++ * Function nsc_ircc_change_speed (self, baud)
++ *
++ * Change the speed of the device
++ *
++ */
++static void nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed)
++{
++ struct net_device *dev = self->netdev;
++ __u8 mcr = MCR_SIR;
++ int iobase;
++ __u8 bank;
++
++ IRDA_DEBUG(2, "%s(), speed=%d\n", __FUNCTION__, speed);
++
++ ASSERT(self != NULL, return;);
++
++ iobase = self->io.fir_base;
++
++ /* Update accounting for new speed */
++ self->io.speed = speed;
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ /* Disable interrupts */
++ switch_bank(iobase, BANK0);
++ outb(0, iobase+IER);
++
++ /* Select Bank 2 */
++ switch_bank(iobase, BANK2);
++
++ outb(0x00, iobase+BGDH);
++ switch (speed) {
++ case 9600: outb(0x0c, iobase+BGDL); break;
++ case 19200: outb(0x06, iobase+BGDL); break;
++ case 38400: outb(0x03, iobase+BGDL); break;
++ case 57600: outb(0x02, iobase+BGDL); break;
++ case 115200: outb(0x01, iobase+BGDL); break;
++ case 576000:
++ switch_bank(iobase, BANK5);
++
++ /* IRCR2: MDRS is set */
++ outb(inb(iobase+4) | 0x04, iobase+4);
++
++ mcr = MCR_MIR;
++ IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__);
++ break;
++ case 1152000:
++ mcr = MCR_MIR;
++ IRDA_DEBUG(0, "%s(), handling baud of 1152000\n", __FUNCTION__);
++ break;
++ case 4000000:
++ mcr = MCR_FIR;
++ IRDA_DEBUG(0, "%s(), handling baud of 4000000\n", __FUNCTION__);
++ break;
++ default:
++ mcr = MCR_FIR;
++ IRDA_DEBUG(0, "%s(), unknown baud rate of %d\n",
++ __FUNCTION__, speed);
++ break;
++ }
++
++ /* Set appropriate speed mode */
++ switch_bank(iobase, BANK0);
++ outb(mcr | MCR_TX_DFR, iobase+MCR);
++
++ /* Give some hits to the transceiver */
++ nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id);
++
++ /* Set FIFO threshold to TX17, RX16 */
++ switch_bank(iobase, BANK0);
++ outb(0x00, iobase+FCR);
++ outb(FCR_FIFO_EN, iobase+FCR);
++ outb(FCR_RXTH| /* Set Rx FIFO threshold */
++ FCR_TXTH| /* Set Tx FIFO threshold */
++ FCR_TXSR| /* Reset Tx FIFO */
++ FCR_RXSR| /* Reset Rx FIFO */
++ FCR_FIFO_EN, /* Enable FIFOs */
++ iobase+FCR);
++
++ /* Set FIFO size to 32 */
++ switch_bank(iobase, BANK2);
++ outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);
++
++ /* Enable some interrupts so we can receive frames */
++ switch_bank(iobase, BANK0);
++ if (speed > 115200) {
++ /* Install FIR xmit handler */
++ dev->hard_start_xmit = nsc_ircc_hard_xmit_fir;
++ outb(IER_SFIF_IE, iobase+IER);
++ nsc_ircc_dma_receive(self);
++ } else {
++ /* Install SIR xmit handler */
++ dev->hard_start_xmit = nsc_ircc_hard_xmit_sir;
++ outb(IER_RXHDL_IE, iobase+IER);
++ }
++
++ /* Restore BSR */
++ outb(bank, iobase+BSR);
++ netif_wake_queue(dev);
++
++}
++
++/*
++ * Function nsc_ircc_hard_xmit (skb, dev)
++ *
++ * Transmit the frame!
++ *
++ */
++static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
++{
++ struct nsc_ircc_cb *self;
++ unsigned long flags;
++ int iobase;
++ __s32 speed;
++ __u8 bank;
++
++ self = (struct nsc_ircc_cb *) dev->priv;
++
++ ASSERT(self != NULL, return 0;);
++
++ iobase = self->io.fir_base;
++
++ netif_stop_queue(dev);
++
++ /* Check if we need to change the speed */
++ speed = irda_get_next_speed(skb);
++ if ((speed != self->io.speed) && (speed != -1)) {
++ /* Check for empty frame */
++ if (!skb->len) {
++ nsc_ircc_change_speed(self, speed);
++ dev_kfree_skb(skb);
++ return 0;
++ } else
++ self->new_speed = speed;
++ }
++
++ spin_lock_irqsave(&self->lock, flags);
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ self->tx_buff.data = self->tx_buff.head;
++
++ self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data,
++ self->tx_buff.truesize);
++
++ self->stats.tx_bytes += self->tx_buff.len;
++
++ /* Add interrupt on tx low level (will fire immediately) */
++ switch_bank(iobase, BANK0);
++ outb(IER_TXLDL_IE, iobase+IER);
++
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++
++ spin_unlock_irqrestore(&self->lock, flags);
++
++ dev_kfree_skb(skb);
++
++ return 0;
++}
++
++static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
++{
++ struct nsc_ircc_cb *self;
++ unsigned long flags;
++ int iobase;
++ __s32 speed;
++ __u8 bank;
++ int mtt, diff;
++
++ self = (struct nsc_ircc_cb *) dev->priv;
++ iobase = self->io.fir_base;
++
++ netif_stop_queue(dev);
++
++ /* Check if we need to change the speed */
++ speed = irda_get_next_speed(skb);
++ if ((speed != self->io.speed) && (speed != -1)) {
++ /* Check for empty frame */
++ if (!skb->len) {
++ nsc_ircc_change_speed(self, speed);
++ dev_kfree_skb(skb);
++ return 0;
++ } else
++ self->new_speed = speed;
++ }
++
++ spin_lock_irqsave(&self->lock, flags);
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ /* Register and copy this frame to DMA memory */
++ self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail;
++ self->tx_fifo.queue[self->tx_fifo.free].len = skb->len;
++ self->tx_fifo.tail += skb->len;
++
++ self->stats.tx_bytes += skb->len;
++
++ memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data,
++ skb->len);
++
++ self->tx_fifo.len++;
++ self->tx_fifo.free++;
++
++ /* Start transmit only if there is currently no transmit going on */
++ if (self->tx_fifo.len == 1) {
++ /* Check if we must wait the min turn time or not */
++ mtt = irda_get_mtt(skb);
++ if (mtt) {
++ /* Check how much time we have used already */
++ do_gettimeofday(&self->now);
++ diff = self->now.tv_usec - self->stamp.tv_usec;
++ if (diff < 0)
++ diff += 1000000;
++
++ /* Check if the mtt is larger than the time we have
++ * already used by all the protocol processing
++ */
++ if (mtt > diff) {
++ mtt -= diff;
++
++ /*
++ * Use timer if delay larger than 125 us, and
++ * use udelay for smaller values which should
++ * be acceptable
++ */
++ if (mtt > 125) {
++ /* Adjust for timer resolution */
++ mtt = mtt / 125;
++
++ /* Setup timer */
++ switch_bank(iobase, BANK4);
++ outb(mtt & 0xff, iobase+TMRL);
++ outb((mtt >> 8) & 0x0f, iobase+TMRH);
++
++ /* Start timer */
++ outb(IRCR1_TMR_EN, iobase+IRCR1);
++ self->io.direction = IO_XMIT;
++
++ /* Enable timer interrupt */
++ switch_bank(iobase, BANK0);
++ outb(IER_TMR_IE, iobase+IER);
++
++ /* Timer will take care of the rest */
++ goto out;
++ } else
++ udelay(mtt);
++ }
++ }
++ /* Enable DMA interrupt */
++ switch_bank(iobase, BANK0);
++ outb(IER_DMA_IE, iobase+IER);
++
++ /* Transmit frame */
++ nsc_ircc_dma_xmit(self, iobase);
++ }
++ out:
++ /* Not busy transmitting anymore if window is not full */
++ if (self->tx_fifo.free < MAX_TX_WINDOW)
++ netif_wake_queue(self->netdev);
++
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++
++ spin_unlock_irqrestore(&self->lock, flags);
++ dev_kfree_skb(skb);
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_dma_xmit (self, iobase)
++ *
++ * Transmit data using DMA
++ *
++ */
++static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase)
++{
++ int bsr;
++
++ /* Save current bank */
++ bsr = inb(iobase+BSR);
++
++ /* Disable DMA */
++ switch_bank(iobase, BANK0);
++ outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
++
++ self->io.direction = IO_XMIT;
++
++ /* Choose transmit DMA channel */
++ switch_bank(iobase, BANK2);
++ outb(ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1);
++
++ setup_dma(self->io.dma,
++ self->tx_fifo.queue[self->tx_fifo.ptr].start,
++ self->tx_fifo.queue[self->tx_fifo.ptr].len,
++ DMA_TX_MODE);
++
++ /* Enable DMA and SIR interaction pulse */
++ switch_bank(iobase, BANK0);
++ outb(inb(iobase+MCR)|MCR_TX_DFR|MCR_DMA_EN|MCR_IR_PLS, iobase+MCR);
++
++ /* Restore bank register */
++ outb(bsr, iobase+BSR);
++}
++
++/*
++ * Function nsc_ircc_pio_xmit (self, iobase)
++ *
++ * Transmit data using PIO. Returns the number of bytes that actually
++ * got transferred
++ *
++ */
++static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size)
++{
++ int actual = 0;
++ __u8 bank;
++
++ IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ switch_bank(iobase, BANK0);
++ if (!(inb_p(iobase+LSR) & LSR_TXEMP)) {
++ IRDA_DEBUG(4, "%s(), warning, FIFO not empty yet!\n", __FUNCTION__);
++
++ /* FIFO may still be filled to the Tx interrupt threshold */
++ fifo_size -= 17;
++ }
++
++ /* Fill FIFO with current frame */
++ while ((fifo_size-- > 0) && (actual < len)) {
++ /* Transmit next byte */
++ outb(buf[actual++], iobase+TXD);
++ }
++
++ IRDA_DEBUG(4, "%s(), fifo_size %d ; %d sent of %d\n",
++ __FUNCTION__, fifo_size, actual, len);
++
++ /* Restore bank */
++ outb(bank, iobase+BSR);
++
++ return actual;
++}
++
++/*
++ * Function nsc_ircc_dma_xmit_complete (self)
++ *
++ * The transfer of a frame in finished. This function will only be called
++ * by the interrupt handler
++ *
++ */
++static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self)
++{
++ int iobase;
++ __u8 bank;
++ int ret = TRUE;
++
++ IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
++
++ iobase = self->io.fir_base;
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ /* Disable DMA */
++ switch_bank(iobase, BANK0);
++ outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
++
++ /* Check for underrrun! */
++ if (inb(iobase+ASCR) & ASCR_TXUR) {
++ self->stats.tx_errors++;
++ self->stats.tx_fifo_errors++;
++
++ /* Clear bit, by writing 1 into it */
++ outb(ASCR_TXUR, iobase+ASCR);
++ } else {
++ self->stats.tx_packets++;
++ }
++
++ /* Check if we need to change the speed */
++ if (self->new_speed) {
++ nsc_ircc_change_speed(self, self->new_speed);
++ self->new_speed = 0;
++ }
++
++ /* Finished with this frame, so prepare for next */
++ self->tx_fifo.ptr++;
++ self->tx_fifo.len--;
++
++ /* Any frames to be sent back-to-back? */
++ if (self->tx_fifo.len) {
++ nsc_ircc_dma_xmit(self, iobase);
++
++ /* Not finished yet! */
++ ret = FALSE;
++ } else {
++ /* Reset Tx FIFO info */
++ self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
++ self->tx_fifo.tail = self->tx_buff.head;
++ }
++
++ /* Make sure we have room for more frames */
++ if (self->tx_fifo.free < MAX_TX_WINDOW) {
++ /* Not busy transmitting anymore */
++ /* Tell the network layer, that we can accept more frames */
++ netif_wake_queue(self->netdev);
++ }
++
++ /* Restore bank */
++ outb(bank, iobase+BSR);
++
++ return ret;
++}
++
++/*
++ * Function nsc_ircc_dma_receive (self)
++ *
++ * Get ready for receiving a frame. The device will initiate a DMA
++ * if it starts to receive a frame.
++ *
++ */
++static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self)
++{
++ int iobase;
++ __u8 bsr;
++
++ iobase = self->io.fir_base;
++
++ /* Reset Tx FIFO info */
++ self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0;
++ self->tx_fifo.tail = self->tx_buff.head;
++
++ /* Save current bank */
++ bsr = inb(iobase+BSR);
++
++ /* Disable DMA */
++ switch_bank(iobase, BANK0);
++ outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
++
++ /* Choose DMA Rx, DMA Fairness, and Advanced mode */
++ switch_bank(iobase, BANK2);
++ outb(ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1);
++
++ self->io.direction = IO_RECV;
++ self->rx_buff.data = self->rx_buff.head;
++
++ /* Reset Rx FIFO. This will also flush the ST_FIFO */
++ switch_bank(iobase, BANK0);
++ outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
++
++ self->st_fifo.len = self->st_fifo.pending_bytes = 0;
++ self->st_fifo.tail = self->st_fifo.head = 0;
++
++ setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize,
++ DMA_RX_MODE);
++
++ /* Enable DMA */
++ switch_bank(iobase, BANK0);
++ outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR);
++
++ /* Restore bank register */
++ outb(bsr, iobase+BSR);
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_dma_receive_complete (self)
++ *
++ * Finished with receiving frames
++ *
++ *
++ */
++static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase)
++{
++ struct st_fifo *st_fifo;
++ struct sk_buff *skb;
++ __u8 status;
++ __u8 bank;
++ int len;
++
++ st_fifo = &self->st_fifo;
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ /* Read all entries in status FIFO */
++ switch_bank(iobase, BANK5);
++ while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) {
++ /* We must empty the status FIFO no matter what */
++ len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8);
++
++ if (st_fifo->tail >= MAX_RX_WINDOW) {
++ IRDA_DEBUG(0, "%s(), window is full!\n", __FUNCTION__);
++ continue;
++ }
++
++ st_fifo->entries[st_fifo->tail].status = status;
++ st_fifo->entries[st_fifo->tail].len = len;
++ st_fifo->pending_bytes += len;
++ st_fifo->tail++;
++ st_fifo->len++;
++ }
++ /* Try to process all entries in status FIFO */
++ while (st_fifo->len > 0) {
++ /* Get first entry */
++ status = st_fifo->entries[st_fifo->head].status;
++ len = st_fifo->entries[st_fifo->head].len;
++ st_fifo->pending_bytes -= len;
++ st_fifo->head++;
++ st_fifo->len--;
++
++ /* Check for errors */
++ if (status & FRM_ST_ERR_MSK) {
++ if (status & FRM_ST_LOST_FR) {
++ /* Add number of lost frames to stats */
++ self->stats.rx_errors += len;
++ } else {
++ /* Skip frame */
++ self->stats.rx_errors++;
++
++ self->rx_buff.data += len;
++
++ if (status & FRM_ST_MAX_LEN)
++ self->stats.rx_length_errors++;
++
++ if (status & FRM_ST_PHY_ERR)
++ self->stats.rx_frame_errors++;
++
++ if (status & FRM_ST_BAD_CRC)
++ self->stats.rx_crc_errors++;
++ }
++ /* The errors below can be reported in both cases */
++ if (status & FRM_ST_OVR1)
++ self->stats.rx_fifo_errors++;
++
++ if (status & FRM_ST_OVR2)
++ self->stats.rx_fifo_errors++;
++ } else {
++ /*
++ * First we must make sure that the frame we
++ * want to deliver is all in main memory. If we
++ * cannot tell, then we check if the Rx FIFO is
++ * empty. If not then we will have to take a nap
++ * and try again later.
++ */
++ if (st_fifo->pending_bytes < self->io.fifo_size) {
++ switch_bank(iobase, BANK0);
++ if (inb(iobase+LSR) & LSR_RXDA) {
++ /* Put this entry back in fifo */
++ st_fifo->head--;
++ st_fifo->len++;
++ st_fifo->pending_bytes += len;
++ st_fifo->entries[st_fifo->head].status = status;
++ st_fifo->entries[st_fifo->head].len = len;
++ /*
++ * DMA not finished yet, so try again
++ * later, set timer value, resolution
++ * 125 us
++ */
++ switch_bank(iobase, BANK4);
++ outb(0x02, iobase+TMRL); /* x 125 us */
++ outb(0x00, iobase+TMRH);
++
++ /* Start timer */
++ outb(IRCR1_TMR_EN, iobase+IRCR1);
++
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++
++ return FALSE; /* I'll be back! */
++ }
++ }
++
++ /*
++ * Remember the time we received this frame, so we can
++ * reduce the min turn time a bit since we will know
++ * how much time we have used for protocol processing
++ */
++ do_gettimeofday(&self->stamp);
++
++ skb = dev_alloc_skb(len+1);
++ if (skb == NULL) {
++ WARNING("%s(), memory squeeze, "
++ "dropping frame.\n", __FUNCTION__);
++ self->stats.rx_dropped++;
++
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++
++ return FALSE;
++ }
++
++ /* Make sure IP header gets aligned */
++ skb_reserve(skb, 1);
++
++ /* Copy frame without CRC */
++ if (self->io.speed < 4000000) {
++ skb_put(skb, len-2);
++ memcpy(skb->data, self->rx_buff.data, len-2);
++ } else {
++ skb_put(skb, len-4);
++ memcpy(skb->data, self->rx_buff.data, len-4);
++ }
++
++ /* Move to next frame */
++ self->rx_buff.data += len;
++ self->stats.rx_bytes += len;
++ self->stats.rx_packets++;
++
++ skb->dev = self->netdev;
++ skb->mac.raw = skb->data;
++ skb->protocol = htons(ETH_P_IRDA);
++ netif_rx(skb);
++ }
++ }
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++
++ return TRUE;
++}
++
++/*
++ * Function nsc_ircc_pio_receive (self)
++ *
++ * Receive all data in receiver FIFO
++ *
++ */
++static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self)
++{
++ __u8 byte;
++ int iobase;
++
++ iobase = self->io.fir_base;
++
++ /* Receive all characters in Rx FIFO */
++ do {
++ byte = inb(iobase+RXD);
++ async_unwrap_char(self->netdev, &self->stats, &self->rx_buff,
++ byte);
++ } while (inb(iobase+LSR) & LSR_RXDA); /* Data available */
++}
++
++/*
++ * Function nsc_ircc_sir_interrupt (self, eir)
++ *
++ * Handle SIR interrupt
++ *
++ */
++static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir)
++{
++ int actual;
++
++ /* Check if transmit FIFO is low on data */
++ if (eir & EIR_TXLDL_EV) {
++ /* Write data left in transmit buffer */
++ actual = nsc_ircc_pio_write(self->io.fir_base,
++ self->tx_buff.data,
++ self->tx_buff.len,
++ self->io.fifo_size);
++ self->tx_buff.data += actual;
++ self->tx_buff.len -= actual;
++
++ self->io.direction = IO_XMIT;
++
++ /* Check if finished */
++ if (self->tx_buff.len > 0)
++ self->ier = IER_TXLDL_IE;
++ else {
++
++ self->stats.tx_packets++;
++ netif_wake_queue(self->netdev);
++ self->ier = IER_TXEMP_IE;
++ }
++
++ }
++ /* Check if transmission has completed */
++ if (eir & EIR_TXEMP_EV) {
++ /* Check if we need to change the speed? */
++ if (self->new_speed) {
++ IRDA_DEBUG(2, "%s(), Changing speed!\n", __FUNCTION__);
++ nsc_ircc_change_speed(self, self->new_speed);
++ self->new_speed = 0;
++
++ /* Check if we are going to FIR */
++ if (self->io.speed > 115200) {
++ /* Should wait for status FIFO interrupt */
++ self->ier = IER_SFIF_IE;
++
++ /* No need to do anymore SIR stuff */
++ return;
++ }
++ }
++ /* Turn around and get ready to receive some data */
++ self->io.direction = IO_RECV;
++ self->ier = IER_RXHDL_IE;
++ }
++
++ /* Rx FIFO threshold or timeout */
++ if (eir & EIR_RXHDL_EV) {
++ nsc_ircc_pio_receive(self);
++
++ /* Keep receiving */
++ self->ier = IER_RXHDL_IE;
++ }
++}
++
++/*
++ * Function nsc_ircc_fir_interrupt (self, eir)
++ *
++ * Handle MIR/FIR interrupt
++ *
++ */
++static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase,
++ int eir)
++{
++ __u8 bank;
++
++ bank = inb(iobase+BSR);
++
++ /* Status FIFO event*/
++ if (eir & EIR_SFIF_EV) {
++ /* Check if DMA has finished */
++ if (nsc_ircc_dma_receive_complete(self, iobase)) {
++ /* Wait for next status FIFO interrupt */
++ self->ier = IER_SFIF_IE;
++ } else {
++ self->ier = IER_SFIF_IE | IER_TMR_IE;
++ }
++ } else if (eir & EIR_TMR_EV) { /* Timer finished */
++ /* Disable timer */
++ switch_bank(iobase, BANK4);
++ outb(0, iobase+IRCR1);
++
++ /* Clear timer event */
++ switch_bank(iobase, BANK0);
++ outb(ASCR_CTE, iobase+ASCR);
++
++ /* Check if this is a Tx timer interrupt */
++ if (self->io.direction == IO_XMIT) {
++ nsc_ircc_dma_xmit(self, iobase);
++
++ /* Interrupt on DMA */
++ self->ier = IER_DMA_IE;
++ } else {
++ /* Check (again) if DMA has finished */
++ if (nsc_ircc_dma_receive_complete(self, iobase)) {
++ self->ier = IER_SFIF_IE;
++ } else {
++ self->ier = IER_SFIF_IE | IER_TMR_IE;
++ }
++ }
++ } else if (eir & EIR_DMA_EV) {
++ /* Finished with all transmissions? */
++ if (nsc_ircc_dma_xmit_complete(self)) {
++ /* Check if there are more frames to be transmitted */
++ if (irda_device_txqueue_empty(self->netdev)) {
++ /* Prepare for receive */
++ nsc_ircc_dma_receive(self);
++
++ self->ier = IER_SFIF_IE;
++ }
++ } else {
++ /* Not finished yet, so interrupt on DMA again */
++ self->ier = IER_DMA_IE;
++ }
++ }
++ outb(bank, iobase+BSR);
++}
++
++/*
++ * Function nsc_ircc_interrupt (irq, dev_id, regs)
++ *
++ * An interrupt from the chip has arrived. Time to do some work
++ *
++ */
++static void nsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++ struct net_device *dev = (struct net_device *) dev_id;
++ struct nsc_ircc_cb *self;
++ __u8 bsr, eir;
++ int iobase;
++
++ if (!dev) {
++ WARNING("%s: irq %d for unknown device.\n", driver_name, irq);
++ return;
++ }
++ self = (struct nsc_ircc_cb *) dev->priv;
++
++ spin_lock(&self->lock);
++
++ iobase = self->io.fir_base;
++
++ bsr = inb(iobase+BSR); /* Save current bank */
++
++ switch_bank(iobase, BANK0);
++ self->ier = inb(iobase+IER);
++ eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */
++
++ outb(0, iobase+IER); /* Disable interrupts */
++
++ if (eir) {
++ /* Dispatch interrupt handler for the current speed */
++ if (self->io.speed > 115200)
++ nsc_ircc_fir_interrupt(self, iobase, eir);
++ else
++ nsc_ircc_sir_interrupt(self, eir);
++ }
++
++ outb(self->ier, iobase+IER); /* Restore interrupts */
++ outb(bsr, iobase+BSR); /* Restore bank register */
++
++ spin_unlock(&self->lock);
++}
++
++/*
++ * Function nsc_ircc_is_receiving (self)
++ *
++ * Return TRUE is we are currently receiving a frame
++ *
++ */
++static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self)
++{
++ unsigned long flags;
++ int status = FALSE;
++ int iobase;
++ __u8 bank;
++
++ ASSERT(self != NULL, return FALSE;);
++
++ spin_lock_irqsave(&self->lock, flags);
++
++ if (self->io.speed > 115200) {
++ iobase = self->io.fir_base;
++
++ /* Check if rx FIFO is not empty */
++ bank = inb(iobase+BSR);
++ switch_bank(iobase, BANK2);
++ if ((inb(iobase+RXFLV) & 0x3f) != 0) {
++ /* We are receiving something */
++ status = TRUE;
++ }
++ outb(bank, iobase+BSR);
++ } else
++ status = (self->rx_buff.state != OUTSIDE_FRAME);
++
++ spin_unlock_irqrestore(&self->lock, flags);
++
++ return status;
++}
++
++/*
++ * Function nsc_ircc_net_init (dev)
++ *
++ * Initialize network device
++ *
++ */
++static int nsc_ircc_net_init(struct net_device *dev)
++{
++ IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
++
++ /* Setup to be a normal IrDA network device driver */
++ irda_device_setup(dev);
++
++ /* Insert overrides below this line! */
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_net_open (dev)
++ *
++ * Start the device
++ *
++ */
++static int nsc_ircc_net_open(struct net_device *dev)
++{
++ struct nsc_ircc_cb *self;
++ int iobase;
++ char hwname[32];
++ __u8 bank;
++
++ IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
++
++ ASSERT(dev != NULL, return -1;);
++ self = (struct nsc_ircc_cb *) dev->priv;
++
++ ASSERT(self != NULL, return 0;);
++
++ iobase = self->io.fir_base;
++
++ if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) {
++ WARNING("%s, unable to allocate irq=%d\n", driver_name,
++ self->io.irq);
++ return -EAGAIN;
++ }
++ /*
++ * Always allocate the DMA channel after the IRQ, and clean up on
++ * failure.
++ */
++ if (request_dma(self->io.dma, dev->name)) {
++ WARNING("%s, unable to allocate dma=%d\n", driver_name,
++ self->io.dma);
++ free_irq(self->io.irq, dev);
++ return -EAGAIN;
++ }
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ /* turn on interrupts */
++ switch_bank(iobase, BANK0);
++ outb(IER_LS_IE | IER_RXHDL_IE, iobase+IER);
++
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++
++ /* Ready to play! */
++ netif_start_queue(dev);
++
++ /* Give self a hardware name */
++ sprintf(hwname, "NSC-FIR @ 0x%03x", self->io.fir_base);
++
++ /*
++ * Open new IrLAP layer instance, now that everything should be
++ * initialized properly
++ */
++ self->irlap = irlap_open(dev, &self->qos, hwname);
++
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_net_close (dev)
++ *
++ * Stop the device
++ *
++ */
++static int nsc_ircc_net_close(struct net_device *dev)
++{
++ struct nsc_ircc_cb *self;
++ int iobase;
++ __u8 bank;
++
++ IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
++
++ ASSERT(dev != NULL, return -1;);
++
++ self = (struct nsc_ircc_cb *) dev->priv;
++ ASSERT(self != NULL, return 0;);
++
++ /* Stop device */
++ netif_stop_queue(dev);
++
++ /* Stop and remove instance of IrLAP */
++ if (self->irlap)
++ irlap_close(self->irlap);
++ self->irlap = NULL;
++
++ iobase = self->io.fir_base;
++
++ disable_dma(self->io.dma);
++
++ /* Save current bank */
++ bank = inb(iobase+BSR);
++
++ /* Disable interrupts */
++ switch_bank(iobase, BANK0);
++ outb(0, iobase+IER);
++
++ free_irq(self->io.irq, dev);
++ free_dma(self->io.dma);
++
++ /* Restore bank register */
++ outb(bank, iobase+BSR);
++
++ MOD_DEC_USE_COUNT;
++
++ return 0;
++}
++
++/*
++ * Function nsc_ircc_net_ioctl (dev, rq, cmd)
++ *
++ * Process IOCTL commands for this device
++ *
++ */
++static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
++{
++ struct if_irda_req *irq = (struct if_irda_req *) rq;
++ struct nsc_ircc_cb *self;
++ unsigned long flags;
++ int ret = 0;
++
++ ASSERT(dev != NULL, return -1;);
++
++ self = dev->priv;
++
++ ASSERT(self != NULL, return -1;);
++
++ IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
++
++ /* Disable interrupts & save flags */
++ save_flags(flags);
++ cli();
++
++ switch (cmd) {
++ case SIOCSBANDWIDTH: /* Set bandwidth */
++ if (!capable(CAP_NET_ADMIN)) {
++ ret = -EPERM;
++ goto out;
++ }
++ nsc_ircc_change_speed(self, irq->ifr_baudrate);
++ break;
++ case SIOCSMEDIABUSY: /* Set media busy */
++ if (!capable(CAP_NET_ADMIN)) {
++ ret = -EPERM;
++ goto out;
++ }
++ irda_device_set_media_busy(self->netdev, TRUE);
++ break;
++ case SIOCGRECEIVING: /* Check if we are receiving right now */
++ irq->ifr_receiving = nsc_ircc_is_receiving(self);
++ break;
++ default:
++ ret = -EOPNOTSUPP;
++ }
++out:
++ restore_flags(flags);
++ return ret;
++}
++
++static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev)
++{
++ struct nsc_ircc_cb *self = (struct nsc_ircc_cb *) dev->priv;
++
++ return &self->stats;
++}
++
++static void nsc_ircc_suspend(struct nsc_ircc_cb *self)
++{
++ MESSAGE("%s, Suspending\n", driver_name);
++
++ if (self->io.suspended)
++ return;
++
++ nsc_ircc_net_close(self->netdev);
++
++ self->io.suspended = 1;
++}
++
++static void nsc_ircc_wakeup(struct nsc_ircc_cb *self)
++{
++ if (!self->io.suspended)
++ return;
++
++ nsc_ircc_setup(&self->io);
++ nsc_ircc_net_open(self->netdev);
++
++ MESSAGE("%s, Waking up\n", driver_name);
++
++ self->io.suspended = 0;
++}
++
++static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++ struct nsc_ircc_cb *self = (struct nsc_ircc_cb*) dev->data;
++ if (self) {
++ switch (rqst) {
++ case PM_SUSPEND:
++ nsc_ircc_suspend(self);
++ break;
++ case PM_RESUME:
++ nsc_ircc_wakeup(self);
++ break;
++ }
++ }
++ return 0;
++}
++
++#ifdef MODULE
++MODULE_AUTHOR("Dag Brattli <dagb at cs.uit.no>");
++MODULE_DESCRIPTION("NSC IrDA Device Driver");
++MODULE_LICENSE("GPL");
++
++
++MODULE_PARM(qos_mtt_bits, "i");
++MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");
++MODULE_PARM(io, "1-4i");
++MODULE_PARM_DESC(io, "Base I/O addresses");
++MODULE_PARM(irq, "1-4i");
++MODULE_PARM_DESC(irq, "IRQ lines");
++MODULE_PARM(dma, "1-4i");
++MODULE_PARM_DESC(dma, "DMA channels");
++MODULE_PARM(dongle_id, "i");
++MODULE_PARM_DESC(dongle_id, "Type-id of used dongle");
++
++int init_module(void)
++{
++ return nsc_ircc_init();
++}
++
++void cleanup_module(void)
++{
++ nsc_ircc_cleanup();
++}
++#endif /* MODULE */
++
+diff -urN kernel-source-2.4.27-8/drivers/net/irda/sa1100_ir.c kernel-source-2.4.27-8-arm-1/drivers/net/irda/sa1100_ir.c
+--- kernel-source-2.4.27-8/drivers/net/irda/sa1100_ir.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/irda/sa1100_ir.c 2005-02-18 17:48:43.000000000 +0000
+@@ -38,11 +38,7 @@
+
+ #include <asm/arch/assabet.h>
+
+-#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 kernel-source-2.4.27-8/drivers/net/irda/w83977af_ir.c kernel-source-2.4.27-8-arm-1/drivers/net/irda/w83977af_ir.c
+--- kernel-source-2.4.27-8/drivers/net/irda/w83977af_ir.c 2002-11-28 23:53:13.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/net/irda/w83977af_ir.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/net/ne2k-bast.c kernel-source-2.4.27-8-arm-1/drivers/net/ne2k-bast.c
+--- kernel-source-2.4.27-8/drivers/net/ne2k-bast.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/ne2k-bast.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,647 @@
++/* linux/drivers/net/ne2k-bast.c
++ *
++ *
++ * based on: ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */
++
++/*
++ A Linux device driver for BAST onboard ASIX ethernet
++
++ Authors and other copyright holders:
++ 2003 by Ben Dooks, Modifies PCI NE2000 support for BAST onboard
++ ASIX fast-ethernet controller
++
++ 1992-2000 by Donald Becker, NE2000 core and various modifications.
++ 1995-1998 by Paul Gortmaker, core modifications and PCI support.
++ Copyright 1993 assigned to the United States Government as represented
++ by the Director, National Security Agency.
++
++ This software may be used and distributed according to the terms of
++ the GNU General Public License (GPL), incorporated herein by reference.
++ Drivers based on or derived from this code fall under the GPL and must
++ retain the authorship, copyright and license notice. This file is not
++ a complete program and may only be used when the entire operating
++ system is licensed under the GPL.
++
++ The author may be reached as becker at scyld.com, or C/O
++ Scyld Computing Corporation
++ 410 Severn Ave., Suite 210
++ Annapolis MD 21403
++
++ Issues remaining:
++ People are making any ne2000 clones! Oh the horror, the horror...
++ Limited full-duplex support.
++*/
++
++#define DRV_NAME "bast-asix"
++#define DRV_VERSION "1.02"
++#define DRV_RELDATE "21/05/2003"
++
++#include "8390-bast.h"
++
++/* The user-configurable values.
++ These may be modified when a driver module is loaded.*/
++
++static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
++
++#define MAX_UNITS 1 /* More are supported, limit only on options */
++/* Used to pass the full-duplex flag, etc. */
++
++#if 0
++static int full_duplex[MAX_UNITS];
++static int options[MAX_UNITS];
++#endif
++
++/* Force a non std. amount of memory. Units are 256 byte pages. */
++/* #define PACKETBUF_MEMSIZE 0x40 */
++
++
++#if 1
++/* define the outsl/insl as the faster multi-word variants, as this
++ * card supports this IO format.
++*/
++
++extern void __raw_m4_writesl(unsigned long reg, void *data, unsigned int size);
++extern void __raw_m4_readsl(unsigned long reg, void *data, unsigned int size);
++
++#define _outsl __raw_m4_writesl
++#define _insl __raw_m4_readsl
++#else
++#define _outsl outsl
++#define _insl insl
++#endif
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/ethtool.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++
++#define COMPILE_BAST
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include "8390.h"
++
++#include <asm/arch/map.h>
++
++
++/* These identify the driver base version and may not be removed. */
++static char version[] __devinitdata =
++KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " D. Becker/P. Gortmaker/Ben Dooks\n"
++KERN_INFO " http://www.simtec.co.uk/products/EB2410ITX/\n";
++
++
++#define PFX DRV_NAME ": "
++
++MODULE_AUTHOR("Donald Becker / Paul Gortmaker / Ben Dooks");
++MODULE_DESCRIPTION("ASIX NE2000 (BAST Onboard ethernet)");
++MODULE_LICENSE("GPL");
++
++MODULE_PARM(debug, "i");
++MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
++MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
++MODULE_PARM_DESC(debug, "debug level (1-2)");
++MODULE_PARM_DESC(options, "Bit 5: full duplex");
++MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)");
++
++/* Some defines that people can play with if so inclined. */
++
++/* Use 32 bit data-movement operations instead of 16 bit. */
++#define USE_LONGIO
++
++/* Do we implement the read before write bugfix ? */
++/* #define NE_RW_BUGFIX */
++
++/* Flags. We rename an existing ei_status field to store flags! */
++/* Thus only the low 8 bits are usable for non-init-time flags. */
++#define ne2k_flags reg0
++enum {
++ ONLY_16BIT_IO=8, ONLY_32BIT_IO=4, /* Chip can do only 16/32-bit xfers. */
++ FORCE_FDX=0x20, /* User override. */
++ STOP_PG_0x60=0x100,
++};
++
++/* ---- No user-serviceable parts below ---- */
++
++#define NE_BASE (dev->base_addr)
++#define NE_CMD EI_SHIFT(0x00)
++#define NE_DATAPORT EI_SHIFT(0x10)
++#define NE_RESET EI_SHIFT(0x1f) /* Issue a read to reset, a write to clear. */
++#define NE_IO_EXTENT EI_SHIFT(0x20)
++
++#define NESM_START_PG 0x40 /* First page of TX buffer */
++#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
++
++
++static int net_bast_open(struct net_device *dev);
++static int net_bast_close(struct net_device *dev);
++
++static void net_bast_reset_8390(struct net_device *dev);
++static void net_bast_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
++ int ring_page);
++static void net_bast_block_input(struct net_device *dev, int count,
++ struct sk_buff *skb, int ring_offset);
++static void net_bast_block_output(struct net_device *dev, const int count,
++ const unsigned char *buf, const int start_page);
++static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
++
++
++
++/* There is no room in the standard 8390 structure for extra info we need,
++ so we build a meta/outer-wrapper structure.. */
++struct net_bast_card {
++ struct net_device *dev;
++};
++
++/* io routines to deal with the register spacing on the BAST onboard
++ * controller... these have an real advantage for other operations
++ */
++
++static inline void
++ax_outb(unsigned int val, unsigned long iobase, unsigned int reg)
++{
++#if defined(CONFIG_NET_NE2KBAST_REGDEBUG)
++ if (reg != 0 && reg < 32)
++ printk("ax_outb(%02x,%08x,%04x)\n", val, iobase, reg);
++#endif
++ __raw_writeb(val, iobase + (reg));
++}
++
++static inline unsigned char
++ax_inb(unsigned long iobase, unsigned int reg)
++{
++#if defined(CONFIG_NET_NE2KBAST_REGDEBUG)
++ if (reg != 0 && reg < 32)
++ printk("ax_inb(%08x,%04x)\n", iobase, reg);
++#endif
++ return __raw_readb(iobase + (reg));
++}
++
++/*
++ NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet
++ buffer memory space. By-the-spec NE2000 clones have 0x57,0x57 in bytes
++ 0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be
++ detected by their SA prefix.
++
++ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
++ mode results in doubled values, which can be detected and compensated for.
++
++ The probe is also responsible for initializing the card and filling
++ in the 'dev' and 'ei_status' structures.
++*/
++
++static int initialised = 0;
++
++int __devinit ne_bast_probe (struct net_device *dev)
++{
++ int i;
++ unsigned char SA_prom[32];
++ int start_page, stop_page;
++ int irq, reg0;
++ unsigned long ioaddr;
++
++/* when built into the kernel, we only print version if device is found */
++#ifndef MODULE
++ static int printed_version;
++ if (!printed_version++)
++ printk(version);
++#endif
++
++ printk("BAST Onboard NE2000 driver, (c) 2003 Simtec Electronics\n");
++
++ if (initialised) {
++ return -ENODEV;
++ }
++ initialised = 1;
++
++ /* configure ioaddr, and make sure it is the fast version */
++
++ ioaddr = BAST_VA_ASIXNET + BAST_ASIXNET_CS;
++
++ irq = IRQ_ASIX;
++
++ if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) {
++ printk (KERN_ERR PFX "I/O resource 0x%x @ 0x%lx busy\n",
++ NE_IO_EXTENT, ioaddr);
++ return -EBUSY;
++ }
++
++ reg0 = ax_inb(ioaddr, 0x00);
++ if (reg0 == 0xFF)
++ goto err_out_free_res;
++
++ /* Do a preliminary verification that we have a 8390. */
++ {
++ int regd;
++ ax_outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr, E8390_CMD);
++ regd = ax_inb(ioaddr, EI_SHIFT(0x0d));
++ ax_outb(0xff, ioaddr, EI_SHIFT(0x0d));
++ ax_outb(E8390_NODMA+E8390_PAGE0, ioaddr, E8390_CMD);
++ ax_inb(ioaddr, EN0_COUNTER0); /* Clear the counter by reading. */
++ if (ax_inb(ioaddr, EN0_COUNTER0) != 0) {
++ printk(__FUNCTION__ ": EN0_COUNTER0 did not clear (%02x)\n", ax_inb(ioaddr, EN0_COUNTER0));
++ ax_outb(reg0, ioaddr, EI_SHIFT(0));
++ ax_outb(regd, ioaddr, EI_SHIFT(0x0d)); /* Restore the old values. */
++ goto err_out_free_res;
++ }
++ }
++
++ SET_MODULE_OWNER(dev);
++
++ /* Reset card. Who knows what dain-bramaged state it was left in. */
++ {
++ unsigned long reset_start_time = jiffies;
++
++ ax_outb(ax_inb(ioaddr, NE_RESET), ioaddr, NE_RESET);
++
++ /* This looks like a horrible timing loop, but it should never take
++ more than a few cycles.
++ */
++ while ((ax_inb(ioaddr, EN0_ISR) & ENISR_RESET) == 0)
++ /* Limit wait: '2' avoids jiffy roll-over. */
++ if (jiffies - reset_start_time > 4) {
++ printk(KERN_ERR PFX "Card failure (no reset ack).\n");
++ goto err_out_free_netdev;
++ }
++
++ ax_outb(0xff, ioaddr, EN0_ISR); /* Ack all intr. */
++ }
++
++ /* Read the 16 bytes of station address PROM.
++ We must first initialize registers, similar to NS8390_init(eifdev, 0).
++ We can't reliably read the SAPROM address without this.
++ (I learned the hard way!). */
++ {
++ struct {
++ unsigned char value;
++ unsigned int offset; } program_seq[] = {
++ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
++ {0x49, EN0_DCFG}, /* Set word-wide access. */
++ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
++ {0x00, EN0_RCNTHI},
++ {0x00, EN0_IMR}, /* Mask completion irq. */
++ {0xFF, EN0_ISR},
++ {E8390_RXOFF|0x40, EN0_RXCR}, /* 0x20 Set to monitor and irq high */
++ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
++ {32, EN0_RCNTLO},
++ {0x00, EN0_RCNTHI},
++ {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
++ {0x00, EN0_RSARHI},
++ {E8390_RREAD+E8390_START, E8390_CMD},
++ };
++ for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
++ ax_outb(program_seq[i].value, ioaddr, program_seq[i].offset);
++
++ }
++
++ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++)
++ SA_prom[i] = ax_inb(ioaddr, NE_DATAPORT);
++
++ /* We always set the 8390 registers for word mode. */
++ ax_outb(0x49, ioaddr, EN0_DCFG);
++ start_page = NESM_START_PG;
++ stop_page = NESM_STOP_PG;
++
++ /* Set up the rest of the parameters. */
++ dev->irq = irq;
++ dev->base_addr = ioaddr;
++
++ /* Allocate dev->priv and fill in 8390 specific dev fields. */
++ if (bast_ethdev_init(dev)) {
++ printk (KERN_ERR "ne2k-bast: no memory for dev->priv.\n");
++ goto err_out_free_netdev;
++ }
++
++ ei_status.name = "bast";
++ ei_status.tx_start_page = start_page;
++ ei_status.stop_page = stop_page;
++ ei_status.word16 = 1;
++
++#if 0
++ if (fnd_cnt < MAX_UNITS) {
++ if (full_duplex[fnd_cnt] > 0 || (options[fnd_cnt] & FORCE_FDX))
++ ei_status.net_flags |= FORCE_FDX;
++ }
++#endif
++
++ ei_status.rx_start_page = start_page + TX_PAGES;
++#ifdef PACKETBUF_MEMSIZE
++ /* Allow the packet buffer size to be overridden by know-it-alls. */
++ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
++#endif
++
++ ei_status.reset_8390 = &net_bast_reset_8390;
++ ei_status.block_input = &net_bast_block_input;
++ ei_status.block_output = &net_bast_block_output;
++ ei_status.get_8390_hdr = &net_bast_get_8390_hdr;
++ dev->open = &net_bast_open;
++ dev->stop = &net_bast_close;
++ dev->do_ioctl = &netdev_ioctl;
++ bast_NS8390_init(dev, 0);
++
++#if 0
++ i = register_netdev(dev);
++ if (i) {
++ printk(KERN_ERR PFX "cannot register network device\n");
++ goto err_out_free_8390;
++ }
++#endif
++
++ printk("%s: bast-ne2k found at %#lx, IRQ %d, ",
++ dev->name, ioaddr, dev->irq);
++
++ for(i = 0; i < 6; i++) {
++ dev->dev_addr[i] = i*2;
++ printk("%2.2X%s", dev->dev_addr[i], i == 5 ? ".\n": ":");
++ }
++
++ return 0;
++
++err_out_free_8390:
++ kfree(dev->priv);
++ dev->priv = 0;
++err_out_free_netdev:
++err_out_free_res:
++ release_region (ioaddr, NE_IO_EXTENT);
++ printk(__FUNCTION__ ": exiting with -ENODEV\n");
++ return -ENODEV;
++
++}
++
++static int net_bast_open(struct net_device *dev)
++{
++ int ret = request_irq(dev->irq, ei_interrupt,
++ SA_SHIRQ, dev->name, dev);
++ if (ret)
++ return ret;
++
++ /* Set full duplex for the chips that we know about. */
++
++ ei_open(dev);
++ return 0;
++}
++
++static int net_bast_close(struct net_device *dev)
++{
++ ei_close(dev);
++ free_irq(dev->irq, dev);
++ return 0;
++}
++
++/* Hard reset the card. This used to pause for the same period that a
++ 8390 reset command required, but that shouldn't be necessary. */
++static void net_bast_reset_8390(struct net_device *dev)
++{
++ unsigned long reset_start_time = jiffies;
++
++ if (debug > 1) printk("%s: Resetting the 8390 t=%ld...",
++ dev->name, jiffies);
++
++ ax_outb(ax_inb(NE_BASE, NE_RESET), NE_BASE, NE_RESET);
++
++ ei_status.txing = 0;
++ ei_status.dmaing = 0;
++
++ /* This check _should_not_ be necessary, omit eventually. */
++ while ((ax_inb(NE_BASE,EN0_ISR) & ENISR_RESET) == 0)
++ if (jiffies - reset_start_time > 2) {
++ printk("%s: net_bast_reset_8390() did not complete.\n", dev->name);
++ break;
++ }
++ ax_outb(ENISR_RESET, NE_BASE, EN0_ISR); /* Ack intr. */
++}
++
++/* Grab the 8390 specific header. Similar to the block_input routine, but
++ we don't need to be concerned with ring wrap as the header will be at
++ the start of a page, so we optimize accordingly. */
++
++static void net_bast_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
++{
++
++ long nic_base = dev->base_addr;
++
++ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
++ if (ei_status.dmaing) {
++ printk("%s: DMAing conflict in net_bast_get_8390_hdr "
++ "[DMAstat:%d][irqlock:%d].\n",
++ dev->name, ei_status.dmaing, ei_status.irqlock);
++ return;
++ }
++
++ ei_status.dmaing |= 0x01;
++ ax_outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base, NE_CMD);
++ ax_outb(sizeof(struct e8390_pkt_hdr), nic_base, EN0_RCNTLO);
++ ax_outb(0, nic_base, EN0_RCNTHI);
++ ax_outb(0, nic_base, EN0_RSARLO); /* On page boundary */
++ ax_outb(ring_page, nic_base, EN0_RSARHI);
++ ax_outb(E8390_RREAD+E8390_START, nic_base, NE_CMD);
++
++ insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
++
++ ax_outb(ENISR_RDC, nic_base, EN0_ISR); /* Ack intr. */
++ ei_status.dmaing &= ~0x01;
++}
++
++/* Block input and output, similar to the Crynwr packet driver. If you
++ are porting to a new ethercard, look at the packet driver source for hints.
++ The NEx000 doesn't share the on-board packet memory -- you have to put
++ the packet out through the "remote DMA" dataport using ax_outb. */
++
++static void net_bast_block_input(struct net_device *dev, int count,
++ struct sk_buff *skb, int ring_offset)
++{
++ long nic_base = dev->base_addr;
++ unsigned char *buf = (unsigned char *)skb->data;
++ unsigned long tmp;
++
++ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
++ if (ei_status.dmaing) {
++ printk("%s: DMAing conflict in net_bast_block_input "
++ "[DMAstat:%d][irqlock:%d].\n",
++ dev->name, ei_status.dmaing, ei_status.irqlock);
++ return;
++ }
++ ei_status.dmaing |= 0x01;
++ ax_outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base, NE_CMD);
++ ax_outb(count & 0xff, nic_base, EN0_RCNTLO);
++ ax_outb(count >> 8, nic_base, EN0_RCNTHI);
++ ax_outb(ring_offset & 0xff, nic_base, EN0_RSARLO);
++ ax_outb(ring_offset >> 8, nic_base, EN0_RSARHI);
++ ax_outb(E8390_RREAD+E8390_START, nic_base, NE_CMD);
++
++#if 1
++ /* ensure that we have an aligned buffer... */
++ if ((((unsigned int)buf) & 3) == 2) {
++ *((unsigned short *)buf) = __raw_readw(NE_BASE + NE_DATAPORT);
++ count -= 2;
++ buf += 2;
++ }
++
++ _insl(NE_BASE + NE_DATAPORT,buf,count>>2);
++
++ /* mop up any remaining bytes */
++ switch (count & 0x3) {
++ case 0x03:
++ tmp = __raw_readl(NE_BASE + NE_DATAPORT);
++ buf[count-3] = tmp;
++ buf[count-2] = tmp>>8;
++ buf[count-1] = tmp>>16;
++ break;
++
++ case 0x02:
++ tmp = __raw_readw(NE_BASE + NE_DATAPORT);
++ buf[count-2] = tmp;
++ buf[count-1] = tmp>>8;
++ break;
++
++ case 0x01:
++ buf[count-1] = __raw_readw(NE_BASE + NE_DATAPORT);
++ break;
++ }
++#else
++ insw(NE_BASE + NE_DATAPORT, buf, (count+1)>>1);
++#endif
++
++ ax_outb(ENISR_RDC, nic_base, EN0_ISR); /* Ack intr. */
++ ei_status.dmaing &= ~0x01;
++}
++
++static void net_bast_block_output(struct net_device *dev, int count,
++ const unsigned char *buf, const int start_page)
++{
++ long nic_base = NE_BASE;
++ unsigned short *bufptr = (unsigned short *)buf;
++ unsigned long dma_start;
++ int rcount;
++
++ /* On little-endian it's always safe to round the count up for
++ word writes. */
++
++ rcount = ((count + 3) & ~0x03);
++
++ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
++ if (ei_status.dmaing) {
++ printk("%s: DMAing conflict in net_bast_block_output."
++ "[DMAstat:%d][irqlock:%d]\n",
++ dev->name, ei_status.dmaing, ei_status.irqlock);
++ return;
++ }
++ ei_status.dmaing |= 0x01;
++ /* We should already be in page 0, but to be safe... */
++ ax_outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base, NE_CMD);
++
++#ifdef NE8390_RW_BUGFIX
++ /* Handle the read-before-write bug the same way as the
++ Crynwr packet driver -- the NatSemi method doesn't work.
++ Actually this doesn't always work either, but if you have
++ problems with your NEx000 this is better than nothing! */
++ ax_outb(0x42, nic_base, EN0_RCNTLO);
++ ax_outb(0x00, nic_base, EN0_RCNTHI);
++ ax_outb(0x42, nic_base, EN0_RSARLO);
++ ax_outb(0x00, nic_base, EN0_RSARHI);
++ ax_outb(E8390_RREAD+E8390_START, nic_base, NE_CMD);
++#endif
++ ax_outb(ENISR_RDC, nic_base, EN0_ISR);
++
++ /* Now the normal output. */
++ ax_outb(count & 0xff, nic_base, EN0_RCNTLO);
++ ax_outb(count >> 8, nic_base, EN0_RCNTHI);
++ ax_outb(0x00, nic_base, EN0_RSARLO);
++ ax_outb(start_page, nic_base, EN0_RSARHI);
++ ax_outb(E8390_RWRITE+E8390_START, nic_base, NE_CMD);
++
++ if (((unsigned int)bufptr & 3) == 2) {
++ __raw_writew(*bufptr, NE_BASE + NE_DATAPORT);
++ bufptr++;
++ }
++
++ _outsl(NE_BASE + NE_DATAPORT, bufptr, rcount>>2);
++
++ dma_start = jiffies;
++
++ while ((ax_inb(nic_base, EN0_ISR) & ENISR_RDC) == 0)
++ if (jiffies - dma_start > 2) { /* Avoid clock roll-over. */
++ printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
++ net_bast_reset_8390(dev);
++ bast_NS8390_init(dev,1);
++ break;
++ }
++
++ ax_outb(ENISR_RDC, nic_base, EN0_ISR); /* Ack intr. */
++ ei_status.dmaing &= ~0x01;
++ return;
++}
++
++static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
++{
++ struct ei_device *ei = dev->priv;
++ u32 ethcmd;
++
++ if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
++ return -EFAULT;
++
++ switch (ethcmd) {
++ case ETHTOOL_GDRVINFO: {
++ struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
++ strcpy(info.driver, DRV_NAME);
++ strcpy(info.version, DRV_VERSION);
++ strcpy(info.bus_info, "bast");
++ if (copy_to_user(useraddr, &info, sizeof(info)))
++ return -EFAULT;
++ return 0;
++ }
++
++ }
++
++ return -EOPNOTSUPP;
++}
++
++static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
++{
++ switch(cmd) {
++ case SIOCETHTOOL:
++ return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
++ default:
++ return -EOPNOTSUPP;
++ }
++}
++
++
++static int __init net_bast_modinit(void)
++{
++ struct net_device *dev;
++/* when a module, this is printed whether or not devices are found in probe */
++#if defined(MODULE) || 1
++ printk(version);
++#endif
++
++ dev = (struct net_device *)kmalloc(sizeof(*dev), 0);
++ printk("net_bast_modinit: new device at %p\n", dev);
++
++ if (dev != NULL) {
++ memset(dev, 0, sizeof(*dev));
++
++ dev->init = ne_bast_probe;
++ if (register_netdev(dev) != 0) {
++ printk("failed to initialise driver\n");
++ return -ENXIO;
++ }
++ }
++
++ return 0;
++}
++
++
++static void __exit net_bast_modcleanup(void)
++{
++ /* todo: cleanup */
++}
++
++module_init(net_bast_modinit);
++module_exit(net_bast_modcleanup);
+diff -urN kernel-source-2.4.27-8/drivers/net/smc9194.c kernel-source-2.4.27-8-arm-1/drivers/net/smc9194.c
+--- kernel-source-2.4.27-8/drivers/net/smc9194.c 2003-06-13 15:51:35.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/smc9194.c 2005-02-18 17:48:43.000000000 +0000
+@@ -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 at vt.edu)\n";
++ DRV_NAME ".c:v" DRV_VERSION " 12/15/00 by Erik Stahlman (erik at vt.edu)\n";
+
++#include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/version.h>
+ #include <linux/kernel.h>
+@@ -69,16 +78,26 @@
+ #include <linux/in.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
++#include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/crc32.h>
+-#include <asm/bitops.h>
+-#include <asm/io.h>
+ #include <linux/errno.h>
++#include <linux/ethtool.h>
+
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/skbuff.h>
+
++#include <asm/bitops.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++
++#ifdef CONFIG_ARCH_SA1100
++#include <asm/hardware.h>
++#include <asm/arch/assabet.h>
++#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,10 +753,11 @@
+ /* 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;
+ }
+
+@@ -500,15 +777,15 @@
+
+ /*
+ ** 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;
+
+- 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);
+@@ -521,8 +798,8 @@
+ 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,18 +816,18 @@
+ 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 );
++ 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;
+ }
+@@ -561,46 +838,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;
++ struct sk_buff *skb = lp->saved_skb;
++ word length, lastword;
++ u_int ioaddr = dev->base_addr;
+ byte packet_no;
+- struct sk_buff * skb = lp->saved_skb;
+- word length;
+- unsigned short ioaddr;
+- byte * buf;
++ byte *buf;
+
+- ioaddr = dev->base_addr;
+-
+- 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 +885,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 +906,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 +936,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 +951,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 +1027,8 @@
+ return 0;
+
+ /* couldn't find anything */
+- return -ENODEV;
++#endif
++ return ret;
+ }
+
+ /*----------------------------------------------------------------------
+@@ -718,10 +1038,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 +1058,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 +1095,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 +1105,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 +1215,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 +1231,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,117 +1259,100 @@
+ 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.
++ * Ok, now that we have everything in a
++ * sane state, probe for the interrupt.
+ */
+- 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;
++ 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!!!!
+- .
++ * Get the interface characteristics.
++ * is it using AUI or 10BaseT ?
+ */
+- if ( dev->irq < 2 ) {
+- int trials;
++ switch (dev->if_port) {
++ case IF_PORT_10BASET:
++ smc->port = PORT_TP;
++ break;
+
+- trials = 3;
+- while ( trials-- ) {
+- dev->irq = smc_findirq( ioaddr );
+- if ( dev->irq )
++ case IF_PORT_AUI:
++ smc->port = PORT_AUI;
+ 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);
+@@ -1014,6 +1374,7 @@
+ 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 +1383,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;
+-
+- for ( i = 0; i < lines ; i ++ ) {
+- int cur;
++ struct smc_local *smc = dev->priv;
++ u_int ioaddr = dev->base_addr;
++ u_int val;
+
+- 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 +1429,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
+ 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 +1575,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;
+@@ -1148,7 +1601,7 @@
+ 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 +1614,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,52 +1661,55 @@
+
+ /* 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;
+ }
+
+@@ -1261,7 +1717,7 @@
+ .
+ . 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 +1728,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 +1788,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 +1805,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,15 +1828,15 @@
+ . 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;
+@@ -1406,45 +1845,47 @@
+
+ /* 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 +1901,7 @@
+ {
+ netif_stop_queue(dev);
+ /* clear everything */
+- smc_shutdown( dev->base_addr );
++ smc_shutdown(dev);
+
+ /* Update the statistics here. */
+ return 0;
+@@ -1481,16 +1922,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 +1943,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 );
++ 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 +1991,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;
+
+- return 0;
++ return register_netdev(&devSMC9194);
+ }
+
+ void cleanup_module(void)
+diff -urN kernel-source-2.4.27-8/drivers/net/smc9194.h kernel-source-2.4.27-8-arm-1/drivers/net/smc9194.h
+--- kernel-source-2.4.27-8/drivers/net/smc9194.h 2001-09-08 20:13:55.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/net/smc9194.h 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/parport/Config.in kernel-source-2.4.27-8-arm-1/drivers/parport/Config.in
+--- kernel-source-2.4.27-8/drivers/parport/Config.in 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/parport/Config.in 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/parport/Makefile kernel-source-2.4.27-8-arm-1/drivers/parport/Makefile
+--- kernel-source-2.4.27-8/drivers/parport/Makefile 2004-08-08 00:26:05.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/parport/Makefile 2005-02-18 17:48:43.000000000 +0000
+@@ -28,6 +28,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 kernel-source-2.4.27-8/drivers/parport/init.c kernel-source-2.4.27-8-arm-1/drivers/parport/init.c
+--- kernel-source-2.4.27-8/drivers/parport/init.c 2002-11-28 23:53:14.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/parport/init.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/parport/parport_idp.c kernel-source-2.4.27-8-arm-1/drivers/parport/parport_idp.c
+--- kernel-source-2.4.27-8/drivers/parport/parport_idp.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/parport/parport_idp.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,247 @@
++/* Low-level polled-mode parallel port routines for the Accelent IDP
++ *
++ * Author: Rich Dulabahn <rich at accelent.com>
++ *
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/parport.h>
++#include <asm/hardware.h>
++
++/*
++ * 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 kernel-source-2.4.27-8/drivers/pci/Makefile kernel-source-2.4.27-8-arm-1/drivers/pci/Makefile
+--- kernel-source-2.4.27-8/drivers/pci/Makefile 2003-08-25 12:44:42.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pci/Makefile 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pci/bridge.c kernel-source-2.4.27-8-arm-1/drivers/pci/bridge.c
+--- kernel-source-2.4.27-8/drivers/pci/bridge.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pci/bridge.c 2005-02-18 17:48:43.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 <arjanv at redhat.com>
++ *
++ */
++
++
++/*
++ * Generic PCI driver for PCI bridges for powermanagement purposes
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++
++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 kernel-source-2.4.27-8/drivers/pci/pci.c kernel-source-2.4.27-8-arm-1/drivers/pci/pci.c
+--- kernel-source-2.4.27-8/drivers/pci/pci.c 2004-08-08 00:26:05.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pci/pci.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pci/setup-bus.c kernel-source-2.4.27-8-arm-1/drivers/pci/setup-bus.c
+--- kernel-source-2.4.27-8/drivers/pci/setup-bus.c 2003-06-13 15:51:35.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pci/setup-bus.c 2005-02-18 17:48:43.000000000 +0000
+@@ -12,6 +12,8 @@
+ /*
+ * Nov 2000, Ivan Kokshaysky <ink at jurassic.park.msu.ru>
+ * PCI-PCI bridges cleanup, sorted resource allocation.
++ * May 2001, Russell King <rmk at arm.linux.org.uk>
++ * Allocate prefetchable memory regions where available.
+ * Feb 2002, Ivan Kokshaysky <ink at jurassic.park.msu.ru>
+ * 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 kernel-source-2.4.27-8/drivers/pcmcia/Config.in kernel-source-2.4.27-8-arm-1/drivers/pcmcia/Config.in
+--- kernel-source-2.4.27-8/drivers/pcmcia/Config.in 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/Config.in 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/Makefile kernel-source-2.4.27-8-arm-1/drivers/pcmcia/Makefile
+--- kernel-source-2.4.27-8/drivers/pcmcia/Makefile 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/Makefile 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/cistpl.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/cistpl.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/cistpl.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/cistpl.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/clps6700.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/clps6700.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/clps6700.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/clps6700.c 2005-02-18 17:48:43.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 <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/proc_fs.h>
++#include <linux/spinlock.h>
++#include <linux/init.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/page.h>
++
++#include <asm/arch/syspld.h>
++#include <asm/hardware/clps7111.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/ss.h>
++
++#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 kernel-source-2.4.27-8/drivers/pcmcia/clps6700.h kernel-source-2.4.27-8-arm-1/drivers/pcmcia/clps6700.h
+--- kernel-source-2.4.27-8/drivers/pcmcia/clps6700.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/clps6700.h 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/cs.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/cs.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/cs.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/cs.c 2005-02-18 17:48:43.000000000 +0000
+@@ -679,6 +679,12 @@
+ 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)))
+@@ -1613,7 +1619,7 @@
+ 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
+@@ -1640,7 +1646,7 @@
+
+ /* 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;
+@@ -1875,7 +1881,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;
+diff -urN kernel-source-2.4.27-8/drivers/pcmcia/ds.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/ds.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/ds.c 2001-11-12 17:39:01.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/ds.c 2005-02-18 17:48:43.000000000 +0000
+@@ -55,6 +55,7 @@
+ #include <pcmcia/bulkmem.h>
+ #include <pcmcia/cistpl.h>
+ #include <pcmcia/ds.h>
++#include <linux/devfs_fs_kernel.h>
+
+ /*====================================================================*/
+
+@@ -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 kernel-source-2.4.27-8/drivers/pcmcia/i82365.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/i82365.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/i82365.c 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/i82365.c 2005-02-18 17:48:43.000000000 +0000
+@@ -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;
+@@ -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);
+
+@@ -1057,7 +1068,7 @@
+ 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) {
+@@ -1098,7 +1109,7 @@
+
+ /* 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);
+@@ -1177,7 +1188,7 @@
+ }
+
+ /* 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;
+diff -urN kernel-source-2.4.27-8/drivers/pcmcia/rsrc_mgr.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/rsrc_mgr.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/rsrc_mgr.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/rsrc_mgr.c 2005-02-18 17:48:43.000000000 +0000
+@@ -55,6 +55,10 @@
+ #include <pcmcia/cistpl.h>
+ #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,6 +191,12 @@
+ 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.
+@@ -251,7 +267,7 @@
+
+ ======================================================================*/
+
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+ static void do_io_probe(ioaddr_t base, ioaddr_t num)
+ {
+
+@@ -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),
+@@ -414,7 +430,7 @@
+ }
+ }
+
+-#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)
+@@ -432,7 +448,7 @@
+ }
+ }
+
+-#endif /* CONFIG_ISA */
++#endif /* CONFIG_PCMCIA_PROBE */
+
+ /*======================================================================
+
+@@ -503,7 +519,7 @@
+
+ ======================================================================*/
+
+-#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)
+ {
+@@ -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,7 +689,7 @@
+
+ static int adjust_irq(adjust_t *adj)
+ {
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+ int irq;
+ irq_info_t *info;
+
+diff -urN kernel-source-2.4.27-8/drivers/pcmcia/sa1100.h kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100.h
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100.h 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100.h 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_adsbitsy.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_adsbitsy.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_adsbitsy.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_adsbitsy.c 2005-02-18 17:48:43.000000000 +0000
+@@ -11,28 +11,156 @@
+ */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/ioport.h>
+
+ #include <asm/hardware.h>
++#include <asm/hardware/sa1111.h>
++#include <asm/irq.h>
+
+ #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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_adsbitsyplus.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_adsbitsyplus.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_adsbitsyplus.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_adsbitsyplus.c 2005-02-18 17:48:43.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 <rwhaley at applieddata.net>
++ *
++ * This file comes from sa1100_adsbitsy.c of Woojung Huh <whuh at applieddata.net>
++ *
++ */
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++
++#include <asm/hardware.h>
++#include <asm/hardware/sa1111.h>
++#include <asm/irq.h>
++
++#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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_freebird.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_freebird.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_freebird.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_freebird.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_generic.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_generic.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_generic.c 2003-06-13 15:51:35.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_generic.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_graphicsclient.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_graphicsclient.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_graphicsclient.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_graphicsclient.c 2005-02-18 17:48:43.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 <asm/irq.h>
+ #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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_graphicsmaster.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_graphicsmaster.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_graphicsmaster.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_graphicsmaster.c 2005-02-18 17:48:43.000000000 +0000
+@@ -12,13 +12,13 @@
+ #include <linux/sched.h>
+
+ #include <asm/hardware.h>
++#include <asm/hardware/sa1111.h>
+
+ #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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_h3600.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_h3600.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_h3600.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_h3600.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_jornada720.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_jornada720.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_jornada720.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_jornada720.c 2005-02-18 17:48:43.000000000 +0000
+@@ -8,6 +8,7 @@
+ #include <linux/sched.h>
+
+ #include <asm/hardware.h>
++#include <asm/hardware/sa1111.h>
+
+ #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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_pangolin.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_pangolin.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_pangolin.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_pangolin.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_shannon.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_shannon.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_shannon.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_shannon.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_simpad.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_simpad.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_simpad.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_simpad.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_stork.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_stork.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_stork.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_stork.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pcmcia/sa1100_yopy.c kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_yopy.c
+--- kernel-source-2.4.27-8/drivers/pcmcia/sa1100_yopy.c 2002-08-03 01:39:44.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pcmcia/sa1100_yopy.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pld/Makefile kernel-source-2.4.27-8-arm-1/drivers/pld/Makefile
+--- kernel-source-2.4.27-8/drivers/pld/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pld/Makefile 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/pld/pld_epxa.c kernel-source-2.4.27-8-arm-1/drivers/pld/pld_epxa.c
+--- kernel-source-2.4.27-8/drivers/pld/pld_epxa.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pld/pld_epxa.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/slab.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/module.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/arch/excalibur.h>
++#include <asm/arch/hardware.h>
++#define PLD_CONF00_TYPE (volatile unsigned int *)
++#define MODE_CTRL00_TYPE (volatile unsigned int *)
++//#define DEBUG(x) x
++#define DEBUG(x)
++
++#include <asm/arch/mode_ctrl00.h>
++#include <asm/arch/pld_conf00.h>
++#ifdef CONFIG_PLD_HOTSWAP
++#include <linux/pld/pld_hotswap.h>
++#endif
++#include <linux/pld/pld_epxa.h>
++
++/*
++ * 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 kernel-source-2.4.27-8/drivers/pld/pld_hotswap.c kernel-source-2.4.27-8-arm-1/drivers/pld/pld_hotswap.c
+--- kernel-source-2.4.27-8/drivers/pld/pld_hotswap.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/pld/pld_hotswap.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,188 @@
++/*
++ * linux/drivers/pld/pld_hotswap.c
++ *
++ * Pld driver for Altera EPXA Excalibur devices
++ *
++ *
++ * Copyright 2001 Altera Corporation (cdavies at 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/pagemap.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <linux/kmod.h>
++#include <linux/proc_fs.h>
++#include <linux/list.h>
++#include <asm/uaccess.h>
++#include <linux/pld/pld_hotswap.h>
++
++
++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 kernel-source-2.4.27-8/drivers/scsi/Makefile kernel-source-2.4.27-8-arm-1/drivers/scsi/Makefile
+--- kernel-source-2.4.27-8/drivers/scsi/Makefile 2005-01-19 09:57:38.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/Makefile 2005-02-18 17:48:43.000000000 +0000
+@@ -21,7 +21,7 @@
+
+ O_TARGET := scsidrv.o
+
+-export-objs := scsi_syms.o 53c700.o libata-core.o
++export-objs := scsi_syms.o 53c700.o scsi_error.o libata-core.o
+ mod-subdirs := pcmcia ../acorn/scsi
+
+
+diff -urN kernel-source-2.4.27-8/drivers/scsi/scsi.c kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi.c
+--- kernel-source-2.4.27-8/drivers/scsi/scsi.c 2003-08-25 12:44:42.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/scsi/scsi.h kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi.h
+--- kernel-source-2.4.27-8/drivers/scsi/scsi.h 2003-08-25 12:44:42.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi.h 2005-02-18 17:48:43.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/scsi/scsi_dma.c kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_dma.c
+--- kernel-source-2.4.27-8/drivers/scsi/scsi_dma.c 2002-02-25 19:38:04.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_dma.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/scsi/scsi_error.c kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_error.c
+--- kernel-source-2.4.27-8/drivers/scsi/scsi_error.c 2004-04-14 14:05:31.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_error.c 2005-02-18 17:48:43.000000000 +0000
+@@ -35,6 +35,8 @@
+ #include "hosts.h"
+ #include "constants.h"
+
++#include <scsi/scsi_ioctl.h> /* 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;
+
++ 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,16 +425,27 @@
+ printk("cannot allocate scsi_result in scsi_request_sense.\n");
+ return FAILED;
+ }
++
++ saved_result = SCpnt->result;
++
++ 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.
++ * 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 *) 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;
+@@ -444,6 +454,12 @@
+ 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,7 +493,9 @@
+ {
+ static unsigned char tur_command[6] =
+ {TEST_UNIT_READY, 0, 0, 0, 0, 0};
++ int tries = 0;
+
++ do {
+ memcpy((void *) SCpnt->cmnd, (void *) tur_command,
+ sizeof(tur_command));
+
+@@ -495,7 +506,8 @@
+ * 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));
++ memset((void *) SCpnt->sense_buffer, 0,
++ sizeof(SCpnt->sense_buffer));
+
+ SCpnt->request_buffer = NULL;
+ SCpnt->request_bufflen = 0;
+@@ -505,19 +517,18 @@
+ SCpnt->sc_data_direction = SCSI_DATA_NONE;
+
+ 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.
++ * Arguments: SDpnt - Scsi_Device structure
+ *
+- * Returns: Nothing
++ * 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
+ *
+- * 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.
++ * Purpose: Try to retry a failed command.
+ *
+- * 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.
++ * Arguments: SCpnt - scsi command structure
++ * done - list of commands that have been successfully
++ * completed.
+ *
+- * 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.
++ * 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;
+
+- /*
+- * 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;
++ 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);
+ }
+- /*
+- * 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.
++
++ return rtn;
++}
++
++
++/*
++ * Function: scsi_eh_restart_device
++ *
++ * Purpose: Retry all failed or timed out commands for a device
++ *
++ * Arguments: SDpnt - SCSI device to retry
++ * done - list of commands that have been successfully
++ * completed.
++ *
++ * Returns: SUCCESS or failure code
+ */
+- SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target));
++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;
++}
+
+ /*
+- * panic("SCSI Error handler woken too early\n");
++ * Function: scsi_eh_set_device_offline
+ *
+- * 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: set a device off line
+ *
+- * 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 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",
++
++ 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",
++
++ 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,154 +1589,170 @@
+
+ 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",
++ 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",
++ 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",
++ 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.
++ *
++ * Retry each failed or timed out command currently
++ * outstanding for 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.
++ * If any command fails, bail out. We will try a
++ * bus reset instead.
+ */
+- rtn = scsi_try_bus_device_reset(SCloop, RESET_TIMEOUT);
++ if (rtn == SUCCESS)
++ scsi_eh_restart_device(SDpnt, done);
++ }
++}
+
+- if (rtn == SUCCESS) {
+- rtn = scsi_test_unit_ready(SCloop);
++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 (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
+- rtn = scsi_eh_retry_command(SCloop);
++ /*
++ * 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"));
+
+- if (rtn == SUCCESS) {
+- SCloop->host->host_failed--;
+- scsi_eh_finish_command(&SCdone, SCloop);
+- }
+- }
+- }
+- }
++ /*
++ * Find the maximum channel number for this host.
++ */
++ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next)
++ if (SDpnt->channel > max_channel)
++ max_channel = SDpnt->channel;
+
+- 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.
++ * Loop over each channel, and see if it any device on
++ * each channel has failed.
+ */
+- SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard bus reset\n"));
++ for (channel = 0; channel <= max_channel; channel++) {
++ Scsi_Cmnd *failed_command;
++ int soft_reset;
++
++ try_again:
++ failed_command = NULL;
++ soft_reset = 0;
+
+ /*
+- * 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.
++ * 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) {
+- next_device:
+- for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
+- if (SCpnt->state != SCSI_STATE_FAILED
+- && SCpnt->state != SCSI_STATE_TIMEOUT) {
++ if (SDpnt->channel != channel)
+ continue;
+- }
++
++ SCpnt = scsi_eh_find_failed_command(SDpnt);
++ if (SCpnt)
++ failed_command = SCpnt;
++
+ /*
+- * 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.
++ * If this device has timed out or failed commands,
++ * and uses the soft_reset option.
+ */
+- 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 (SCpnt && SDpnt->soft_reset)
++ soft_reset = 1;
+ }
+- if (SCloop->state != SCSI_STATE_FAILED
+- && SCloop->state != SCSI_STATE_TIMEOUT) {
++
++ /*
++ * If this channel hasn't failed, we
++ * don't need to reset it.
++ */
++ if (!failed_command)
+ 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
+@@ -1579,14 +1763,19 @@
+ * 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.
++ * *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 next_device;
+- }
+- }
++ goto try_again;
+ }
+
+ /*
+@@ -1594,73 +1783,90 @@
+ * bus that SCpnt points to. There are no soft-reset devices
+ * with outstanding timed out commands.
+ */
+- 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);
+- }
+- }
++ rtn = scsi_try_bus_reset(failed_command);
++
++ /*
++ * If we failed to reset the bus, move on to the next bus.
++ */
++ if (rtn != SUCCESS)
++ continue;
++
+ /*
+- * 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?
++ * 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) {
+- 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);
++ SCpnt = scsi_eh_find_failed_command(SDpnt);
+
+- SDloop->online = FALSE;
+- SDloop->host->host_failed--;
+- scsi_eh_finish_command(&SCdone, SCloop);
+- }
+- }
+- }
++ /*
++ * 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;
++ /*
++ * Locate any failed commands for this device.
++ */
++ SCpnt = scsi_eh_find_failed_command(SDpnt);
++ if (SCpnt)
++ failed_command = SCpnt;
++
++ /*
++ * If this device has timed out or failed commands,
++ * and uses the soft_reset option.
++ */
++ if (SCpnt && SDpnt->soft_reset)
++ soft_reset = 1;
+ }
+- 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
+@@ -1669,9 +1875,14 @@
+ *
+ * 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"));
++ printk("scsi_unjam_host: unable to try "
++ "hard host reset\n"));
+
+ /*
+ * Due to the spinlock, we will never get out of this
+@@ -1679,101 +1890,177 @@
+ */
+ scsi_sleep(1 * HZ);
+
+- goto next_device2;
++ goto try_again;
+ }
+- SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard host reset\n"));
++
++ SCSI_LOG_ERROR_RECOVERY(3,
++ printk("scsi_unjam_host: Try hard host reset\n"));
+
+ /*
+ * FIXME(eric) - we need to obtain a valid SCpnt to perform this call.
+ */
+- rtn = scsi_try_host_reset(SCpnt);
++ 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 (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);
+- }
+- }
++ * 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);
++
+ 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);
+- }
+- }
+- }
++ SCpnt = scsi_eh_find_failed_command(SDpnt);
++
++ /*
++ * 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.
++ */
++ 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 we solved all of the problems, then let's rev up the engines again.
++ * 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.
+ */
+- if (host->host_failed == 0) {
+- ourrtn = TRUE;
+- goto leave;
+- }
++STATIC int scsi_unjam_host(struct Scsi_Host *host)
++{
++ Scsi_Cmnd *SCdone = NULL;
++ Scsi_Cmnd *SCpnt;
++ Scsi_Device *SDpnt;
++ int ourrtn = FALSE;
++ int i;
++
++ ASSERT_LOCK(&io_request_lock, 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.
++ * 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.
+ */
+- SCSI_LOG_ERROR_RECOVERY(1, printk("scsi_unjam_host: Take device offline\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) {
+- 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;
++ 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;
+ }
+-
+ /*
+- * This should pass the failure up to the top level driver, and
+- * it will have to try and do something intelligent with it.
++ * 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.
+ */
+- 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_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target));
+
+- scsi_eh_finish_command(&SCdone, SCloop);
+- }
+- }
++/*
++ * 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)
++ */
+ }
+-
+- 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"));
+
+- ourrtn = FALSE;
++ for (i = 0; i < ARRAY_SIZE(unjam_method); i++) {
++ unjam_method[i](host, &SCdone);
+
+- leave:
++ /*
++ * 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 kernel-source-2.4.27-8/drivers/scsi/scsi_ioctl.c kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_ioctl.c
+--- kernel-source-2.4.27-8/drivers/scsi/scsi_ioctl.c 2003-08-25 12:44:42.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_ioctl.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/scsi/scsi_lib.c kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_lib.c
+--- kernel-source-2.4.27-8/drivers/scsi/scsi_lib.c 2004-04-14 14:05:31.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_lib.c 2005-02-18 17:48:43.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/scsi/scsi_syms.c kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_syms.c
+--- kernel-source-2.4.27-8/drivers/scsi/scsi_syms.c 2004-04-14 14:05:31.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/scsi_syms.c 2005-02-18 17:48:43.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/scsi/sd.c kernel-source-2.4.27-8-arm-1/drivers/scsi/sd.c
+--- kernel-source-2.4.27-8/drivers/scsi/sd.c 2003-08-25 12:44:42.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/sd.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/scsi/sr_ioctl.c kernel-source-2.4.27-8-arm-1/drivers/scsi/sr_ioctl.c
+--- kernel-source-2.4.27-8/drivers/scsi/sr_ioctl.c 2002-11-28 23:53:14.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/scsi/sr_ioctl.c 2005-02-18 17:48:43.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 kernel-source-2.4.27-8/drivers/serial/21285.c kernel-source-2.4.27-8-arm-1/drivers/serial/21285.c
+--- kernel-source-2.4.27-8/drivers/serial/21285.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/21285.c 2005-02-18 17:48:43.000000000 +0000
+@@ -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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/serial.h>
++#include <linux/major.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/console.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/hardware/dec21285.h>
++#include <asm/hardware.h>
++
++#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 <asm/mach-types.h>
++/*
++ * 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 kernel-source-2.4.27-8/drivers/serial/8250.c kernel-source-2.4.27-8-arm-1/drivers/serial/8250.c
+--- kernel-source-2.4.27-8/drivers/serial/8250.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/8250.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++#include <linux/serial_reg.h>
++#include <linux/serialP.h>
++#include <linux/delay.h>
++#include <linux/serial_core.h>
++#include <linux/kmod.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++
++#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 <asm/serial.h>
++
++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 at 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 kernel-source-2.4.27-8/drivers/serial/8250.h kernel-source-2.4.27-8-arm-1/drivers/serial/8250.h
+--- kernel-source-2.4.27-8/drivers/serial/8250.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/8250.h 2005-02-18 17:48:43.000000000 +0000
+@@ -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 <linux/config.h>
++
++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 kernel-source-2.4.27-8/drivers/serial/8250_pci.c kernel-source-2.4.27-8-arm-1/drivers/serial/8250_pci.c
+--- kernel-source-2.4.27-8/drivers/serial/8250_pci.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/8250_pci.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/sched.h>
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/serial.h>
++
++/* 2.4.6 compatibility cruft ;( */
++#define pci_board __pci_board
++#include <linux/serialP.h>
++#undef pci_board
++
++#include <asm/bitops.h>
++#include <asm/byteorder.h>
++#include <asm/serial.h>
++
++#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 <pazke at mail.tp.ru>, 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 at 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 at 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 at 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 at 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 at 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 at 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 <arh at 01019freenet.de> */
++ { 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 kernel-source-2.4.27-8/drivers/serial/8250_pnp.c kernel-source-2.4.27-8-arm-1/drivers/serial/8250_pnp.c
+--- kernel-source-2.4.27-8/drivers/serial/8250_pnp.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/8250_pnp.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/isapnp.h>
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/serial.h>
++#include <linux/serialP.h>
++
++#include <asm/bitops.h>
++#include <asm/byteorder.h>
++#include <asm/serial.h>
++
++#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 kernel-source-2.4.27-8/drivers/serial/Config.in kernel-source-2.4.27-8-arm-1/drivers/serial/Config.in
+--- kernel-source-2.4.27-8/drivers/serial/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/Config.in 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,99 @@
++#
++# 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
++
++ dep_bool 'BAST/S3C2410X serial port spport' CONFIG_SERIAL_S3C2410X $CONFIG_CPU_S3C2410X
++ dep_bool ' Support for console on BAST/S3C2410X serial port' CONFIG_SERIAL_S3C2410X_CONSOLE $CONFIG_SERIAL_S3C2410X
++
++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
++
++dep_bool 'BAST/VR1000 16550 Serial Port support' CONFIG_SERIAL_BAST_16550 $CONFIG_ARCH_BAST $CONFIG_SERIAL_8250
++
++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_S3C2410X" = "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_S3C2410X" = "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_S3C2410X_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 kernel-source-2.4.27-8/drivers/serial/Makefile kernel-source-2.4.27-8-arm-1/drivers/serial/Makefile
+--- kernel-source-2.4.27-8/drivers/serial/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/Makefile 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,41 @@
++#
++# 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_S3C2410X) += s3c2410x.o
++obj-$(CONFIG_SERIAL_AT91US3) += at91us3.o
++obj-$(CONFIG_SERIAL_BAST_16550) += bast_serial.o
++
++include $(TOPDIR)/Rules.make
++
++fastdep:
++
+diff -urN kernel-source-2.4.27-8/drivers/serial/amba.c kernel-source-2.4.27-8-arm-1/drivers/serial/amba.c
+--- kernel-source-2.4.27-8/drivers/serial/amba.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/amba.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++#include <asm/hardware/serial_amba.h>
++
++#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 kernel-source-2.4.27-8/drivers/serial/anakin.c kernel-source-2.4.27-8-arm-1/drivers/serial/anakin.c
+--- kernel-source-2.4.27-8/drivers/serial/anakin.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/anakin.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++
++#include <linux/serial_core.h>
++
++#include <asm/arch/serial_reg.h>
++
++#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 <chan at aleph1.co.uk>");
++MODULE_SUPPORTED_DEVICE("ttyAN");
++MODULE_LICENSE("GPL");
++
++EXPORT_NO_SYMBOLS;
+diff -urN kernel-source-2.4.27-8/drivers/serial/bast_serial.c kernel-source-2.4.27-8-arm-1/drivers/serial/bast_serial.c
+--- kernel-source-2.4.27-8/drivers/serial/bast_serial.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/bast_serial.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,106 @@
++/* linux/drivers/char/serial_bast.c
++ *
++ * Probe for BAST/VR1000 serial ports
++ *
++ * Based on linux/drivers/char/serial_8250_pci.c
++ *
++ * (c) 2004 Simtec Electronics
++ *
++ * 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.
++*/
++
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/sched.h>
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/serial.h>
++#include <linux/serial_core.h>
++
++#include <asm/bitops.h>
++#include <asm/byteorder.h>
++#include <asm/serial.h>
++#include <asm/mach-types.h>
++#include <asm/irq.h>
++
++#include <asm/arch/map.h>
++
++#include "8250.h"
++
++static struct serial_struct req;
++
++static void
++bast_register_port(int port, unsigned long base, unsigned int irq)
++{
++ //printk("Registering port %d (base=0x%08x, %d)\n", port, base, irq);
++
++ req.io_type = SERIAL_IO_MEM;
++ req.iomem_base = ioremap(base, 0x08);
++ req.iomem_reg_shift = 0;
++ req.iomap_base = base;
++ req.port = port;
++ req.irq = irq;
++
++ register_serial(&req);
++}
++
++static int __init serial_bast_init(void)
++{
++ if (machine_is_bast()) {
++ memset(&req, 0, sizeof(req));
++
++ req.baud_base = BASE_BAUD;
++ req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
++
++ printk("Registering BAST serial ports\n");
++
++ bast_register_port(0, BAST_PCSIO + 0x3f8, IRQ_PCSERIAL1);
++ bast_register_port(1, BAST_PCSIO + 0x2f8, IRQ_PCSERIAL2);
++ }
++
++ if (machine_is_vr1000()) {
++ int i;
++
++ printk("Registering VR1000 serial ports\n");
++
++ /* we have a quad uart (Philips 16C554) sitting here
++ * but it does not reliably auto-detect... we tell the
++ * serial driver it is an SC16554 to ensure the correct
++ * operation
++ */
++
++ memset(&req, 0, sizeof(req));
++
++ /* Exar part */
++
++ req.baud_base = 3692307/16;
++ req.flags = ASYNC_SKIP_TEST;
++
++ for (i = 0; i < 4; i++) {
++ bast_register_port(i,
++ VR1000_PA_SERIAL + 0x80 + (i << 5),
++ IRQ_VR1000_SERIAL0 + i);
++ }
++ }
++
++ return 0;
++}
++
++static void __exit serial_bast_exit(void)
++{
++}
++
++module_init(serial_bast_init);
++module_exit(serial_bast_exit);
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("BAST serial probe module");
++
+diff -urN kernel-source-2.4.27-8/drivers/serial/clps711x.c kernel-source-2.4.27-8-arm-1/drivers/serial/clps711x.c
+--- kernel-source-2.4.27-8/drivers/serial/clps711x.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/clps711x.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/bitops.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include <linux/serial_core.h>
++
++#include <asm/hardware/clps7111.h>
++
++#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 kernel-source-2.4.27-8/drivers/serial/core.c kernel-source-2.4.27-8-arm-1/drivers/serial/core.c
+--- kernel-source-2.4.27-8/drivers/serial/core.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/core.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,2588 @@
++/*
++ * 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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/tty.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++#include <linux/pm.h>
++#include <linux/serial_core.h>
++#include <linux/smp_lock.h>
++
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++
++#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 <baud><parity><bits><flow>,
++ * 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 s3c2410uart_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
++#ifdef CONFIG_SERIAL_S3C2410X_CONSOLE
++ s3c2410uart_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 kernel-source-2.4.27-8/drivers/serial/omaha.c kernel-source-2.4.27-8-arm-1/drivers/serial/omaha.c
+--- kernel-source-2.4.27-8/drivers/serial/omaha.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/omaha.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++
++#if defined(CONFIG_SERIAL_OMAHA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++#include <asm/hardware/serial_omaha.h>
++
++#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 kernel-source-2.4.27-8/drivers/serial/s3c2410x.c kernel-source-2.4.27-8-arm-1/drivers/serial/s3c2410x.c
+--- kernel-source-2.4.27-8/drivers/serial/s3c2410x.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/s3c2410x.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,896 @@
++/*
++ * linux/drivers/char/serial_mx1ads.c
++ *
++ * Driver for Samsung S3C2410's internal serial ports (UART0, UART1 and UART2)
++ *
++ * Copyright (C) 2002 Steve Hein, SGI Inc. (ssh at sgi.com)
++ *
++ * Based on drivers/serial/serial_amba.c
++ *
++ *
++ * Copyright 1999 ARM Limited
++ * Copyright (C) 2000 Deep Blue Solutions Ltd.
++ * Copyright (C) 2002 Shane Nay (shane at minirl.com)
++ * Copyright (C) 2003 Simtec Electronics (ben at simtec.co.uk)
++ *
++ * 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
++ *
++ *
++ * Based on the AMBA driver, this is a driver for the S3C2410 boards
++ * UARTs.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++
++#if defined(CONFIG_SERIAL_S3C2410_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++#include <asm/mach-types.h>
++
++#include <asm/arch/map.h>
++#include <asm/hardware/serial_s3c2410.h>
++
++//extern void debug_pk(const char *);
++
++//#define DEBUG 1
++
++#define UART_NR 3
++
++#define MINOR_START 64
++
++#define SERIAL_S3C2410_NAME "ttySAC"
++#define SERIAL_S3C2410_MAJOR 204
++#define SERIAL_S3C2410_MINOR MINOR_START
++#define SERIAL_S3C2410_NR UART_NR
++
++#define CALLOUT_S3C2410_NAME "cuasac"
++#define CALLOUT_S3C2410_MAJOR 205
++#define CALLOUT_S3C2410_MINOR MINOR_START
++#define CALLOUT_S3C2410_NR UART_NR
++
++
++static struct tty_driver normal, callout;
++static struct tty_struct *s3c2410_table[UART_NR];
++static struct termios *s3c2410_termios[UART_NR], *s3c2410_termios_locked[UART_NR];
++#ifdef SUPPORT_SYSRQ
++static struct console s3c2410_console;
++#endif
++
++#define S3C2410_ISR_PASS_LIMIT 256
++
++/*
++ * Access macros for the S3C2410 UARTs
++ */
++
++#define UART_GET_CHAR(p) __raw_readb((p)->membase + S3C2410_UARTRXH0_OFF)
++#define UART_PUT_CHAR(p,c) __raw_writeb((c), (p)->membase + S3C2410_UARTTXH0_OFF)
++
++#define UART_GET_ULCON(p) __raw_readl((p)->membase + S3C2410_UARTLCON_OFF)
++#define UART_GET_UCON(p) __raw_readl((p)->membase + S3C2410_UARTCON_OFF)
++#define UART_GET_UFCON(p) __raw_readl((p)->membase + S3C2410_UARTFCON_OFF)
++#define UART_GET_UMCON(p) __raw_readl((p)->membase + S3C2410_UARTMCON_OFF)
++#define UART_GET_UBRDIV(p) __raw_readl((p)->membase + S3C2410_UARTBRDIV_OFF)
++
++#define UART_GET_UTRSTAT(p) __raw_readl((p)->membase + S3C2410_UARTTRSTAT_OFF)
++#define UART_GET_UERSTAT(p) __raw_readl((p)->membase + S3C2410_UARTERSTAT_OFF)
++#define UART_GET_UFSTAT(p) __raw_readl((p)->membase + S3C2410_UARTFSTAT_OFF)
++#define UART_GET_UMSTAT(p) __raw_readl((p)->membase + S3C2410_UARTMSTAT_OFF)
++
++#define UART_PUT_ULCON(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTLCON_OFF)
++#define UART_PUT_UCON(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTCON_OFF)
++#define UART_PUT_UFCON(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTFCON_OFF)
++#define UART_PUT_UMCON(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTMCON_OFF)
++#define UART_PUT_UBRDIV(p,c) __raw_writel(c, (p)->membase + S3C2410_UARTBRDIV_OFF)
++
++
++/* When using the integer divisor for the UART,
++ * we must set the IR to 0xf prior to programming
++ * the divisor
++ */
++
++
++
++#define UART_RX_DATA(s) (((s) & S3C2410_UTRSTAT_RXDR) == S3C2410_UTRSTAT_RXDR)
++
++#define UART_TX_READY(s) (((s) & S3C2410_UTRSTAT_TXFE) == S3C2410_UTRSTAT_TXFE)
++#define TX_FIFOCOUNT(port) (((UART_GET_UFSTAT(port))>>4)&0xf)
++#define RX_FIFOCOUNT(port) ((UART_GET_UFSTAT(port) & 1<<8)?16:(((UART_GET_UFSTAT(port))>>0)&0xf) )
++
++#define ER_IRQ(port) (port->irq + 2)
++#define TX_IRQ(port) (port->irq + 1)
++#define RX_IRQ(port) (port->irq)
++
++#define UART_DUMMY_RSR_RX 256
++#define UART_PORT_SIZE 64
++
++/*
++ * Our private driver data mappings.
++ */
++
++#define drv_old_status driver_priv
++
++
++#define PORT_TXIRQ_ENABLED (1)
++#define PORT_RXIRQ_ENABLED (2)
++#define PORT_ERIRQ_ENABLED (4)
++
++struct port_priv {
++ unsigned char irq_claimed;
++};
++
++static struct port_priv port_privs[UART_NR] = {
++ { 0 },
++ { 0 },
++ { 0 }
++};
++
++#if DEBUG
++#include <stdarg.h>
++
++void
++debug_pk(const char *msg, ...)
++{
++ char buff[1024];
++ va_list va;
++
++ va_start(va, msg);
++ vsprintf(buff, msg, va);
++ printascii(buff);
++ va_end(va);
++}
++
++#else
++#define debug_pk(x...)
++#endif
++
++static struct port_priv *
++s3c2410uart_get_port_priv(struct uart_port *port)
++{
++ switch (port->irq) {
++ case IRQ_S3CUART_RX0:
++ return &port_privs[0];
++ case IRQ_S3CUART_RX1:
++ return &port_privs[1];
++ case IRQ_S3CUART_RX2:
++ return &port_privs[2];
++ default:
++ printk(KERN_ERR __FUNCTION__ ": request with irq=%d\n", port->irq);
++ return NULL;
++ }
++
++ return NULL;
++}
++
++
++
++static void
++s3c2410uart_stop_tx(struct uart_port *port, u_int from_tty)
++{
++ struct port_priv *priv = s3c2410uart_get_port_priv(port);
++
++ if (priv->irq_claimed & PORT_TXIRQ_ENABLED) {
++ priv->irq_claimed &= ~PORT_TXIRQ_ENABLED;
++ disable_irq(TX_IRQ(port));
++ }
++}
++
++static void
++s3c2410uart_start_tx(struct uart_port *port, u_int from_tty)
++{
++ struct port_priv *priv = s3c2410uart_get_port_priv(port);
++
++ if ((priv->irq_claimed & PORT_TXIRQ_ENABLED) == 0) {
++ enable_irq(TX_IRQ(port));
++ priv->irq_claimed |= PORT_TXIRQ_ENABLED;
++ }
++}
++
++static void
++s3c2410uart_stop_rx(struct uart_port *port)
++{
++ //printk(__FUNCTION__ ": port=%p, irq=%d\n", port, RX_IRQ(port));
++ //disable_irq(RX_IRQ(port));
++}
++
++static void
++s3c2410uart_enable_ms(struct uart_port *port)
++{
++ /* What is MS? */
++}
++
++static void
++#ifdef SUPPORT_SYSRQ
++s3c2410uart_rx_chars(struct uart_port *port, struct pt_regs *regs)
++#else
++s3c2410uart_rx_chars(struct uart_port *port)
++#endif
++{
++ struct tty_struct *tty = port->info->tty;
++ unsigned int status, ch, max_count = 256;
++
++ status = UART_GET_UTRSTAT(port);
++ while (RX_FIFOCOUNT(port) > 0 && 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);
++ //printk("rxchar=%d\n", ch);
++
++ *tty->flip.char_buf_ptr = ch;
++ *tty->flip.flag_buf_ptr = TTY_NORMAL;
++ port->icount.rx++;
++ tty->flip.flag_buf_ptr++;
++ tty->flip.char_buf_ptr++;
++ tty->flip.count++;
++ /* No error handling just yet.
++ * On the MX1 these are seperate
++ * IRQs, so we need to deal with
++ * the sanity of 5 IRQs for one
++ * serial port before we deal
++ * with the error path properly.
++ */
++ status = UART_GET_UTRSTAT(port);
++ }
++ tty_flip_buffer_push(tty);
++ return;
++}
++
++static void
++s3c2410uart_tx_chars(struct uart_port *port)
++{
++ int count;
++
++ if (TX_FIFOCOUNT(port) >= 15)
++ return;
++
++ if (port->x_char) {
++ UART_PUT_CHAR(port, port->x_char);
++ port->icount.tx++;
++ port->x_char = 0;
++ return;
++ }
++
++#if 0
++ if (port->info->xmit.head == port->info->xmit.tail
++ || port->info->tty->stopped || port->info->tty->hw_stopped) {
++ s3c2410uart_stop_tx(port, 0);
++ return;
++ }
++#else
++ if (uart_circ_empty(&port->info->xmit) ||
++ uart_tx_stopped(port)) {
++ s3c2410uart_stop_tx(port, 0);
++ return;
++ }
++#endif
++
++ count = port->fifosize - TX_FIFOCOUNT(port);
++
++
++ while (count-- > 0) {
++ UART_PUT_CHAR(port, port->info->xmit.buf[port->info->xmit.tail]);
++ port->info->xmit.tail = (port->info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
++ port->icount.tx++;
++ if (port->info->xmit.head == port->info->xmit.tail)
++ break;
++ }
++
++ if (CIRC_CNT(port->info->xmit.head, port->info->xmit.tail, UART_XMIT_SIZE) < WAKEUP_CHARS)
++ uart_write_wakeup(port);
++
++ if (uart_circ_empty(&port->info->xmit))
++ s3c2410uart_stop_tx(port, 0);
++}
++
++static void
++s3c2410uart_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++ struct uart_port *port = dev_id;
++ unsigned int status;
++
++ //printk(KERN_DEBUG "s3c2410uart_int: port=%p, irq=%d\n", port, irq);
++ //debug_pk("s3c2410uart_int: port=%p, irq=%d\n", port, irq);
++
++ status = UART_GET_UTRSTAT(port);
++
++ if(irq==(TX_IRQ(port))) {
++ s3c2410uart_tx_chars(port);
++ }
++
++ if (RX_FIFOCOUNT(port) > 0) {
++#ifdef SUPPORT_SYSRQ
++ s3c2410uart_rx_chars(port, regs);
++#else
++ s3c2410uart_rx_chars(port);
++#endif
++ }
++
++
++}
++
++static void
++s3c2410uart_err_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++ struct uart_port *port = dev_id;
++ unsigned int estatus;
++
++ estatus = UART_GET_UERSTAT(port);
++
++ if(estatus)
++ {
++ printk(KERN_DEBUG "s3c2410uart_int: error=%d irq=%d\n", estatus,TX_IRQ(port));
++ }
++}
++
++static u_int
++s3c2410uart_tx_empty(struct uart_port *port)
++{
++#if 0
++ return UART_GET_UTRSTAT(port) & S3C2410_UTRSTAT_TXFE ? TIOCSER_TEMT : 0;
++#else
++ return TX_FIFOCOUNT(port) == 0;
++#endif
++}
++
++static u_int
++s3c2410uart_get_mctrl(struct uart_port *port)
++{
++ /* modem control lines state */
++ int mstat;
++ int ret = 0;
++
++ mstat = UART_GET_UMSTAT(port);
++ if (mstat & S3C2410_UMSTAT_CTS) {
++ ret |= TIOCM_CTS;
++ }
++ return ret;
++}
++
++static void
++s3c2410uart_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++ int mcon = UART_GET_UMCON(port);
++
++ if (mctrl & TIOCM_RTS)
++ mcon |= S3C2410_UMCON_RTS; // set RTS
++ else
++ mcon &= ~S3C2410_UMCON_RTS; // clear RTS
++
++ UART_PUT_UMCON(port, mcon);
++}
++
++static void
++s3c2410uart_break_ctl(struct uart_port *port, int break_state)
++{
++ unsigned long ucon=UART_GET_UCON(port);
++ ucon |= S3C2410_UCON_SBREAK;
++ UART_PUT_UCON(port,ucon);
++}
++
++static int
++s3c2410uart_startup(struct uart_port *port)
++{
++ struct port_priv *priv = s3c2410uart_get_port_priv(port);
++ int retval;
++ /*
++ * Allocate the IRQ
++ */
++
++#if 0
++ debug_pk("s3c2410uart_startup:\n");
++ debug_pk("s3c2410uart: IRQs (rx=%d, tx=%d)\n",
++ RX_IRQ(port), TX_IRQ(port));
++#endif
++
++ debug_pk("s3c2410uart: enabling RX interrupt\n");
++
++ priv->irq_claimed |= PORT_RXIRQ_ENABLED;
++
++ retval = request_irq(RX_IRQ(port), s3c2410uart_int, 0,
++ "s3c2410-rx", port);
++ if (retval) {
++ priv->irq_claimed &= ~PORT_RXIRQ_ENABLED;
++ return retval;
++ }
++
++
++ debug_pk("s3c2410uart: enabling TX interrupt\n");
++
++ priv->irq_claimed |= PORT_TXIRQ_ENABLED;
++
++ retval = request_irq(TX_IRQ(port), s3c2410uart_int, 0,
++ "s3c2410-tx", port);
++
++ if (retval) {
++ priv->irq_claimed &= ~PORT_TXIRQ_ENABLED;
++ return retval;
++ }
++
++
++ debug_pk("s3c2410uart: enabling ERROR interrupt\n");
++
++ priv->irq_claimed |= PORT_ERIRQ_ENABLED;
++
++ retval = request_irq(ER_IRQ(port), s3c2410uart_err_int, 0,
++ "s3c2410-error", port);
++
++ if (retval) {
++ priv->irq_claimed &= ~PORT_ERIRQ_ENABLED;
++ return retval;
++ }
++
++ debug_pk("s3c2410uart_startup: setting ucon\n");
++ if (!machine_is_bast() && !machine_is_vr1000()) {
++ //debug_pk("configuring for pclk clock source\n");
++ UART_PUT_UCON(port, S3C2410_UCON_DEFAULT);
++ } else {
++ unsigned long tmp;
++
++ //debug_pk("configuring for bast\n");
++ /* run uart clocks from uclk (external 24MHz) and
++ * use pulse-driven interrupts...
++ */
++
++ tmp = S3C2410_UCON_DEFAULT;
++ tmp |= 1<<10;
++ //tmp &= ~S3C2410_UCON_RXILEVEL;
++ //tmp &= ~S3C2410_UCON_TXILEVEL;
++
++ UART_PUT_UCON(port, tmp);
++ }
++ //debug_pk("s3c2410uart_startup: setting ufcon\n");
++ UART_PUT_UFCON(port, S3C2410_UFCON_DEFAULT);
++ //debug_pk("s3c2410uart_startup: setting mucon\n");
++ UART_PUT_UMCON(port, 0);
++// UART_PUT_UBRDIV(port,(int)(PCLK/CURRENT_BAUD_RATE*16))-1);
++
++#if 1
++ debug_pk("-> UCON = %08x\n", UART_GET_UCON(port));
++ debug_pk("-> UFCON = %08x\n", UART_GET_UFCON(port));
++ debug_pk("-> UMCON = %08x\n", UART_GET_UMCON(port));
++#endif
++
++ //debug_pk("s3c2410uart_startup: done\n");
++ return 0;
++
++}
++
++static void
++s3c2410uart_shutdown(struct uart_port *port)
++{
++ struct port_priv *priv = s3c2410uart_get_port_priv(port);
++
++ /*
++ * Free the interrupt
++ */
++
++ /* printk(__FUNCTION__ ": port=%p\n");*/
++ free_irq(RX_IRQ(port), port);
++ free_irq(TX_IRQ(port), port);
++ free_irq(ER_IRQ(port), port);
++
++ priv->irq_claimed &= ~(PORT_ERIRQ_ENABLED | PORT_RXIRQ_ENABLED | PORT_TXIRQ_ENABLED);
++}
++
++static void
++s3c2410uart_change_speed(struct uart_port *port, u_int cflag, u_int iflag,
++ u_int quot)
++{
++ unsigned long flags, lcon;
++
++ debug_pk("s3c2410uart_set_cflag(0x%x) called (quot=%d)\n",
++ cflag, quot);
++
++ /* first, disable everything */
++ save_flags(flags);
++ cli();
++ lcon=UART_GET_ULCON(port);
++ /* Clear the bits we're going to configure */
++ lcon &= ~(S3C2410_LCON_CFGMASK);
++
++ switch (cflag & CSIZE) {
++ case CS5:
++ lcon |= S3C2410_LCON_CS5;
++ break;
++ case CS6:
++ lcon |= S3C2410_LCON_CS6;
++ break;
++ case CS7:
++ lcon |= S3C2410_LCON_CS7;
++ break;
++ default:
++ lcon |= S3C2410_LCON_CS8;
++ break;
++ }
++
++ if(cflag & PARENB) {
++ lcon |= (cflag & PARODD)? S3C2410_LCON_PODD:S3C2410_LCON_PEVEN;
++ } else {
++ lcon |= S3C2410_LCON_PNONE;
++ }
++
++ if(cflag & CSTOPB) {
++ lcon |= S3C2410_LCON_2STOP;
++ } else {
++ lcon |= S3C2410_LCON_1STOP;
++ }
++
++ UART_PUT_ULCON(port, lcon);
++
++ /* Enable automatic flow control if apropriate */
++ if ((cflag & CRTSCTS) || (!(cflag & CLOCAL))) {
++ unsigned long mcon = UART_GET_UMCON(port);
++ mcon |= S3C2410_UMCON_AFC;
++ UART_PUT_UMCON(port, mcon);
++ }
++
++ if(iflag & BRKINT) {
++ unsigned long ucon=UART_GET_UCON(port);
++ ucon |= S3C2410_UCON_SBREAK;
++ UART_PUT_UCON(port, ucon);
++ }
++
++ UART_PUT_UBRDIV(port, quot - 1);
++
++ restore_flags(flags);
++
++}
++
++static const char *
++s3c2410uart_type(struct uart_port *port)
++{
++ return port->type == PORT_S3C2410 ? "S3C2410" : NULL;
++}
++
++/*
++ * Release the memory region(s) being used by 'port'.
++ */
++static void
++s3c2410uart_release_port(struct uart_port *port)
++{
++ return;
++}
++
++/*
++ * Request the memory region(s) being used by 'port'.
++ */
++static int
++s3c2410uart_request_port(struct uart_port *port)
++{
++ return 0;
++}
++
++/*
++ * Configure/autoconfigure the port.
++ */
++static void
++s3c2410uart_config_port(struct uart_port *port, int flags)
++{
++ if (flags & UART_CONFIG_TYPE && s3c2410uart_request_port(port) == 0)
++ port->type = PORT_S3C2410;
++}
++
++static int
++s3c2410uart_verify_port(struct uart_port *port, struct serial_struct *ser)
++{
++ return 0;
++}
++
++static struct uart_ops s3c2410_pops = {
++ tx_empty: s3c2410uart_tx_empty,
++ set_mctrl: s3c2410uart_set_mctrl,
++ get_mctrl: s3c2410uart_get_mctrl,
++ stop_tx: s3c2410uart_stop_tx,
++ start_tx: s3c2410uart_start_tx,
++ stop_rx: s3c2410uart_stop_rx,
++ enable_ms: s3c2410uart_enable_ms,
++ break_ctl: s3c2410uart_break_ctl,
++ startup: s3c2410uart_startup,
++ shutdown: s3c2410uart_shutdown,
++ change_speed: s3c2410uart_change_speed,
++ type: s3c2410uart_type,
++ release_port: s3c2410uart_release_port,
++ request_port: s3c2410uart_request_port,
++ config_port: s3c2410uart_config_port,
++ verify_port: s3c2410uart_verify_port,
++};
++
++static struct uart_port s3c2410_ports[UART_NR] = {
++ {
++ membase: (void *) S3C2410_VA_UART,
++ mapbase: S3C2410_VA_UART,
++ iotype: SERIAL_IO_MEM,
++ irq: IRQ_S3CUART_RX0,
++ uartclk: 24000000,
++ fifosize: 15, /* we'll loose chars if 16 */
++ unused: {4, 5},
++ ops: &s3c2410_pops,
++ type: PORT_S3C2410,
++ flags: ASYNC_BOOT_AUTOCONF,
++ line: 0,
++ },
++ {
++ membase: (void *) (S3C2410_VA_UART + S3C2410_UART1_OFF),
++ mapbase: (S3C2410_VA_UART + S3C2410_UART1_OFF),
++ iotype: SERIAL_IO_MEM,
++ irq: IRQ_S3CUART_RX1,
++ uartclk: 24000000,
++ fifosize: 15,
++ unused: {6, 7},
++ ops: &s3c2410_pops,
++ type: PORT_S3C2410,
++ flags: ASYNC_BOOT_AUTOCONF,
++ line: 1,
++ },
++ {
++ membase: (void *) (S3C2410_VA_UART + S3C2410_UART2_OFF),
++ mapbase: (S3C2410_VA_UART + S3C2410_UART2_OFF),
++ iotype: SERIAL_IO_MEM,
++ irq: IRQ_S3CUART_RX2,
++ uartclk: 24000000,
++ fifosize: 15,
++ unused: {8, 9},
++ ops: &s3c2410_pops,
++ type: PORT_S3C2410,
++ flags: ASYNC_BOOT_AUTOCONF,
++ line: 2,
++ }
++};
++
++#if defined(CONFIG_SERIAL_S3C2410_CONSOLE) || defined(CONFIG_SERIAL_S3C2410X_CONSOLE)
++
++#ifdef used_and_not_const_char_pointer
++static int
++s3c2410uart_console_read(struct uart_port *port, char *s, u_int count)
++{
++ unsigned int status;
++ int c;
++
++ debug_pk("s3c2410uart_console_read() called\n");
++
++ c = 0;
++ while (c < count) {
++ status = UART_GET_UTRSTAT(port);
++ if (RX_FIFOCOUNT(port) > 0) {
++ *s++ = UART_GET_CHAR(port);
++ c++;
++ } else {
++ // nothing more to get, return
++ return c;
++ }
++ }
++ /* return the count */
++ return c;
++}
++#endif
++
++
++static void
++s3c2410uart_console_write(struct console *co, const char *s, u_int count)
++{
++ struct uart_port *port = s3c2410_ports + co->index;
++ unsigned int status;
++ int i;
++ /*
++ * Now, do each character
++ */
++ for (i = 0; i < count; i++) {
++ do {
++ status = TX_FIFOCOUNT(port);
++ } while (status >= 15);
++ UART_PUT_CHAR(port, (s[i]) & 0xff);
++ if (s[i] == '\n') {
++ do {
++ status = TX_FIFOCOUNT(port);
++ } while (status >= 15);
++ UART_PUT_CHAR(port, '\r');
++ }
++ }
++}
++
++static kdev_t
++s3c2410uart_console_device(struct console *co)
++{
++
++ return MKDEV(SERIAL_S3C2410_MAJOR, SERIAL_S3C2410_MINOR + co->index);
++}
++
++#define CURRENT_BAUD_RATE (115200)
++
++static void __init
++s3c2410uart_console_get_options(struct uart_port *port, int *baud, int *parity,
++ int *bits)
++{
++#if 0
++ *baud = (7833600)/(UART_GET_LCR(port) +1);
++#else
++ *baud = CURRENT_BAUD_RATE;
++#endif
++ *bits = 8;
++ *parity = 'n';
++}
++
++static int __init
++s3c2410uart_console_setup(struct console *co, char *options)
++{
++ struct uart_port *port;
++ int baud = CURRENT_BAUD_RATE;
++ 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(s3c2410_ports, UART_NR, co);
++
++ /* ensure console uses correct base rate clock */
++ if(machine_is_bast()) {
++ port->uartclk=24000000;
++ } else if(machine_is_vr1000()) {
++ port->uartclk=3692307;
++ }
++
++ if (options)
++ uart_parse_options(options, &baud, &parity, &bits, &flow);
++ else
++ s3c2410uart_console_get_options(port, &baud, &parity, &bits);
++
++ return uart_set_options(port, co, baud, parity, bits, flow);
++}
++
++static struct console s3c2410_console = {
++ name: SERIAL_S3C2410_NAME,
++ write: s3c2410uart_console_write,
++#ifdef used_and_not_const_char_pointer
++ read: s3c2410uart_console_read,
++#endif
++ device: s3c2410uart_console_device,
++#if 0
++ wait_key: s3c2410uart_console_wait_key,
++#endif
++ setup: s3c2410uart_console_setup,
++ flags: CON_PRINTBUFFER,
++ index: -1,
++};
++
++void __init
++s3c2410uart_console_init(void)
++{
++ //debug_pk("registering s3c2410x console\n");
++ register_console(&s3c2410_console);
++}
++
++#define S3C2410_CONSOLE &s3c2410_console
++#else
++#define S3C2410_CONSOLE NULL
++#endif
++
++static struct uart_driver s3c2410_reg = {
++ owner: THIS_MODULE,
++ normal_major: SERIAL_S3C2410_MAJOR,
++#ifdef CONFIG_DEVFS_FS
++ normal_name: SERIAL_S3C2410_NAME "%d",
++ callout_name: CALLOUT_S3C2410_NAME "%d",
++#else
++ normal_name: SERIAL_S3C2410_NAME,
++ callout_name: CALLOUT_S3C2410_NAME,
++#endif
++ normal_driver :&normal,
++ callout_major: CALLOUT_S3C2410_MAJOR,
++ callout_driver: &callout,
++ table: s3c2410_table,
++ termios: s3c2410_termios,
++ termios_locked: s3c2410_termios_locked,
++ minor: SERIAL_S3C2410_MINOR,
++ nr: UART_NR,
++ cons: S3C2410_CONSOLE,
++};
++
++/* ensure that the uart is put into a known state for the console,
++ * we also ensure that the fifos are reset to a good state (we have
++ * seen some very weird behviour with received data lagging 14bytes
++ * behind the input if not)
++*/
++
++static int __init
++s3c2410uart_reset(struct uart_port *port)
++{
++ /* enable and reset the fifo */
++ UART_PUT_UFCON(port, S3C2410_UFCON_DEFAULT | S3C2410_UFCON_RESETBOTH);
++ UART_PUT_UMCON(port, 0);
++ return 0;
++}
++
++static int __init
++s3c2410uart_init(void)
++{
++ int i, ret;
++
++ /* reset the uarts into an known state */
++ for (i = 0; i < UART_NR; i++)
++ {
++ s3c2410uart_reset(&s3c2410_ports[i]);
++ if(machine_is_bast()) {
++ s3c2410_ports[i].uartclk=24000000;
++ } else if(machine_is_vr1000()) {
++ s3c2410_ports[i].uartclk=3692307;
++ }
++ }
++
++ ret = uart_register_driver(&s3c2410_reg);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < UART_NR; i++)
++ uart_add_one_port(&s3c2410_reg, &s3c2410_ports[i]);
++
++ return 0;
++}
++
++static void __exit
++s3c2410uart_exit(void)
++{
++ int i;
++
++ for (i = 0; i < UART_NR; i++)
++ uart_remove_one_port(&s3c2410_reg, &s3c2410_ports[i]);
++
++ uart_unregister_driver(&s3c2410_reg);
++}
++
++module_init(s3c2410uart_init);
++module_exit(s3c2410uart_exit);
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_AUTHOR("Steve Hein <ssh at sgi.com>");
++MODULE_DESCRIPTION("Samsung S3C2410X serial port driver");
++
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/serial/sa1100.c kernel-source-2.4.27-8-arm-1/drivers/serial/sa1100.c
+--- kernel-source-2.4.27-8/drivers/serial/sa1100.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/sa1100.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/mach/serial_sa1100.h>
++
++#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++/* 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 kernel-source-2.4.27-8/drivers/serial/uart00.c kernel-source-2.4.27-8-arm-1/drivers/serial/uart00.c
+--- kernel-source-2.4.27-8/drivers/serial/uart00.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/serial/uart00.c 2005-02-18 17:48:43.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 <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++#include <linux/pld/pld_hotswap.h>
++#include <linux/proc_fs.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++#include <asm/sizes.h>
++
++#if defined(CONFIG_SERIAL_UART00_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++#include <asm/arch/excalibur.h>
++#define UART00_TYPE (volatile unsigned int*)
++#include <asm/arch/uart00.h>
++#include <asm/arch/int_ctrl00.h>
++
++#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;i<UART_NR;i++){
++ if(dev_port_map[i].port){
++ uart_unregister_port(&uart00_reg,i);
++ dev_port_map[i].port=NULL;
++ }
++ }
++ return 0;
++
++}
++
++#ifdef CONFIG_PROC_FS
++
++
++int uart00_proc_read(char* buf,char** start,off_t offset,int count,int *eof,void *data){
++
++ int i,len=0;
++ struct uart_port *port;
++ int limit = count - 80;
++ char ps_data[80];
++ if(*start)
++ buf=*start;
++ for(i=0;(i<UART_NR)&&(len<limit);i++){
++ if(dev_port_map[i].port){
++ port=dev_port_map[i].port;
++ sprintf(ps_data,"clk, %dHz, fifo size, %dbytes",
++ port->uartclk,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<UART_NR;i++){
++ uart00_ports[i].ops=&uart00_pops;
++ }
++
++ printk(KERN_WARNING "uart00:Using temporary major/minor pairs - these WILL change in the future\n");
++
++#ifdef CONFIG_PLD_HOTSWAP
++ pldhs_register_driver(&uart00_pldhs_ops);
++#endif
++ for (i=0; i<UART_NR; i++)
++ uart_add_one_port(&uart00_reg,&uart00_ports[i]);
++
++ uart00_console.flags = 0;
++ register_console(&uart00_console);
++#ifdef CONFIG_ARCH_CAMELOT
++ dev_port_map[0].port=uart00_ports;
++#endif
++ return ret;
++}
++
++
++__initcall(uart00_init);
++
++
+diff -urN kernel-source-2.4.27-8/drivers/sound/.version kernel-source-2.4.27-8-arm-1/drivers/sound/.version
+--- kernel-source-2.4.27-8/drivers/sound/.version 1997-11-10 07:01:54.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/.version 1970-01-01 01:00:00.000000000 +0100
+@@ -1,2 +0,0 @@
+-3.8s
+-0x030804
+diff -urN kernel-source-2.4.27-8/drivers/sound/Config.in kernel-source-2.4.27-8-arm-1/drivers/sound/Config.in
+--- kernel-source-2.4.27-8/drivers/sound/Config.in 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/Config.in 2005-02-18 17:48:43.000000000 +0000
+@@ -131,6 +131,22 @@
+ dep_tristate ' VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX $CONFIG_PCI
+ dep_mbool ' VIA 82C686 MIDI' CONFIG_MIDI_VIA82CXXX $CONFIG_SOUND_VIA82CXXX
+
++if [ "$CONFIG_CPU_S3C2410X" = "y" ]; then
++ dep_bool ' Samsung S3C2410X Audio Driver' CONFIG_SOUND_S3C2410 $CONFIG_SOUND
++ dep_tristate ' TLV320AIC23 Audio Codec driver' CONFIG_SOUND_S3C2410_TLV320AIC23 $CONFIG_SOUND_S3C2410
++fi
++
++if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
++ dep_tristate ' StrongARM-11x0 Sound Drivers' CONFIG_SOUND_SA1100 $CONFIG_ARCH_SA1100 $CONFIG_SOUND
++ dep_tristate ' UDA1341 Stereo Codec' CONFIG_SOUND_UDA1341 $CONFIG_L3 $CONFIG_SOUND_SA1100 $CONFIG_SOUND
++ dep_tristate ' Assabet audio support' CONFIG_SOUND_ASSABET_UDA1341 $CONFIG_SA1100_ASSABET $CONFIG_SOUND_UDA1341
++ dep_tristate ' Compaq iPAQ audio support' CONFIG_SOUND_H3600_UDA1341 $CONFIG_SA1100_H3600 $CONFIG_SOUND_UDA1341
++ dep_tristate ' Pangolin audio support' CONFIG_SOUND_PANGOLIN_UDA1341 $CONFIG_SA1100_PANGOLIN $CONFIG_SOUND_UDA1341
++ dep_tristate ' SA1111 audio support' CONFIG_SOUND_SA1111_UDA1341 $CONFIG_SA1111 $CONFIG_SOUND_UDA1341
++ dep_tristate ' SA1111 AC97 Sound' CONFIG_SOUND_SA1111_AC97 $CONFIG_SA1111 $CONFIG_SOUND_SA1100
++ dep_tristate ' Generic DAC on the SA11x0 SSP port' CONFIG_SOUND_SA1100SSP $CONFIG_SOUND_SA1100
++fi
++
+ dep_tristate ' OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
+
+ if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
+@@ -221,13 +237,13 @@
+ fi
+ fi
+
+- if [ "$CONFIG_ARM" = "y" ]; then
+- if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" ]; then
++fi
++
++if [ "$CONFIG_ARM" = "y" ]; then
++ if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" -o "$CONFIG_ARCH_RISCSTATION" ]; then
+ dep_tristate ' VIDC 16-bit sound' CONFIG_SOUND_VIDC $CONFIG_SOUND_OSS
+ fi
+ dep_tristate ' Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS $CONFIG_ARCH_NETWINDER
+- fi
+-
+ fi
+
+ dep_tristate ' TV card (bt848) mixer support' CONFIG_SOUND_TVMIXER $CONFIG_SOUND $CONFIG_I2C
+diff -urN kernel-source-2.4.27-8/drivers/sound/Makefile kernel-source-2.4.27-8-arm-1/drivers/sound/Makefile
+--- kernel-source-2.4.27-8/drivers/sound/Makefile 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/Makefile 2005-02-18 17:48:43.000000000 +0000
+@@ -10,7 +10,8 @@
+ export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \
+ msnd.o opl3.o sb_common.o sequencer_syms.o \
+ sound_core.o sound_syms.o uart401.o \
+- nm256_audio.o ac97.o ac97_codec.o aci.o
++ nm256_audio.o ac97.o ac97_codec.o aci.o \
++ sa1100-audio.o
+
+ # Each configuration option enables a list of files.
+
+@@ -76,6 +77,16 @@
+ obj-$(CONFIG_SOUND_FORTE) += forte.o ac97_codec.o
+ obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o
+ obj-$(CONFIG_SOUND_HARMONY) += harmony.o
++obj-$(CONFIG_SOUND_S3C2410) += s3c2410-audio.o
++obj-$(CONFIG_SOUND_S3C2410_TLV320AIC23) += tlv320aic23.o s3c2410-tlv320aic23.o
++obj-$(CONFIG_SOUND_SA1100) += sa1100-audio.o
++obj-$(CONFIG_SOUND_UDA1341) += uda1341.o
++obj-$(CONFIG_SOUND_ASSABET_UDA1341) += assabet-uda1341.o
++obj-$(CONFIG_SOUND_PANGOLIN_UDA1341) += pangolin-uda1341.o
++obj-$(CONFIG_SOUND_H3600_UDA1341) += h3600-uda1341.o
++obj-$(CONFIG_SOUND_SA1111_UDA1341) += sa1111-uda1341.o
++obj-$(CONFIG_SOUND_SA1111_AC97) += sa1111-ac97.o ac97_codec.o
++obj-$(CONFIG_SOUND_SA1100SSP) += sa1100ssp.o
+ obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o
+ obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
+ obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o
+diff -urN kernel-source-2.4.27-8/drivers/sound/assabet-uda1341.c kernel-source-2.4.27-8-arm-1/drivers/sound/assabet-uda1341.c
+--- kernel-source-2.4.27-8/drivers/sound/assabet-uda1341.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/assabet-uda1341.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,404 @@
++/*
++ * Glue audio driver for the SA1110 Assabet board & Philips UDA1341 codec.
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico at cam.org>
++ *
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/cpufreq.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++#include <asm/arch/assabet.h>
++
++#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 kernel-source-2.4.27-8/drivers/sound/h3600-uda1341.c kernel-source-2.4.27-8-arm-1/drivers/sound/h3600-uda1341.c
+--- kernel-source-2.4.27-8/drivers/sound/h3600-uda1341.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/h3600-uda1341.c 2005-02-18 17:48:43.000000000 +0000
+@@ -0,0 +1,352 @@
++/*
++ * Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec.
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico at cam.org>
++ *
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++//#include <asm/arch/h3600_hal.h>
++
++#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 kernel-source-2.4.27-8/drivers/sound/pangolin-uda1341.c kernel-source-2.4.27-8-arm-1/drivers/sound/pangolin-uda1341.c
+--- kernel-source-2.4.27-8/drivers/sound/pangolin-uda1341.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/pangolin-uda1341.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,322 @@
++/*
++ * Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec.
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico at cam.org>
++ *
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++
++#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 kernel-source-2.4.27-8/drivers/sound/s3c2410-audio.c kernel-source-2.4.27-8-arm-1/drivers/sound/s3c2410-audio.c
+--- kernel-source-2.4.27-8/drivers/sound/s3c2410-audio.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/s3c2410-audio.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,983 @@
++/*
++ * Common audio handling for the S3C2410 processor
++ *
++ *
++ * 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 S3C2410 chip. All features depending on specific
++ * hardware implementations like supported audio formats or samplerates are
++ * relegated to separate specific modules.
++ *
++ *
++ * 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.
++ *
++ * 2002-03-4 Sangwook Lee <hitchcar at samsung.co.kr>
++ * modified for s3c2410
++ *
++ * 2003-Jul-12 Ben Dooks <ben at simtec.co.uk>
++ * modified for arch-bast
++ *
++ * 2004-May-24 Ben Dooks <ben at simtec.co.uk>
++ * fixed build warnings
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/poll.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/sysrq.h>
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/semaphore.h>
++#include <asm/dma.h>
++#include "s3c2410-audio.h"
++
++#define DEBUG_IOCTL 0
++
++#if defined(DEBUG)
++#define DPRINTK( x... ) printk( ##x )
++#define swldebug(x...) printk( ##x )
++#else
++#define DPRINTK( x... )
++#define swldebug( x... )
++#endif
++
++/* dma client information */
++
++s3c2410_dma_client_t s3c2410_audio_out_client = {
++ .name = "S3C2410 Audio Output"
++};
++
++s3c2410_dma_client_t s3c2410_audio_in_client = {
++ .name = "S3C2410 Audio Input"
++};
++
++
++#define AUDIO_NAME "s3c2410-audio"
++#define AUDIO_NBFRAGS_DEFAULT 16
++
++
++/* 8192 works on 22.05Khz but not 44.1Khz */
++
++#define AUDIO_FRAGSIZE_DEFAULT 16384
++
++
++#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(KERN_DEBUG "audio_clear_buf\n");
++
++ /* ensure DMA won't run anymore */
++ s->active = 0;
++ s->stopped = 0;
++ s3c2410_dma_ctrl(s->dma_ch, S3C2410_DMAOP_FLUSH);
++
++ 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(KERN_DEBUG "buf %d: virt_start %p dma_addr %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->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;
++ s3c2410_dma_ctrl(s->dma_ch, S3C2410_DMAOP_FLUSH);
++ 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->fragcount = 0;
++}
++
++
++/*
++ * DMA callback functions
++ */
++
++static void
++audio_dmaout_done_callback(s3c2410_dma_chan_t *dmach,
++ void *buf_id, int size,
++ s3c2410_dma_buffresult_t result)
++{
++ audio_buf_t *b = (audio_buf_t *) buf_id;
++ audio_stream_t *s = b->stream;
++
++ /* Accounting */
++ s->bytecount += size;
++ s->fragcount++;
++
++ DPRINTK(KERN_DEBUG __FUNCTION__ ": s=%p, s->mapped=%d\n",
++ s, s->mapped);
++
++ /* Recycle buffer */
++ if (s->mapped) {
++ s3c2410_dma_enqueue(s->dma_ch, buf_id,
++ b->dma_addr, s->fragsize);
++ }
++ else{
++ DPRINTK(KERN_DEBUG "up(&b->sem %p)\n", &b->sem);
++ up(&b->sem);
++ }
++ /* And any process polling on write. */
++ wake_up(&s->wq);
++}
++
++static void
++audio_dmain_done_callback(s3c2410_dma_chan_t *dmach,
++ void *buf_id, int size,
++ s3c2410_dma_buffresult_t result)
++{
++ 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) {
++ s3c2410_dma_enqueue(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(KERN_DEBUG "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);
++ s3c2410_dma_enqueue(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;
++
++ // printk("\n 1~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \n");
++ // printk(__FUNCTION__ " continue write fucntion here \n");
++
++ 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;
++ DPRINTK(KERN_DEBUG __FUNCTION__ "waiting for b->sem\n");
++ if (down_trylock(&b->sem)) break;
++ DPRINTK(KERN_DEBUG __FUNCTION__ "finished waiting for b->sem\n");
++ } else {
++ ret = -ERESTARTSYS;
++ DPRINTK(KERN_DEBUG __FUNCTION__ ": down_interruptable(&b->sem %p)\n", &b->sem);
++ if (down_interruptible(&b->sem)) break;
++ DPRINTK(KERN_DEBUG __FUNCTION__ ": down_interruptable(&b->sem) returned\n");
++ }
++
++ /* Feed the current buffer */
++ chunksize = s->fragsize - b->size;
++ DPRINTK(KERN_DEBUG __FUNCTION__ ": s->fragsize=0x%08X b->size=0x%08X count=0x%08X,nblock=%d \n", s->fragsize, b->size, count, file->f_flags & O_NONBLOCK);
++
++ if (chunksize > count) chunksize = count;
++
++ DPRINTK(KERN_DEBUG "write %d to buf_idx %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) {
++ DPRINTK(KERN_DEBUG __FUNCTION__ ": b->size=0x%08X s->fragsize=0x%08X\n",b->size,s->fragsize);
++ up(&b->sem);
++ break;
++ }
++
++ /*
++ * Send current buffer to dma
++ * b->dma_addr is physical address
++ */
++ s->active = 1;
++ s3c2410_dma_enqueue(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(KERN_DEBUG "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 S3C2410.
++ * 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.
++ */
++#ifdef S3C2410_AUDIO_REC
++ if (state->need_tx_for_rx && !state->tx_spinning) {
++ s3c2410_dma_set_spin(state->output_stream->dma_ch,
++ (dma_addr_t)FLUSH_BASE_PHYS, 2048);
++ state->tx_spinning = 1;
++ }
++#endif
++}
++
++
++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);
++ s3c2410_dma_enqueue(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 */
++ s3c2410_dma_enqueue(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 (is->bytecount > 0)
++ 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 > 0)
++ 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;
++
++#if DEBUG_IOCTL
++ printk(KERN_DEBUG "audio_ioctl(%p,%p,%08x,%08x)\n",
++ inode, file, cmd, arg);
++#endif
++
++ /* 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_STEREO:
++ //printk("DSP_STEREO(%08x)\n", arg);
++ return put_user(1, (int *)arg);
++
++ case SNDCTL_DSP_CHANNELS:
++ //printk("DSP_CHANNELS(%08x)\n", arg);
++ return put_user(2, (int *)arg);
++
++ 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;
++ s3c2410_dma_ctrl(is->dma_ch,
++ S3C2410_DMAOP_RESUME);
++ }
++ } else {
++ s3c2410_dma_ctrl(is->dma_ch,
++ S3C2410_DMAOP_STOP);
++ 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;
++ s3c2410_dma_ctrl(os->dma_ch,
++ S3C2410_DMAOP_RESUME);
++ }
++ } else {
++ s3c2410_dma_ctrl(os->dma_ch,
++ S3C2410_DMAOP_STOP);
++ 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;
++ 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);
++ offset = 0;
++ bytecount = s->bytecount + offset;
++ s->bytecount = -offset;
++ 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) {
++ 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
++ * your can find other functions in s3c2410-uda1341.c
++ * or s3c2410-tlv320aic23.c
++ */
++ 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) {
++ audio_clear_buf(state->input_stream);
++ if (!state->skip_dma_init) {
++ s3c2410_dma_free(state->input_stream->dma_ch,
++ &s3c2410_audio_in_client);
++ if (state->need_tx_for_rx && !state->wr_ref)
++ s3c2410_dma_free(state->output_stream->dma_ch,
++ &s3c2410_audio_out_client);
++ }
++ 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) {
++ s3c2410_dma_free(state->output_stream->dma_ch,
++ &s3c2410_audio_out_client);
++ }
++ state->wr_ref = 0;
++ }
++
++ if ((file->f_mode & (FMODE_READ|FMODE_WRITE)) ==
++ (FMODE_READ|FMODE_WRITE)) {
++ state->rdwr_ref = 0;
++ }
++
++ if (!AUDIO_ACTIVE(state)) {
++ if (state->hw_shutdown)
++ state->hw_shutdown(state->data);
++ }
++
++ up(&state->sem);
++ return 0;
++}
++
++extern int s3c2410_audio_attach(struct inode *inode,
++ struct file *file,
++ audio_state_t *state)
++{
++ int err, need_tx_dma;
++
++ 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;
++
++ /* check to see if this is a single open for read/write, or
++ * wether we've already been opened for a read or write operation
++ * before
++ */
++
++ if ((file->f_mode & (FMODE_READ|FMODE_WRITE)) ==
++ (FMODE_READ|FMODE_WRITE) && !state->rd_ref && !state->wr_ref) {
++ state->rdwr_ref = 1;
++ }
++
++ /* request DMA channels */
++
++ need_tx_dma = (file->f_mode & FMODE_WRITE);
++ if (state->wr_ref)
++ need_tx_dma = 0;
++
++ if (need_tx_dma) {
++ err = s3c2410_dma_request(state->output_stream->dma_ch,
++ state->output_id,
++ /* (void *)state->output_dma */ NULL);
++ if (err) {
++ printk(KERN_ERR "s3c2410-audio: s3c2410_dma_request() failed for output (%d)\n", err);
++ goto out;
++ }
++
++ s3c2410_dma_setflags(state->output_stream->dma_ch,
++ S3C2410_DMAF_AUTOSTART);
++
++ s3c2410_dma_set_buffdone_fn(state->output_stream->dma_ch,
++ audio_dmaout_done_callback);
++ }
++
++ if ((file->f_mode & FMODE_READ)) {
++ err = s3c2410_dma_request(state->input_stream->dma_ch,
++ state->input_id, NULL);
++
++ if (err) {
++ printk(KERN_ERR "s3c2410-audio: s3c2410_dma_request() failed for input (%d)\n", err);
++ goto out;
++ }
++
++ s3c2410_dma_setflags(state->input_stream->dma_ch,
++ S3C2410_DMAF_AUTOSTART);
++
++ s3c2410_dma_set_buffdone_fn(state->input_stream->dma_ch,
++ audio_dmain_done_callback);
++ }
++
++ /* now complete initialisation */
++ if (!AUDIO_ACTIVE(state)) {
++ if (state->hw_init)state->hw_init(state->data);
++ }
++
++ 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;
++ 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;
++ 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(s3c2410_audio_attach);
++
++MODULE_AUTHOR("Nicolas Pitre, Ben Dooks");
++MODULE_DESCRIPTION("Common audio handling for the S3C2410 processor");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/sound/s3c2410-audio.h kernel-source-2.4.27-8-arm-1/drivers/sound/s3c2410-audio.h
+--- kernel-source-2.4.27-8/drivers/sound/s3c2410-audio.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/s3c2410-audio.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,75 @@
++/*
++ * Common audio handling for the S3C2400
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico at cam.org>
++ * Copyright (c) 2004 Simtec Electronics
++ *
++ * 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 */
++ struct semaphore *wsem; /* down before touching the buffer */
++} 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 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;
++ s3c2410_dma_client_t *output_id;
++ s3c2410_dma_client_t *input_id;
++
++ int rd_ref:1; /* open reference for recording */
++ int wr_ref:1; /* open reference for playback */
++ int rdwr_ref:1; /* channel was opened for both */
++ 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 S3C2400 */
++
++ 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 s3c2410_audio_attach( struct inode *inode, struct file *file,
++ audio_state_t *state);
++
++
++extern s3c2410_dma_client_t s3c2410_audio_in_client;
++extern s3c2410_dma_client_t s3c2410_audio_out_client;
++
+diff -urN kernel-source-2.4.27-8/drivers/sound/s3c2410-tlv320aic23.c kernel-source-2.4.27-8-arm-1/drivers/sound/s3c2410-tlv320aic23.c
+--- kernel-source-2.4.27-8/drivers/sound/s3c2410-tlv320aic23.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/s3c2410-tlv320aic23.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,847 @@
++/* linux/drivers/sound/s3c2410-tlv320aic23.c
++ *
++ * Glue audio driver for the S3C2410 & TI TLV320AIC23 driver
++ *
++ * Copyright (c) 2000 John Dorsey
++ * Copyright (c) 2003,2004 Simtec Electronics
++ *
++ * 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.
++ * 2002-02-26 Sangwook Lee Modifed for S3C2400 <hitchcar at samsung.co.kr>
++ * 2002-06-10 Sangwook Lee Modified for S3C2410 <hitchcar at sec.samsung.com>
++ * 2003-06-02 Ben Dooks Copied for TLV320AIC23 <ben at simtec.co.uk>
++ * 2004-05-24 Ben Dooks Build tidy
++ * 2004-06-15 Ben Dooks Fixed errors in record selection and mixing
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/pm.h>
++
++#include <linux/i2c.h>
++
++#include <asm/semaphore.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++
++#include <asm/io.h>
++#include <asm/arch/hardware.h>
++
++#include <asm/arch-s3c2410/S3C2410-iis.h>
++#include <asm/arch-s3c2410/S3C2410-gpio.h>
++
++#include "s3c2410-audio.h"
++#include "tlv320aic23.h"
++#include "tlv320aic23-regs.h"
++
++/* debug settings */
++
++#define DEBUG_TXDMAOP (0)
++#define DEBUG_RXDMAOP (0)
++
++#if 0
++#define DPRINTK(x...) printk(KERN_DEBUG "tlv320aic23: " ## x)
++#else
++#define DPRINTK(x...)
++#endif
++#define DPRINTKR(x...)
++
++struct tlv_mixer {
++ unsigned short line; /* line input - 80 steps */
++ unsigned short volume; /* hp volume - 32 steps, 1.5dB/step */
++ unsigned short mic;
++ unsigned short aapc_reg; /* current setting for analog audio path */
++ unsigned int rec_sel; /* record selection */
++};
++
++/* we scale the headphone volume by 3, and add offset 4 to get to the
++ * register value
++ *
++ * line is simply shifted by 20
++ */
++
++#define get_l(val) ((val) & 0xff)
++#define get_r(val) (((val) & 0xff00) >> 8)
++
++#define get_hpvol(val) ((val) < 20 ? 0 : ((val) + 27))
++#define get_lvol(val) ((val - 6) / 3)
++
++static void tlv_wr(unsigned int reg, unsigned int val);
++
++/* tlv_write_audiopath
++ *
++ * configure the audio path, such as microphone inputs etc
++*/
++
++static int
++tlv_write_audiopath(struct tlv_mixer *mix)
++{
++ if (mix->rec_sel & SOUND_MASK_LINE) {
++ mix->aapc_reg &= ~TLV320AIC23_APATH_MICADC;
++ } else {
++ mix->aapc_reg |= TLV320AIC23_APATH_MICADC;
++ }
++
++ mix->aapc_reg &= ~(TLV320AIC23_APATH_MICMUTE | TLV320AIC23_APATH_MICBOOST);
++
++ if (mix->mic > 50) {
++ mix->aapc_reg |= TLV320AIC23_APATH_MICBOOST;
++ } else if (mix->mic == 0) {
++ mix->aapc_reg |= TLV320AIC23_APATH_MICMUTE;
++ }
++
++ /* hack -> if mic muted, and line not muted, then we'll go and
++ * select the line over the microphone, since not many utilities
++ * seem to bother with the ability to select the input
++ */
++ if (mix->mic == 0 && mix->line > 0) {
++ mix->aapc_reg &= ~TLV320AIC23_APATH_MICADC;
++ }
++
++ tlv_wr(TLV320AIC23_ANALOGPATH, mix->aapc_reg);
++ return 0;
++}
++
++/* tlv_write_mixer
++ *
++ * set the current volume settings in the tlv320aic23 to the specified
++ * values.
++*/
++
++static int
++tlv_write_mixer(struct tlv_mixer *mix)
++{
++ int l, r;
++
++ DPRINTK("tlv_write_mixer: %p: vol=%d, mic=%d, line=%d, rec=%08x\n",
++ mix, mix->volume, mix->mic, mix->line, mix->rec_sel);
++
++ l = get_l(mix->volume);
++ r = get_r(mix->volume);
++ tlv_wr(TLV320AIC23_LHPVOL, TLV320AIC23_HPVOL_ZEROCROSS | get_hpvol(l));
++ tlv_wr(TLV320AIC23_RHPVOL, TLV320AIC23_HPVOL_ZEROCROSS | get_hpvol(r));
++
++ l = get_l(mix->line);
++ if (l < 4) {
++ tlv_wr(TLV320AIC23_LINPVOL, TLV320AIC23_INP_MUTE);
++ } else {
++ tlv_wr(TLV320AIC23_LINPVOL, get_lvol(l));
++ }
++
++ r = get_r(mix->line);
++ if (r < 4) {
++ tlv_wr(TLV320AIC23_RINPVOL, TLV320AIC23_INP_MUTE);
++ } else {
++ tlv_wr(TLV320AIC23_RINPVOL, get_lvol(r));
++ }
++
++ /* update mic volume */
++ tlv_write_audiopath(mix);
++
++ return 0;
++}
++
++/* 3dB input, 0dB output */
++#define DEFAULT_INP ((TLV320AIC23_INP_0dB + 2 + 6) * 3)
++#define DEFAULT_VOL ((TLV320AIC23_HPVOL_0dB - 27))
++
++#define make_vol(l,r) ((l) | ((r) << 8))
++
++/* default values for the tlv */
++static struct tlv_mixer tlv_mixer = {
++ line: make_vol(DEFAULT_INP, DEFAULT_INP),
++ volume: make_vol(DEFAULT_VOL, DEFAULT_VOL),
++ mic: 0,
++ rec_sel: 0
++};
++
++
++static int
++mixer_ioctl(struct inode *inode,
++ struct file *file,
++ uint cmd, ulong arg)
++{
++ struct tlv_mixer *mix = &tlv_mixer;
++ long val;
++ int ret;
++
++ /*
++ * We only accept mixer (type 'M') ioctls.
++ */
++
++#if 0
++ DPRINTK(__FUNCTION__ ": inode=%p, file=%p, cmd=%d (dir=%d, type=%d, nr=%d, size=%d), arg=%08x\n",
++ inode, file, cmd,
++ _IOC_DIR(cmd), _IOC_TYPE(cmd),
++ _IOC_NR(cmd), _IOC_SIZE(cmd),
++ (unsigned int)arg);
++#endif
++
++ if (_IOC_TYPE(cmd) != 'M') {
++ DPRINTK(__FUNCTION__ ": _IOC_TYPE(cmd)=%c\n", _IOC_TYPE(cmd));
++ return -EINVAL;
++ }
++
++ switch (cmd) {
++ case OSS_GETVERSION:
++ DPRINTK("mixer: OSS_GETVERSION\n");
++ return put_user(SOUND_VERSION ,(int *)arg);
++
++ case SOUND_MIXER_READ_DEVMASK:
++ DPRINTKR(__FUNCTION__ ": SOUND_MIXER_READ_DEVMASK:\n");
++ return put_user(SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC, (int *)arg);
++
++ case SOUND_MIXER_READ_RECMASK:
++ DPRINTKR(__FUNCTION__ ": SOUND_MIXER_READ_RECMASK:\n");
++ return put_user(SOUND_MASK_MIC | SOUND_MASK_LINE, (int *)arg);
++
++ case SOUND_MIXER_READ_STEREODEVS:
++ DPRINTKR(__FUNCTION__ ": SOUND_MIXER_READ_STEREODEVS:\n");
++ return put_user(SOUND_MIXER_LINE | SOUND_MIXER_VOLUME, (int *)arg);
++
++ case SOUND_MIXER_READ_RECSRC:
++ DPRINTKR(__FUNCTION__ ": SOUND_MIXER_READ_RECSRC:\n");
++ return put_user(SOUND_MASK_MIC | SOUND_MASK_LINE, (int *)arg);
++
++ case SOUND_MIXER_GETLEVELS:
++ DPRINTK("mixer: SOUND_MIXER_GETLEVELS\n");
++ return -EINVAL;
++
++ case SOUND_MIXER_SETLEVELS:
++ DPRINTK("mixer: SOUND_MIXER_SETLEVELS\n");
++ return -EINVAL;
++
++ case SOUND_MIXER_INFO:
++ DPRINTK("mixer: SOUND_MIXER_INFO\n");
++ return -EINVAL;
++
++ case SOUND_OLD_MIXER_INFO:
++ DPRINTK("mixer: SOUND_OLD_MIXER_INFO\n");
++ return -EINVAL;
++
++ case SOUND_MIXER_READ_CD:
++ DPRINTK(__FUNCTION__ ": SOUND_MIXER_READ_CD\n");
++ return put_user(0, (int *)arg);
++
++ case SOUND_MIXER_READ_LINE:
++ DPRINTKR(__FUNCTION__ ": SOUND_MIXER_READ_LINE\n");
++ return put_user(mix->line, (int *)arg);
++
++ case SOUND_MIXER_READ_MIC:
++ DPRINTKR(__FUNCTION__ ": SOUND_MIXER_READ_MIC\n");
++ return put_user(mix->mic, (int *)arg);
++
++ case SOUND_MIXER_READ_VOLUME:
++ DPRINTKR(__FUNCTION__ ": SOUND_MIXER_READ_VOLUME\n");
++ return put_user(mix->volume, (int *)arg);
++
++ case SOUND_MIXER_READ_CAPS:
++ DPRINTK(__FUNCTION__ ": SOUND_MIXER_READ_CAPS\n");
++ return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
++
++ case SOUND_MIXER_WRITE_MIC:
++ DPRINTK(__FUNCTION__ ": SOUND_MIXER_WRITE_MIC:\n");
++ ret = get_user(val, (long *) arg);
++
++ if (ret)
++ return ret;
++
++ mix->mic = val;
++ return tlv_write_mixer(mix);
++
++ case SOUND_MIXER_WRITE_LINE:
++ DPRINTK(__FUNCTION__ ": SOUND_MIXER_WRITE_LINE:\n");
++ ret = get_user(val, (long *) arg);
++
++ if (ret)
++ return ret;
++
++ mix->line = val;
++ return tlv_write_mixer(mix);
++
++ case SOUND_MIXER_WRITE_VOLUME:
++ DPRINTK(__FUNCTION__ ": SOUND_MIXER_WRITE_VOLUME:\n");
++ ret = get_user(val, (long *) arg);
++
++ if (ret) {
++ DPRINTK(__FUNCTION__ ": get_user() error %d\n", ret);
++ return ret;
++ }
++
++ DPRINTK(__FUNCTION__ ": SOUND_MIXER_WRITE_VOLUME: %ld\n", val);
++ mix->volume = val;
++ return tlv_write_mixer(mix);
++
++ case SOUND_MIXER_WRITE_RECSRC:
++ DPRINTK(__FUNCTION__ ": SOUND_MIXER_WRITE_VOLUME:\n");
++
++ ret = get_user(val, (long *)arg);
++ if (ret) {
++ DPRINTK(__FUNCTION__ ": get_user() error %d\n", ret);
++ return ret;
++ }
++
++ if (val == 0)
++ val = SOUND_MASK_MIC;
++
++ if ((val & (SOUND_MASK_MIC | SOUND_MASK_LINE)) == 0) {
++ DPRINTK(__FUNCTION__ ": invalid input selection\n");
++ return -EINVAL;
++ }
++
++ mix->rec_sel = val;
++
++ return tlv_write_audiopath(mix);
++
++ }
++
++ DPRINTK(__FUNCTION__ ": unhandled ioctl(%08x)\n", cmd);
++
++ return -EINVAL;
++}
++
++static struct file_operations tlv320aic23_mixer_fops = {
++ ioctl: mixer_ioctl,
++ owner: THIS_MODULE
++};
++
++static unsigned int bitrate = 44100;
++
++static int s3c2410_audio_ioctl(struct inode *inode,
++ struct file *file,
++ uint cmd, ulong arg)
++{
++ audio_state_t *state = (audio_state_t *)file->private_data;
++ long val = 0;
++ int ret = 0;
++ int sr;
++
++ DPRINTK(__FUNCTION__ ": inode=%p, file=%p, cmd=%d, arg=%08x\n",
++ inode, file, cmd, (unsigned int)arg);
++
++ switch (cmd) {
++ case SNDCTL_DSP_STEREO:
++ ret = get_user(val, (int *) arg);
++ if (ret)
++ return ret;
++ /* think the TLV320AIC23 is stereo only */
++ ret = (val == 0) ? -EINVAL : 1;
++ return put_user(ret, (int *) arg);
++
++ case SNDCTL_DSP_CHANNELS:
++ case SOUND_PCM_READ_CHANNELS:
++ /* think the TLV320AIC23 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 > 96000) val = 96000;
++
++ DPRINTK("dsp_speed: val=%d, bitrate=%d, r/w/b=%d,%d,%d\n",
++ val, bitrate, state->rd_ref, state->wr_ref,
++ state->rdwr_ref);
++
++ if (state->rd_ref && state->wr_ref && !state->rdwr_ref &&
++ val != bitrate) {
++ /* open for read/write, but very probably not by
++ * the same application, we cannot do split-rates
++ * so we'd better just tell them NO */
++
++ printk(KERN_WARNING "s3c2410-tlv320aic23: cannot support split-rate, requested %ld => %ld\n", val, bitrate);
++
++ return put_user(bitrate, (long *)arg);
++ }
++
++ switch(val) {
++ case 8000:
++ sr = (3 << 2);
++ bitrate = 8000;
++ break;
++ case 8021:
++ sr = (11 <<2) | TLV320AIC23_SAMPLERATE_BOSR;
++ bitrate = 8012;
++ break;
++ case 32000:
++ sr = (5 <<2);
++ bitrate = 32000;
++ break;
++ case 44100:
++ default:
++ sr = (8 << 2) | TLV320AIC23_SAMPLERATE_BOSR;
++ bitrate = 44100;
++ break;
++ case 88200:
++ sr = (15 << 2) | TLV320AIC23_SAMPLERATE_BOSR;
++ bitrate = 88200;
++ break;
++ case 96000:
++ sr = (7 << 2);
++ bitrate = 96000;
++ break;
++ }
++
++ sr |= TLV320AIC23_SAMPLERATE_USB;
++ tlv_wr(TLV320AIC23_SAMPLERATE, sr);
++ return put_user(bitrate , (long *) arg);
++
++ case SOUND_PCM_READ_RATE:
++ return put_user(bitrate , (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 void s3c2410_audio_init(void *dummy)
++{
++ /* initialise the codec */
++}
++
++static void s3c2410_audio_shutdown(void *dummy)
++{
++ /* shut down the audio code, and possibly restore any changed CPU PLL
++ * control */
++
++}
++
++static audio_stream_t input_stream;
++static audio_stream_t output_stream;
++
++static audio_state_t audio_state = {
++ input_stream: &input_stream,
++ output_stream: &output_stream,
++ skip_dma_init: 0, /* done locally */
++ hw_init: s3c2410_audio_init,
++ hw_shutdown: s3c2410_audio_shutdown,
++ client_ioctl: s3c2410_audio_ioctl,
++ sem: __MUTEX_INITIALIZER(audio_state.sem),
++};
++
++
++#define bit(bval,bit) (((bval) & 1) << (bit))
++
++#define bits9(b8,b7,b6,b5,b4,b3,b2,b1,b0) ((bit(b8,8)) | (bit(b7,7)) | (bit(b6,6)) | (bit(b5,5)) | (bit(b4,4)) | (bit(b3,3)) | (bit(b2,2)) | (bit(b1,1)) | (bit(b0,0)))
++
++extern int tlv320aic23_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++static int s3c2410_tlv320aic23_initialised = 0;
++
++static void tlv_wr(unsigned int reg, unsigned int val)
++{
++ unsigned char wr[2];
++ int ret;
++
++ wr[0] = reg << 1;
++ wr[0] |= (val >> 8) & 1;
++ wr[1] = val & 0xff;
++
++ DPRINTK("tlv_wr: reg=%02x, val=%02x, wr=%02x,%02x\n",
++ reg, val, wr[0], wr[1]);
++
++ ret = tlv320aic23_command(tlv320aic23_i2c_client,
++ TLV320AIC23_REG_WRITE, wr);
++
++ if (ret < 0) {
++ printk(KERN_ERR "tlv320aic23: write returned %d\n", ret);
++ } else {
++ DPRINTK(__FUNCTION__ ": ret=%d\n", ret);
++ }
++}
++
++/* allow audio in => output via mixing */
++static unsigned int enable_bypass = 0;
++
++static void s3c2410_tlv320aic23_setup(void)
++{
++ DPRINTK(__FUNCTION__ ":\n");
++
++ /* reset the chip */
++ tlv_wr(TLV320AIC23_RESET, 0);
++ mdelay(1000); /* think this should be ok for reset time... */
++
++ tlv_wr(TLV320AIC23_DIGITALACT, 1);
++
++ /* initialise the mixer */
++ tlv_write_mixer(&tlv_mixer);
++
++ /* configure the analogue side */
++ /* enable dac and bypass, disable microphone, adc from line,
++ * no mic boost */
++
++ if (enable_bypass) {
++ tlv_mixer.aapc_reg = bits9(0,0,0,0,1,1,0,1,0);
++ } else {
++ tlv_mixer.aapc_reg = bits9(0,0,0,0,1,0,0,1,0);
++ }
++
++ tlv_wr(TLV320AIC23_ANALOGPATH, tlv_mixer.aapc_reg);
++
++ /* configure digital-path control */
++ /* possibly the high-pass adc filter control is inverted */
++ /* de-emphasis at 44.1KHz */
++ tlv_wr(TLV320AIC23_DIGITALPATH, bits9(0,0,0,0,0,0,0,0,1));
++
++ /* enable correct power */
++ /* possibly disable clock power */
++ tlv_wr(TLV320AIC23_POWERCTRL, bits9(0,0,0,0,0,0,1,1,0));
++
++ /* sort out digital audio format */
++
++#define DFMT \
++ TLV320AIC23_DIGITALFMT_MASTER | TLV320AIC23_DIGITALFMT_16BIT | \
++ TLV320AIC23_DIGITALFMT_I2S
++
++ tlv_wr(TLV320AIC23_DIGITALFMT, DFMT);
++
++ tlv_wr(TLV320AIC23_SAMPLERATE,
++ TLV320AIC23_SAMPLERATE_USB | TLV320AIC23_SAMPLERATE_BOSR | TLV320AIC23_SAMPLERATE_USB44K1);
++
++ tlv_wr(TLV320AIC23_DIGITALACT, 1);
++
++ /* ok, done */
++ s3c2410_tlv320aic23_initialised = 1;
++}
++
++static int s3c2410_audio_dma_txop(s3c2410_dma_chan_t *chan,
++ s3c2410_chan_op_t op)
++{
++ unsigned long iisfcon, iiscon;
++ unsigned long flags;
++
++ local_irq_save(flags);
++
++ iisfcon = __raw_readl(S3C2410_IISFCON);
++ iiscon = __raw_readl(S3C2410_IISCON);
++
++#if DEBUG_TXDMAOP
++ printk(KERN_DEBUG __FUNCTION__ ": current IISFCON=%08x, IISCON=%08x\n",
++ (unsigned int)iisfcon, (unsigned int)iiscon);
++#endif
++
++ switch (op) {
++
++ case S3C2410_DMAOP_START:
++ if (tlv320aic23_i2c_client != NULL &&
++ s3c2410_tlv320aic23_initialised == 0) {
++ s3c2410_tlv320aic23_setup();
++ }
++
++#if 0
++ printk(KERN_DEBUG "enabling audio dma\n");
++#endif
++
++ iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
++ iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
++ iiscon &= ~S3C2410_IISCON_TXIDLE;
++ break;
++
++ case S3C2410_DMAOP_STOP:
++#if DEBUG_TXDMAOP
++ printk(KERN_DEBUG "disabling audio dma\n");
++#endif
++
++ /* note, we have to disable the FIFOs otherwise bad things
++ * seem to happen when the DMA stops. According to the
++ * Samsung supplied kernel, this should allow the DMA
++ * engine and FIFOs to reset. If this isn't allowed, the
++ * DMA engine will simply freeze randomly.
++ */
++
++ iisfcon &= ~S3C2410_IISFCON_TXENABLE;
++ iisfcon &= ~S3C2410_IISFCON_TXDMA;
++ iiscon |= S3C2410_IISCON_TXIDLE;
++ iiscon &= ~S3C2410_IISCON_TXDMAEN;
++ break;
++
++ case S3C2410_DMAOP_PAUSE:
++ case S3C2410_DMAOP_RESUME:
++ /* not implemented at the moment */
++ break;
++
++
++ case S3C2410_DMAOP_TIMEOUT:
++ break;
++
++ case S3C2410_DMAOP_FLUSH:
++ /* do nothing, don't think we need to deal with this */
++
++ local_irq_restore(flags);
++ return 0;
++ }
++
++#if DEBUG_TXDMAOP
++ printk(KERN_DEBUG __FUNCTION__ ": setting IISFCON=%08x, IISCON=%08x\n",
++ (unsigned int)iisfcon, (unsigned int)iiscon);
++#endif
++
++ __raw_writel(iisfcon, S3C2410_IISFCON);
++ __raw_writel(iiscon, S3C2410_IISCON);
++
++#if DEBUG_TXDMAOP
++ printk(KERN_DEBUG "IIS: FCON=%08x, CON=%08x, MOD=%08x\n",
++ __raw_readl(S3C2410_IISFCON),
++ __raw_readl(S3C2410_IISCON),
++ __raw_readl(S3C2410_IISMOD));
++#endif
++
++ local_irq_restore(flags);
++ return 0;
++}
++
++static int s3c2410_audio_dma_rxop(s3c2410_dma_chan_t *chan,
++ s3c2410_chan_op_t op)
++{
++ unsigned long iisfcon, iiscon;
++ unsigned long flags;
++
++ local_irq_save(flags);
++
++ iisfcon = __raw_readl(S3C2410_IISFCON);
++ iiscon = __raw_readl(S3C2410_IISCON);
++
++#if DEBUG_RXDMAOP
++ printk(KERN_DEBUG __FUNCTION__ ": current IISFCON=%08x, IISCON=%08x\n",
++ (unsigned int)iisfcon, (unsigned int)iiscon);
++#endif
++
++ switch (op) {
++
++ case S3C2410_DMAOP_START:
++ if (tlv320aic23_i2c_client != NULL &&
++ s3c2410_tlv320aic23_initialised == 0) {
++ s3c2410_tlv320aic23_setup();
++ }
++
++#if DEBUG_RXDMAOP
++ printk(KERN_DEBUG "enabling audio dma\n");
++#endif
++
++ iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
++ iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
++ iiscon &= ~S3C2410_IISCON_RXIDLE;
++ break;
++
++ case S3C2410_DMAOP_STOP:
++#if DEBUG_RXDMAOP
++ printk(KERN_DEBUG "disabling audio dma\n");
++#endif
++
++ /* note, we have to disable the FIFOs otherwise bad things
++ * seem to happen when the DMA stops. According to the
++ * Samsung supplied kernel, this should allow the DMA
++ * engine and FIFOs to reset. If this isn't allowed, the
++ * DMA engine will simply freeze randomly.
++ */
++
++ iisfcon &= ~S3C2410_IISFCON_RXENABLE;
++ iisfcon &= ~S3C2410_IISFCON_RXDMA;
++ iiscon |= S3C2410_IISCON_RXIDLE;
++ iiscon &= ~S3C2410_IISCON_RXDMAEN;
++ break;
++
++ case S3C2410_DMAOP_PAUSE:
++ case S3C2410_DMAOP_RESUME:
++ /* not implemented at the moment */
++ break;
++
++ case S3C2410_DMAOP_TIMEOUT:
++ /* not implemented at the moment */
++ break;
++
++ case S3C2410_DMAOP_FLUSH:
++ /* do nothing, don't think we need to deal with this */
++ local_irq_restore(flags);
++ return 0;
++ }
++
++#if DEBUG_RXDMAOP
++ printk(KERN_DEBUG __FUNCTION__ ": setting IISFCON=%08x, IISCON=%08x\n",
++ (unsigned int)iisfcon, (unsigned int)iiscon);
++#endif
++
++ __raw_writel(iisfcon, S3C2410_IISFCON);
++ __raw_writel(iiscon, S3C2410_IISCON);
++
++#if DEBUG_RXDMAOP
++ printk(KERN_DEBUG "IIS: FCON=%08x, CON=%08x, MOD=%08x\n",
++ __raw_readl(S3C2410_IISFCON),
++ __raw_readl(S3C2410_IISCON),
++ __raw_readl(S3C2410_IISMOD));
++#endif
++
++ local_irq_restore(flags);
++ return 0;
++}
++
++
++
++static int s3c2410_audio_open(struct inode *inode, struct file *file)
++{
++ /* Acquire and initialize DMA for transfer */
++
++ output_stream.dma_ch = 2;
++ input_stream.dma_ch = 1;
++ audio_state.output_id = &s3c2410_audio_out_client;
++ audio_state.input_id = &s3c2410_audio_in_client;
++
++ if (tlv320aic23_i2c_client != NULL &&
++ s3c2410_tlv320aic23_initialised == 0) {
++ s3c2410_tlv320aic23_setup();
++ }
++
++ /* configure the output dma stream... */
++ if (file->f_mode & FMODE_WRITE) {
++ s3c2410_dma_config(output_stream.dma_ch,
++ 2, S3C2410_DCON_HANDSHAKE |
++ S3C2410_DCON_SYNC_PCLK |
++ (0 << S3C2410_DCON_SRCSHIFT));
++
++ s3c2410_dma_devconfig(output_stream.dma_ch,
++ S3C2410_DMASRC_MEM, 0x3, 0x55000010);
++
++ s3c2410_dma_set_opfn(output_stream.dma_ch,
++ s3c2410_audio_dma_txop);
++ }
++
++
++ /* configure the input stream */
++ if (file->f_mode & FMODE_READ) {
++ s3c2410_dma_config(input_stream.dma_ch,
++ 2, S3C2410_DCON_HANDSHAKE |
++ S3C2410_DCON_SYNC_PCLK |
++ (2 << S3C2410_DCON_SRCSHIFT));
++
++ s3c2410_dma_devconfig(input_stream.dma_ch,
++ S3C2410_DMASRC_HW, 0x3, 0x55000010);
++
++ s3c2410_dma_set_opfn(input_stream.dma_ch,
++ s3c2410_audio_dma_rxop);
++
++ /* power on to the adc */
++ tlv_wr(TLV320AIC23_POWERCTRL, bits9(0,0,0,0,0,0,0,1,0));
++
++ }
++
++
++ return s3c2410_audio_attach(inode, file, &audio_state);
++}
++
++/*
++ * Missing fields of this structure will be patched with the call
++ * to xxx_audio_attach()
++ */
++static struct file_operations s3c2410_audio_fops = {
++ open: s3c2410_audio_open,
++ owner: THIS_MODULE
++};
++
++
++static int audio_dev_id, mixer_dev_id;
++
++extern int tlv320aic23_init(void);
++
++
++static int __init s3c2410_tlv320aic23_init(void)
++{
++ unsigned long tmp;
++
++ DPRINTK("s3c2410_tlv320aic23_init \n");
++
++ /* init codec */
++ tlv320aic23_init();
++
++ /* register devices */
++ audio_dev_id = register_sound_dsp(&s3c2410_audio_fops, -1);
++ mixer_dev_id = register_sound_mixer(&tlv320aic23_mixer_fops, -1);
++
++ /* configure clock sources and routing */
++
++ /* de-route the I2S LR clock */
++ tmp = __raw_readb(BAST_VA_CTRL1);
++ tmp &= ~BAST_CPLD_CTRL1_LRMASK;
++ tmp |= BAST_CPLD_CTRL1_LRCOFF;
++ __raw_writeb(tmp, BAST_VA_CTRL1);
++
++ /* set the s3c2410 to slave, and 16bit iis */
++ __raw_writel(S3C2410_IISMOD_SLAVE | S3C2410_IISMOD_TXRXMODE |
++ S3C2410_IISMOD_32FS | S3C2410_IISMOD_16BIT |
++ S3C2410_IISMOD_LR_LLOW, S3C2410_IISMOD);
++
++ __raw_writel(S3C2410_IISCON_RXIDLE |
++ S3C2410_IISCON_TXIDLE, S3C2410_IISCON);
++
++ printk("IISCON=%08x, IISMOD=%08x, IISFCON=%08x\n",
++ __raw_readl(S3C2410_IISCON),
++ __raw_readl(S3C2410_IISMOD),
++ __raw_readl(S3C2410_IISFCON));
++
++ /* update pin config to ensure I2S is routed to the io pins */
++ tmp = __raw_readl(S3C2410_GPECON);
++ tmp &= ~( S3C2410_GPE0_MASK | S3C2410_GPE1_MASK |
++ S3C2410_GPE3_MASK | S3C2410_GPE4_MASK );
++ tmp |= S3C2410_GPE0_I2SLRCK | S3C2410_GPE1_I2SSCLK;
++ tmp |= S3C2410_GPE3_I2SSDI | S3C2410_GPE4_I2SSDO;
++ __raw_writel(tmp, S3C2410_GPECON);
++
++ /* route the lrclock from the dac->s3c2410 */
++ tmp = __raw_readb(BAST_VA_CTRL1);
++ tmp &= ~BAST_CPLD_CTRL1_LRMASK;
++ tmp |= BAST_CPLD_CTRL1_LRCDAC;
++ __raw_writeb(tmp, BAST_VA_CTRL1);
++
++ DPRINTK("checing to see if we can setup chip...\n");
++ if (tlv320aic23_i2c_client != NULL) {
++ s3c2410_tlv320aic23_setup();
++ }
++
++ printk(KERN_INFO "S3C2410: TLV320AIC23 / IIS initialized\n");
++
++ return 0;
++}
++
++static void __exit s3c2410_tlv320aic23_exit(void)
++{
++ unregister_sound_dsp(audio_dev_id);
++ unregister_sound_mixer(mixer_dev_id);
++}
++
++module_init(s3c2410_tlv320aic23_init);
++module_exit(s3c2410_tlv320aic23_exit);
++
++MODULE_AUTHOR("Sangwook Lee, Ben Dooks");
++MODULE_DESCRIPTION("Audio driver for the S3C2410 & Philips TLV320AIC23 codec.");
++
++EXPORT_NO_SYMBOLS;
+diff -urN kernel-source-2.4.27-8/drivers/sound/sa1100-audio.c kernel-source-2.4.27-8-arm-1/drivers/sound/sa1100-audio.c
+--- kernel-source-2.4.27-8/drivers/sound/sa1100-audio.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/sa1100-audio.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,976 @@
++/*
++ * Common audio handling for the SA11x0 processor
++ *
++ * Copyright (C) 2000, 2001 Nicolas Pitre <nico at cam.org>
++ *
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/poll.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/sysrq.h>
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/semaphore.h>
++#include <asm/dma.h>
++
++#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 kernel-source-2.4.27-8/drivers/sound/sa1100-audio.h kernel-source-2.4.27-8-arm-1/drivers/sound/sa1100-audio.h
+--- kernel-source-2.4.27-8/drivers/sound/sa1100-audio.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/sa1100-audio.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,68 @@
++/*
++ * Common audio handling for the SA11x0
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico at cam.org>
++ *
++ * 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 kernel-source-2.4.27-8/drivers/sound/sa1100ssp.c kernel-source-2.4.27-8-arm-1/drivers/sound/sa1100ssp.c
+--- kernel-source-2.4.27-8/drivers/sound/sa1100ssp.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/sa1100ssp.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,182 @@
++/*
++ * Glue audio driver for a simple DAC on the SA1100's SSP port
++ *
++ * Copyright (c) 2001 Nicolas Pitre <nico at cam.org>
++ *
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++
++#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 kernel-source-2.4.27-8/drivers/sound/sa1111-ac97.c kernel-source-2.4.27-8-arm-1/drivers/sound/sa1111-ac97.c
+--- kernel-source-2.4.27-8/drivers/sound/sa1111-ac97.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/sa1111-ac97.c 2005-02-18 17:48:44.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 at technolution.nl)
++ * Copyright (c) 2002 Robert Whaley (rwhaley at 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/proc_fs.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/ac97_codec.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/dma.h>
++#include <asm/hardware/sa1111.h>
++
++#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 kernel-source-2.4.27-8/drivers/sound/sa1111-uda1341.c kernel-source-2.4.27-8-arm-1/drivers/sound/sa1111-uda1341.c
+--- kernel-source-2.4.27-8/drivers/sound/sa1111-uda1341.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/sa1111-uda1341.c 2005-02-18 17:48:44.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 <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/ioport.h>
++#include <linux/pm.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/semaphore.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++#include <asm/arch/assabet.h>
++#include <asm/hardware/sa1111.h>
++
++#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 kernel-source-2.4.27-8/drivers/sound/tlv320aic23-regs.h kernel-source-2.4.27-8-arm-1/drivers/sound/tlv320aic23-regs.h
+--- kernel-source-2.4.27-8/drivers/sound/tlv320aic23-regs.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/tlv320aic23-regs.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,83 @@
++/* linux/drivers/sound/tlv320aic23-regs.h
++ *
++ * Copyright (c) 2003 Simtec Electronics, <linux at simtec.co.uk>
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * Register definitions for TI TLV320AIC23 audio codec
++ *
++ * 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:
++ * 12-Jul-2003 BJD Created file
++ *
++*/
++
++#define TLV320AIC23_LINPVOL (0x00)
++#define TLV320AIC23_RINPVOL (0x01)
++#define TLV320AIC23_LHPVOL (0x02)
++#define TLV320AIC23_RHPVOL (0x03)
++#define TLV320AIC23_ANALOGPATH (0x04)
++#define TLV320AIC23_DIGITALPATH (0x05)
++#define TLV320AIC23_POWERCTRL (0x06)
++#define TLV320AIC23_DIGITALFMT (0x07)
++#define TLV320AIC23_SAMPLERATE (0x08)
++#define TLV320AIC23_DIGITALACT (0x09)
++#define TLV320AIC23_RESET (0x0f)
++
++/* for the L/R line input */
++#define TLV320AIC23_INP_SIMULTUPD (1<<8)
++#define TLV320AIC23_INP_MUTE (1<<7)
++#define TLV320AIC23_INP_0dB (0x17)
++
++/* L/R Headphone volume */
++#define TLV320AIC23_HPVOL_SIMULTUPD (1<<8)
++#define TLV320AIC23_HPVOL_ZEROCROSS (1<<7)
++#define TLV320AIC23_HPVOL_0dB (0x79)
++
++/* Analog path control */
++#define TLV320AIC23_APATH_SIDETONE6dB (0<<6)
++#define TLV320AIC23_APATH_SIDETONE9dB (1<<6)
++#define TLV320AIC23_APATH_SIDETONE12dB (2<<6)
++#define TLV320AIC23_APATH_SIDETONE15dB (3<<6)
++#define TLV320AIC23_APATH_SIDETONE_EN (1<<5)
++#define TLV320AIC23_APATH_DACEN (1<<4)
++#define TLV320AIC23_APATH_BYPASSEN (1<<3)
++#define TLV320AIC23_APATH_MICADC (1<<2)
++#define TLV320AIC23_APATH_MICMUTE (1<<1)
++#define TLV320AIC23_APATH_MICBOOST (1<<0)
++
++#define TLV320AIC23_DPATH_SOFTMUTE (1<<3)
++#define TLV320AIC23_DPATH_DEEMP_OFF (0<<1)
++#define TLV320AIC23_DPATH_DEEMP_32K (1<<1)
++#define TLV320AIC23_DPATH_DEEMP_44K1 (2<<1)
++#define TLV320AIC23_DPATH_DEEMP_48K (3<<1)
++#define TLV320AIC23_DPATH_ADC_HIGHPASS (1<<1)
++
++/* power down control (set bit to power down) */
++#define TLV320AIC23_PDC_DEVICE (1<<7)
++#define TLV320AIC23_PDC_CLOCK (1<<6)
++#define TLV320AIC23_PDC_OSC (1<<5)
++#define TLV320AIC23_PDC_OUTPUT (1<<4)
++#define TLV320AIC23_PDC_DAC (1<<3)
++#define TLV320AIC23_PDC_ADC (1<<2)
++#define TLV320AIC23_PDC_MIC (1<<1)
++#define TLV320AIC23_PDC_LINE (1<<0)
++
++/* digital data format control */
++#define TLV320AIC23_DIGITALFMT_MASTER (1<<6)
++#define TLV320AIC23_DIGITALFMT_LRSWAP (1<<5)
++#define TLV320AIC23_DIGITALFMT_LRPHASE (1<<4)
++#define TLV320AIC23_DIGITALFMT_16BIT (0<<2)
++#define TLV320AIC23_DIGITALFMT_20BIT (1<<2)
++#define TLV320AIC23_DIGITALFMT_24BIT (2<<2)
++#define TLV320AIC23_DIGITALFMT_32BIT (3<<2)
++#define TLV320AIC23_DIGITALFMT_DSP (3<<0)
++#define TLV320AIC23_DIGITALFMT_I2S (2<<0)
++#define TLV320AIC23_DIGITALFMT_LEFT (1<<0)
++#define TLV320AIC23_DIGITALFMT_RIGHT (0<<0)
++
++#define TLV320AIC23_SAMPLERATE_USB (1<<0)
++#define TLV320AIC23_SAMPLERATE_USB44K1 (8<<2)
++#define TLV320AIC23_SAMPLERATE_BOSR (1<<1)
+diff -urN kernel-source-2.4.27-8/drivers/sound/tlv320aic23.c kernel-source-2.4.27-8-arm-1/drivers/sound/tlv320aic23.c
+--- kernel-source-2.4.27-8/drivers/sound/tlv320aic23.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/tlv320aic23.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,180 @@
++/* linux/drivers/sound/tlv320aic23.c
++ *
++ * based on drivers/char/ds1307.c
++ *
++ * Copyright (c) 2003 Simtec Electronics, <linux at simtec.co.uk>
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * Driver (I2C mode) for TI TLV320AIC23 audio codec.
++ *
++ * 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:
++ * 12-Jul-2003 BJD Created file
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++#include <linux/poll.h>
++#include <linux/i2c.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/string.h>
++
++#include "tlv320aic23.h"
++
++#if 1
++#define dbg(x...)
++#else
++#define dbg(x...) printk(KERN_DEBUG x)
++#endif
++
++#if 0
++static unsigned short slave_address = TLV320AIC23_I2C_SLAVE_ADDR;
++#endif
++
++struct i2c_driver tlv320aic23_driver;
++
++struct i2c_client *tlv320aic23_i2c_client = 0;
++
++static unsigned short ignore[] = { I2C_CLIENT_END };
++static unsigned short normal_addr[] = { TLV320AIC23_I2C_SLAVE_ADDR, I2C_CLIENT_END };
++static unsigned short force[] = { ANY_I2C_BUS, TLV320AIC23_I2C_SLAVE_ADDR,
++ I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END };
++
++/* temporary i2c driver id */
++#define I2C_DRIVERID_TLV320AIC23 (0xfe00)
++
++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: force,
++};
++
++static int tlv320aic23_probe(struct i2c_adapter *adap);
++static int tlv320aic23_detach(struct i2c_client *client);
++
++int tlv320aic23_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++struct i2c_driver tlv320aic23_driver = {
++ name: "TLV320AIC23",
++ id: I2C_DRIVERID_TLV320AIC23,
++ flags: I2C_DF_NOTIFY,
++ attach_adapter: tlv320aic23_probe,
++ detach_client: tlv320aic23_detach,
++ command: tlv320aic23_command
++};
++
++static int
++tlv320aic23_attach(struct i2c_adapter *adap, int addr,
++ unsigned short flags, int kind)
++{
++ struct i2c_client *c;
++
++ dbg(__FUNCTION__ ": adapter=%p, addr=%d, flags=%04x, kind=%d\n",
++ adap, addr, flags, kind);
++
++ c = (struct i2c_client *)kmalloc(sizeof(*c), GFP_KERNEL);
++ if (c == NULL) {
++ printk(KERN_ERR "tlv320aic23: no memory for i2c_client\n");
++ return -ENOMEM;
++ }
++
++ strcpy(c->name, "TLV320AIC23");
++
++ c->id = tlv320aic23_driver.id;
++ c->flags = 0;
++ c->addr = addr;
++ c->adapter = adap;
++ c->driver = &tlv320aic23_driver;
++ c->data = NULL;
++
++ /* todo - sort out which bus this is on and notifying the proper
++ * driver of the fact we've gotten the i2c client */
++
++ if ((adap->id & I2C_HW_MASK) == I2C_HW_B_S3C2410) {
++ dbg("setting client to %p\n", c);
++ tlv320aic23_i2c_client = c;
++ }
++
++ return i2c_attach_client(c);
++}
++
++static int
++tlv320aic23_probe(struct i2c_adapter *adap)
++{
++ dbg("tlv320aic23_probe: adap=%p, id=%x, algo=%p (%x)\n",
++ adap, adap->id, adap->algo, adap->algo->id);
++
++ if ((adap->id & I2C_HW_MASK) != I2C_HW_B_S3C2410)
++ return 0; /* only for s3c2410 atm */
++
++ dbg(__FUNCTION__ ": calling probe...\n");
++ return i2c_probe(adap, &addr_data, tlv320aic23_attach);
++
++}
++
++static int
++tlv320aic23_detach(struct i2c_client *client)
++{
++ i2c_detach_client(client);
++ return 0;
++}
++
++static int
++tlv320aic23_reg_write(struct i2c_client *client, void *arg)
++{
++ struct i2c_msg msgs[1] = {
++ { client->addr, 0, 2, arg }
++ };
++
++ dbg(__FUNCTION__ ": client=%p, arg=%p (%04x)\n",
++ client, arg, *((unsigned short *)arg));
++
++ return i2c_transfer(client->adapter, msgs, 1);
++}
++
++int
++tlv320aic23_command(struct i2c_client *client, unsigned int cmd, void *arg)
++{
++ switch (cmd) {
++ case TLV320AIC23_REG_WRITE:
++ return tlv320aic23_reg_write(client, arg);
++ default:
++ return -EINVAL;
++ }
++}
++
++static int tlv320aic23_initialised = 0;
++
++int
++tlv320aic23_init(void)
++{
++ int retval;
++
++ if (tlv320aic23_initialised)
++ return 0;
++
++ printk("tlv320aic23: (c) 2003 Simtec Electronics\n");
++
++ retval = i2c_add_driver(&tlv320aic23_driver);
++
++ if (retval == 0) {
++ tlv320aic23_initialised = 1;
++ }
++
++ return retval;
++}
++
++MODULE_AUTHOR("Ben Dooks, <ben at simtec.co.uk>");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/sound/tlv320aic23.h kernel-source-2.4.27-8-arm-1/drivers/sound/tlv320aic23.h
+--- kernel-source-2.4.27-8/drivers/sound/tlv320aic23.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/tlv320aic23.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,23 @@
++/* linux/drivers/sound/tlv320aic23.h
++ *
++ * Copyright (c) 2003 Simtec Electronics, <linux at simtec.co.uk>
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * Driver (I2C) for TI TLV320AIC23 audio codec
++ *
++ * 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:
++ * 12-Jul-2003 BJD Created file
++ *
++*/
++
++
++#define TLV320AIC23_I2C_SLAVE_ADDR ((1<<1) | (3<<3))
++
++extern struct i2c_driver tlv320aic23_driver;
++extern struct i2c_client *tlv320aic23_i2c_client;
++
++#define TLV320AIC23_REG_WRITE (1) /* write register */
+diff -urN kernel-source-2.4.27-8/drivers/sound/uda1341.c kernel-source-2.4.27-8-arm-1/drivers/sound/uda1341.c
+--- kernel-source-2.4.27-8/drivers/sound/uda1341.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/uda1341.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,511 @@
++/*
++ * Philips UDA1341 mixer device driver
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico at cam.org>
++ *
++ * 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 <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/ioctl.h>
++#include <linux/soundcard.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/uaccess.h>
++
++#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 kernel-source-2.4.27-8/drivers/sound/vidc.c kernel-source-2.4.27-8-arm-1/drivers/sound/vidc.c
+--- kernel-source-2.4.27-8/drivers/sound/vidc.c 2001-10-11 17:43:30.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/vidc.c 2005-02-18 17:48:44.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;
+
++ /* 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));
+@@ -232,6 +248,7 @@
+ newsize, new2size);
+ new2size = 4096;
+ }
++ /*printk("VIDC: dma size %d\n", new2size);*/
+ dma_bufsize = new2size;
+ vidc_audio_rate = rate;
+ }
+diff -urN kernel-source-2.4.27-8/drivers/sound/waveartist.c kernel-source-2.4.27-8-arm-1/drivers/sound/waveartist.c
+--- kernel-source-2.4.27-8/drivers/sound/waveartist.c 2001-10-25 21:53:52.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/sound/waveartist.c 2005-02-18 17:48:44.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) {
++ 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");
+- } else
+- printk("waveartist_cmd: timed out\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 kernel-source-2.4.27-8/drivers/ssi/Config.in kernel-source-2.4.27-8-arm-1/drivers/ssi/Config.in
+--- kernel-source-2.4.27-8/drivers/ssi/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ssi/Config.in 2005-02-18 17:48:44.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 kernel-source-2.4.27-8/drivers/ssi/Makefile kernel-source-2.4.27-8-arm-1/drivers/ssi/Makefile
+--- kernel-source-2.4.27-8/drivers/ssi/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ssi/Makefile 2005-02-18 17:48:44.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 kernel-source-2.4.27-8/drivers/ssi/README kernel-source-2.4.27-8-arm-1/drivers/ssi/README
+--- kernel-source-2.4.27-8/drivers/ssi/README 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ssi/README 2005-02-18 17:48:44.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 kernel-source-2.4.27-8/drivers/ssi/clps711x_ssi1.c kernel-source-2.4.27-8-arm-1/drivers/ssi/clps711x_ssi1.c
+--- kernel-source-2.4.27-8/drivers/ssi/clps711x_ssi1.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ssi/clps711x_ssi1.c 2005-02-18 17:48:44.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 <linux/init.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++
++#include <asm/mach-types.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#include <asm/hardware/ep7212.h>
++
++#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:
++ * <control>
++ * 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> <map> <data>...
++ * 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 kernel-source-2.4.27-8/drivers/ssi/juno.c kernel-source-2.4.27-8-arm-1/drivers/ssi/juno.c
+--- kernel-source-2.4.27-8/drivers/ssi/juno.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ssi/juno.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,131 @@
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#include <asm/arch/syspld.h>
++
++#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 kernel-source-2.4.27-8/drivers/ssi/ssi_bus.h kernel-source-2.4.27-8-arm-1/drivers/ssi/ssi_bus.h
+--- kernel-source-2.4.27-8/drivers/ssi/ssi_bus.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ssi/ssi_bus.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,21 @@
++#include <linux/circ_buf.h>
++
++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 kernel-source-2.4.27-8/drivers/ssi/ssi_core.c kernel-source-2.4.27-8-arm-1/drivers/ssi/ssi_core.c
+--- kernel-source-2.4.27-8/drivers/ssi/ssi_core.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ssi/ssi_core.c 2005-02-18 17:48:44.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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/malloc.h>
++#include <linux/init.h>
++
++#include <asm/errno.h>
++
++#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 : "<none>", 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 kernel-source-2.4.27-8/drivers/ssi/ssi_dev.h kernel-source-2.4.27-8-arm-1/drivers/ssi/ssi_dev.h
+--- kernel-source-2.4.27-8/drivers/ssi/ssi_dev.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/ssi/ssi_dev.h 2005-02-18 17:48:44.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 kernel-source-2.4.27-8/drivers/usb/Config.in kernel-source-2.4.27-8-arm-1/drivers/usb/Config.in
+--- kernel-source-2.4.27-8/drivers/usb/Config.in 2005-01-19 09:57:40.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/Config.in 2005-02-18 17:48:44.000000000 +0000
+@@ -4,7 +4,15 @@
+ 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_CPU_S3C2410X" = "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 kernel-source-2.4.27-8/drivers/usb/Makefile kernel-source-2.4.27-8-arm-1/drivers/usb/Makefile
+--- kernel-source-2.4.27-8/drivers/usb/Makefile 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/Makefile 2005-02-18 17:48:44.000000000 +0000
+@@ -74,6 +74,18 @@
+
+ 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_SA1111),y)
++ obj-y += host/usb-ohci.o host/usb-ohci-sa1111.o
++endif
++subdir-$(CONFIG_USB_OHCI_S3C2410)+= host
++ifeq ($(CONFIG_USB_OHCI_S3C2410),y)
++ obj-y += host/usb-ohci.o host/usb-ohci-s3c2410.o
++endif
++subdir-$(CONFIG_USB_OHCI_AT91) += host
++ifeq ($(CONFIG_USB_OHCI_AT91),y)
+ obj-y += host/usb-ohci.o
+ endif
+
+@@ -85,8 +97,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 kernel-source-2.4.27-8/drivers/usb/hcd.c kernel-source-2.4.27-8-arm-1/drivers/usb/hcd.c
+--- kernel-source-2.4.27-8/drivers/usb/hcd.c 2004-04-14 14:05:32.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/hcd.c 2005-02-18 17:48:44.000000000 +0000
+@@ -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 kernel-source-2.4.27-8/drivers/usb/host/Config.in kernel-source-2.4.27-8-arm-1/drivers/usb/host/Config.in
+--- kernel-source-2.4.27-8/drivers/usb/host/Config.in 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/host/Config.in 2005-02-18 17:48:44.000000000 +0000
+@@ -12,8 +12,14 @@
+ 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
++dep_tristate ' S3C2410 OHCI-compatible host interface support' CONFIG_USB_OHCI_S3C2410 $CONFIG_CPU_S3C2410X
+ 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 kernel-source-2.4.27-8/drivers/usb/host/Makefile kernel-source-2.4.27-8-arm-1/drivers/usb/host/Makefile
+--- kernel-source-2.4.27-8/drivers/usb/host/Makefile 2003-11-28 18:26:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/host/Makefile 2005-02-18 17:48:44.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 kernel-source-2.4.27-8/drivers/usb/host/usb-ohci-pci.c kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci-pci.c
+--- kernel-source-2.4.27-8/drivers/usb/host/usb-ohci-pci.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci-pci.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,436 @@
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h> /* for in_interrupt() */
++#undef DEBUG
++#include <linux/usb.h>
++
++#include "usb-ohci.h"
++
++#ifdef CONFIG_PMAC_PBOOK
++#include <asm/machdep.h>
++#include <asm/pmac_feature.h>
++#include <asm/pci-bridge.h>
++#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 kernel-source-2.4.27-8/drivers/usb/host/usb-ohci-s3c2410.c kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci-s3c2410.c
+--- kernel-source-2.4.27-8/drivers/usb/host/usb-ohci-s3c2410.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci-s3c2410.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,107 @@
++/*
++ * linux/drivers/usb/usb-ohci-s3c2410.c
++ * SW.LEE <hitchcar at sec.samsung.com>
++ * Modified for S3C2410
++ *
++ * BAST modifications by Ben Dooks, (c) 2003 Simtec Electronics
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++#include <asm/mach-types.h>
++
++#include <asm/arch/s3c2410.h>
++
++#include "usb-ohci.h"
++
++#if defined(CONFIG_ARCH_BAST) || defined(CONFIG_MACH_VR1000)
++#include <asm/arch-s3c2410/S3C2410-gpio.h>
++#endif
++
++int __devinit
++hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags,
++ ohci_t **ohci, const char *name, const char *slot_name);
++extern void hc_remove_ohci(ohci_t *ohci);
++
++static ohci_t *s3c2410_ohci;
++
++static void __init s3c2410_ohci_configure(void)
++{
++ unsigned long temp;
++
++ /*
++ * Configure the power sense and control lines. Place the USB
++ * host controller in reset.
++ * Now, carefully enable the USB clock, and take
++ * the USB host controller out of reset.
++ */
++
++ /* look for arch/arm/boot/compress/head-s3c2410.S
++ rUPLLCON = head-s3c2410.S
++ rCLKCON = defalt value
++ */
++
++ if (machine_is_bast() || machine_is_vr1000()) {
++ /* we use gpb4 for control of the usb power switch */
++
++ temp = __raw_readl(S3C2410_GPBCON);
++ temp &= ~S3C2410_GPB4_MASK;
++ temp |= S3C2410_GPB4_OUTP;
++ printk("usb: setting gbpcon=%08x\n", temp);
++ __raw_writel(temp, S3C2410_GPBCON);
++
++ temp = __raw_readl(S3C2410_GPBDAT);
++ temp &= ~(1<<4); /* needs to go low to enable usb power */
++ printk("usb: setting gbpdat=%08x\n", temp);
++ __raw_writel(temp, S3C2410_GPBDAT);
++ }
++}
++
++
++static int __init s3c2410_ohci_init(void)
++{
++ int ret;
++
++
++ s3c2410_ohci_configure();
++
++ /*
++ * Initialise the generic OHCI driver.
++ */
++ ret = hc_add_ohci((struct pci_dev *)1, IRQ_USBH,
++ (void *)VA_USB_BASE, 0, &s3c2410_ohci,
++ "usb-ohci", "s3c2410");
++ return ret;
++}
++
++static void __exit s3c2410_ohci_exit(void)
++{
++ hc_remove_ohci(s3c2410_ohci);
++
++ /*
++ * Put the USB host controller into reset.
++ */
++
++ /*
++ * Stop the USB clock. (on the bast, bad idea, other board timings
++ * are generated from the usb pll to avoid problems with having to
++ * move the mpll)
++ */
++
++ /*
++ * Release memory resources.
++ */
++
++}
++
++module_init(s3c2410_ohci_init);
++module_exit(s3c2410_ohci_exit);
+diff -urN kernel-source-2.4.27-8/drivers/usb/host/usb-ohci-sa1111.c kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci-sa1111.c
+--- kernel-source-2.4.27-8/drivers/usb/host/usb-ohci-sa1111.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci-sa1111.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,118 @@
++/*
++ * linux/drivers/usb/usb-ohci-sa1111.c
++ *
++ * The outline of this code was taken from Brad Parkers <brad at heeltoe.com>
++ * original OHCI driver modifications, and reworked into a cleaner form
++ * by Russell King <rmk at arm.linux.org.uk>.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++#include <asm/pci.h>
++#include <asm/arch/assabet.h>
++#include <asm/arch/badge4.h>
++#include <asm/hardware/sa1111.h>
++
++#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 kernel-source-2.4.27-8/drivers/usb/host/usb-ohci.c kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci.c
+--- kernel-source-2.4.27-8/drivers/usb/host/usb-ohci.c 2005-01-19 09:57:43.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci.c 2005-02-18 17:48:44.000000000 +0000
+@@ -12,7 +12,6 @@
+ *
+ * History:
+ *
+- * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX <thchou at ali>)
+ * 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 <asm/machdep.h>
+-#include <asm/pmac_feature.h>
+-#include <asm/pci-bridge.h>
+-#ifndef CONFIG_PM
+-#define CONFIG_PM
+-#endif
+-#endif
+-
+-
+ /*
+ * Version Information
+ */
+@@ -98,12 +87,12 @@
+ #define DRIVER_AUTHOR "Roman Weissgaerber <weissg at vienna.at>, 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.
+@@ -131,57 +120,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 */
+
+ static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv)
+@@ -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;
+ }
+
+@@ -539,8 +483,8 @@
+ 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;
+
+@@ -554,6 +498,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 +509,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 +540,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;
+@@ -616,19 +562,15 @@
+ 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 +592,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 +611,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 +623,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 +637,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 +659,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 +685,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 +701,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 +735,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 +744,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 +752,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 +771,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;
+ } else
+@@ -866,8 +798,6 @@
+ if (urb->complete)
+ urb->complete (urb);
+ }
+- } else {
+- spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+ }
+ return 0;
+ }
+@@ -910,14 +840,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 +855,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 +892,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 +1189,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 +1212,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);
+@@ -1300,6 +1236,7 @@
+ ed->int_load = load;
+ }
+
++ spin_unlock_irqrestore (&usb_ed_lock, flags);
+ return ed_ret;
+ }
+
+@@ -1340,7 +1277,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,6 +1298,10 @@
+ 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];
+@@ -1461,7 +1401,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 +1429,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 +1496,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 +1504,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,6 +1528,9 @@
+ 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 +1557,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 +1569,7 @@
+
+ static void dl_del_list (ohci_t * ohci, unsigned int frame)
+ {
++ unsigned long flags;
+ ed_t * ed;
+ __u32 edINFO;
+ __u32 tdINFO;
+@@ -1631,6 +1577,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 +1599,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 +1656,7 @@
+ }
+
+ ohci->ed_rm_list[frame] = NULL;
++ spin_unlock_irqrestore (&usb_ed_lock, flags);
+ }
+
+
+@@ -1725,6 +1674,8 @@
+ 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 +1704,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 +1719,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 +1810,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,7 +1910,6 @@
+ 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,8 +1919,6 @@
+ __u16 wIndex;
+ __u16 wLength;
+
+- spin_lock_irqsave(&ohci->ohci_lock, flags);
+-
+ if (usb_pipeint(pipe)) {
+ ohci->rh.urb = urb;
+ ohci->rh.send = 1;
+@@ -1974,7 +1926,6 @@
+ rh_init_int_timer(urb);
+ urb->status = cc_to_error [TD_CC_NOERROR];
+
+- spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+ return 0;
+ }
+
+@@ -2146,7 +2097,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 +2109,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 +2124,6 @@
+ urb->status = -ENOENT;
+ if (urb->complete)
+ urb->complete (urb);
+- } else {
+- spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+ }
+ return 0;
+ }
+@@ -2213,16 +2158,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 +2232,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 +2261,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 +2274,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 +2296,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,13 +2304,11 @@
+ /* 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;
+ }
+
+@@ -2382,7 +2317,7 @@
+ 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 +2333,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 +2364,6 @@
+
+ writel (ints, ®s->intrstatus);
+ writel (OHCI_INTR_MIE, ®s->intrenable);
+- (void)readl (®s->intrdisable); /* PCI posting flush */
+-
+- ohci_complete(ohci);
+ }
+
+ /*-------------------------------------------------------------------------*/
+@@ -2475,10 +2394,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 +2424,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 +2437,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 +2447,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 +2462,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 +2480,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 +2499,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 +2511,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 +2522,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 +2543,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 kernel-source-2.4.27-8/drivers/usb/host/usb-ohci.h kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci.h
+--- kernel-source-2.4.27-8/drivers/usb/host/usb-ohci.h 2004-04-14 14:05:33.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/usb/host/usb-ohci.h 2005-02-18 17:48:44.000000000 +0000
+@@ -384,11 +384,12 @@
+ #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 */
+@@ -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 kernel-source-2.4.27-8/drivers/video/Config.in kernel-source-2.4.27-8-arm-1/drivers/video/Config.in
+--- kernel-source-2.4.27-8/drivers/video/Config.in 2005-01-19 09:57:40.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/video/Config.in 2005-02-18 17:48:44.000000000 +0000
+@@ -30,13 +30,29 @@
+ tristate ' Permedia3 support (EXPERIMENTAL)' CONFIG_FB_PM3
+ fi
+ fi
+- if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
++ 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 ' S3C2410 LCD support' CONFIG_FB_S3C2410 $CONFIG_ARCH_BAST
++ 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
+- dep_tristate ' Cyber2000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI
+- if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+- bool ' SA-1100 LCD support' CONFIG_FB_SA1100
+ 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 +281,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 +289,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 +329,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 +372,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 +529,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 kernel-source-2.4.27-8/drivers/video/Makefile kernel-source-2.4.27-8-arm-1/drivers/video/Makefile
+--- kernel-source-2.4.27-8/drivers/video/Makefile 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/video/Makefile 2005-02-18 17:48:44.000000000 +0000
+@@ -16,6 +16,7 @@
+ fbcon-cfb2.o fbcon-cfb24.o fbcon-cfb32.o fbcon-cfb4.o \
+ fbcon-cfb8.o fbcon-mac.o fbcon-mfb.o \
+ cyber2000fb.o sa1100fb.o fbcon-hga.o fbgen.o
++#swapbits.o
+
+ # Each configuration option enables a list of files.
+
+@@ -55,6 +56,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
+@@ -128,12 +130,15 @@
+ obj-$(CONFIG_FB_BWTWO) += bwtwofb.o
+ obj-$(CONFIG_FB_HGA) += hgafb.o
+ obj-$(CONFIG_FB_SA1100) += sa1100fb.o
++obj-$(CONFIG_FB_S3C2410) += s3c2410fb.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
+
+@@ -150,6 +155,7 @@
+ obj-$(CONFIG_FBCON_IPLAN2P8) += fbcon-iplan2p8.o
+ obj-$(CONFIG_FBCON_IPLAN2P16) += fbcon-iplan2p16.o
+ obj-$(CONFIG_FBCON_MAC) += fbcon-mac.o
++#obj-$(CONFIG_FBCON_MFB) += fbcon-mfb.o swapbits.o
+ obj-$(CONFIG_FBCON_MFB) += fbcon-mfb.o
+ obj-$(CONFIG_FBCON_VGA) += fbcon-vga.o
+ obj-$(CONFIG_FBCON_HGA) += fbcon-hga.o
+diff -urN kernel-source-2.4.27-8/drivers/video/acornfb.c kernel-source-2.4.27-8-arm-1/drivers/video/acornfb.c
+--- kernel-source-2.4.27-8/drivers/video/acornfb.c 2002-08-03 01:39:45.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/acornfb.c 2005-02-18 17:48:44.000000000 +0000
+@@ -752,9 +752,10 @@
+ }
+ #endif
+ #ifdef FBCON_HAS_CFB16
+- if (bpp == 16 && regno < 16) {
++ if (bpp == 16) {
+ int i;
+
++ if (regno < 16)
+ current_par.cmap.cfb16[regno] =
+ regno | regno << 5 | regno << 10;
+
+@@ -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 kernel-source-2.4.27-8/drivers/video/anakinfb.c kernel-source-2.4.27-8-arm-1/drivers/video/anakinfb.c
+--- kernel-source-2.4.27-8/drivers/video/anakinfb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/anakinfb.c 2005-02-18 17:48:44.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 <linux/types.h>
++#include <linux/fb.h>
++#include <linux/string.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/module.h>
++
++#include <asm/io.h>
++
++#include <video/fbcon.h>
++#include <video/fbcon-cfb16.h>
++
++static u16 colreg[16];
++static int currcon = 0;
++static struct fb_info fb_info;
++static struct display display;
++
++static int
++anakinfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
++ u_int *transp, struct fb_info *info)
++{
++ if (regno > 15)
++ return 1;
++
++ *red = colreg[regno] & 0xf800;
++ *green = colreg[regno] & 0x7e0 << 5;
++ *blue = colreg[regno] & 0x1f << 11;
++ *transp = 0;
++ return 0;
++}
++
++static int
++anakinfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++ u_int transp, struct fb_info *info)
++{
++ if (regno > 15)
++ return 1;
++
++ colreg[regno] = (red & 0xf800) | (green & 0xfc00 >> 5) |
++ (blue & 0xf800 >> 11);
++ return 0;
++}
++
++static int
++anakinfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++{
++ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
++ strcpy(fix->id, "AnakinFB");
++ fix->smem_start = VGA_START;
++ fix->smem_len = VGA_SIZE;
++ fix->type = FB_TYPE_PACKED_PIXELS;
++ fix->type_aux = 0;
++ fix->visual = FB_VISUAL_TRUECOLOR;
++ fix->xpanstep = 0;
++ fix->ypanstep = 0;
++ fix->ywrapstep = 0;
++ fix->line_length = 400 * 2;
++ fix->accel = FB_ACCEL_NONE;
++ return 0;
++}
++
++static int
++anakinfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++ memset(var, 0, sizeof(struct fb_var_screeninfo));
++ var->xres = 400;
++ var->yres = 234;
++ var->xres_virtual = 400;
++ var->yres_virtual = 234;
++ var->xoffset = 0;
++ var->yoffset = 0;
++ var->bits_per_pixel = 16;
++ var->grayscale = 0;
++ var->red.offset = 11;
++ var->red.length = 5;
++ var->green.offset = 5;
++ var->green.length = 6;
++ var->blue.offset = 0;
++ var->blue.length = 5;
++ var->transp.offset = 0;
++ var->transp.length = 0;
++ var->nonstd = 0;
++ var->activate = FB_ACTIVATE_NOW;
++ var->height = -1;
++ var->width = -1;
++ var->pixclock = 0;
++ var->left_margin = 0;
++ var->right_margin = 0;
++ var->upper_margin = 0;
++ var->lower_margin = 0;
++ var->hsync_len = 0;
++ var->vsync_len = 0;
++ var->sync = 0;
++ var->vmode = FB_VMODE_NONINTERLACED;
++ return 0;
++}
++
++static int
++anakinfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++ return -EINVAL;
++}
++
++static int
++anakinfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
++ struct fb_info *info)
++{
++ if (con == currcon)
++ return fb_get_cmap(cmap, kspc, anakinfb_getcolreg, info);
++ else if (fb_display[con].cmap.len)
++ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
++ else
++ fb_copy_cmap(fb_default_cmap(16), cmap, kspc ? 0 : 2);
++ return 0;
++}
++
++static int
++anakinfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++ struct fb_info *info)
++{
++ int err;
++
++ if (!fb_display[con].cmap.len) {
++ if ((err = fb_alloc_cmap(&fb_display[con].cmap, 16, 0)))
++ return err;
++ }
++ if (con == currcon)
++ return fb_set_cmap(cmap, kspc, anakinfb_setcolreg, info);
++ else
++ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
++ return 0;
++}
++
++static int
++anakinfb_switch_con(int con, struct fb_info *info)
++{
++ currcon = con;
++ return 0;
++
++}
++
++static int
++anakinfb_updatevar(int con, struct fb_info *info)
++{
++ return 0;
++}
++
++static void
++anakinfb_blank(int blank, struct fb_info *info)
++{
++ /*
++ * TODO: use I2C to blank/unblank the screen
++ */
++}
++
++static struct fb_ops anakinfb_ops = {
++ owner: THIS_MODULE,
++ fb_get_fix: anakinfb_get_fix,
++ fb_get_var: anakinfb_get_var,
++ fb_set_var: anakinfb_set_var,
++ fb_get_cmap: anakinfb_get_cmap,
++ fb_set_cmap: anakinfb_set_cmap,
++};
++
++int __init
++anakinfb_init(void)
++{
++ memset(&fb_info, 0, sizeof(struct fb_info));
++ strcpy(fb_info.modename, "AnakinFB");
++ fb_info.node = -1;
++ fb_info.flags = FBINFO_FLAG_DEFAULT;
++ fb_info.fbops = &anakinfb_ops;
++ fb_info.disp = &display;
++ strcpy(fb_info.fontname, "VGA8x16");
++ fb_info.changevar = NULL;
++ fb_info.switch_con = &anakinfb_switch_con;
++ fb_info.updatevar = &anakinfb_updatevar;
++ fb_info.blank = &anakinfb_blank;
++
++ memset(&display, 0, sizeof(struct display));
++ anakinfb_get_var(&display.var, 0, &fb_info);
++ display.screen_base = ioremap(VGA_START, VGA_SIZE);
++ display.visual = FB_VISUAL_TRUECOLOR;
++ display.type = FB_TYPE_PACKED_PIXELS;
++ display.type_aux = 0;
++ display.ypanstep = 0;
++ display.ywrapstep = 0;
++ display.line_length = 400 * 2;
++ display.can_soft_blank = 1;
++ display.inverse = 0;
++
++#ifdef FBCON_HAS_CFB16
++ display.dispsw = &fbcon_cfb16;
++ display.dispsw_data = colreg;
++#else
++ display.dispsw = &fbcon_dummy;
++#endif
++
++ if (register_framebuffer(&fb_info) < 0)
++ return -EINVAL;
++
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++MODULE_AUTHOR("Tak-Shing Chan <chan at aleph1.co.uk>");
++MODULE_DESCRIPTION("Anakin framebuffer driver");
++MODULE_SUPPORTED_DEVICE("fb");
+diff -urN kernel-source-2.4.27-8/drivers/video/clps711xfb.c kernel-source-2.4.27-8-arm-1/drivers/video/clps711xfb.c
+--- kernel-source-2.4.27-8/drivers/video/clps711xfb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/clps711xfb.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,677 @@
++/*
++ * linux/drivers/video/clps711xfb.c
++ *
++ * 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
++ *
++ * Framebuffer driver for the CLPS7111 and EP7212 processors.
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/proc_fs.h>
++#include <linux/delay.h>
++
++#include <video/fbcon.h>
++#include <video/fbcon-cfb4.h>
++#include <video/fbcon-cfb2.h>
++#include <video/fbcon-mfb.h>
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++
++#include <asm/hardware/clps7111.h>
++#include <asm/arch/syspld.h>
++
++static struct clps7111fb_info {
++ struct fb_info fb;
++ int currcon;
++} *cfb;
++
++#define CMAP_MAX_SIZE 16
++
++/* The /proc entry for the backlight. */
++static struct proc_dir_entry *clps7111fb_backlight_proc_entry = NULL;
++
++static int clps7111fb_proc_backlight_read(char *page, char **start, off_t off,
++ int count, int *eof, void *data);
++static int clps7111fb_proc_backlight_write(struct file *file,
++ const char *buffer, unsigned long count, void *data);
++
++/*
++ * LCD AC Prescale. This comes from the LCD panel manufacturers specifications.
++ * This determines how many clocks + 1 of CL1 before the M signal toggles.
++ * The number of lines on the display must not be divisible by this number.
++ */
++static unsigned int lcd_ac_prescale = 13;
++
++/*
++ * Set a single color register. Return != 0 for invalid regno.
++ */
++static int
++clps7111fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++ u_int transp, struct fb_info *info)
++{
++ unsigned int level, mask, shift, pal;
++
++ if (regno >= (1 << info->var.bits_per_pixel))
++ return 1;
++
++ /* gray = 0.30*R + 0.58*G + 0.11*B */
++ level = (red * 77 + green * 151 + blue * 28) >> 20;
++
++ /*
++ * On an LCD, a high value is dark, while a low value is light.
++ * So we invert the level.
++ *
++ * This isn't true on all machines, so we only do it on EDB7211.
++ * --rmk
++ */
++ if (machine_is_edb7211() || machine_is_guidea07()) {
++ level = 15 - level;
++ }
++
++ shift = 4 * (regno & 7);
++ level <<= shift;
++ mask = 15 << shift;
++ level &= mask;
++
++ regno = regno < 8 ? PALLSW : PALMSW;
++
++ pal = clps_readl(regno);
++ pal = (pal & ~mask) | level;
++ clps_writel(pal, regno);
++
++ return 0;
++}
++
++/*
++ * Set the colormap
++ */
++static int
++clps7111fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++ struct fb_info *info)
++{
++ struct clps7111fb_info *cfb = (struct clps7111fb_info *)info;
++ struct fb_cmap *dcmap = &fb_display[con].cmap;
++ int err = 0;
++
++ /* no colormap allocated? */
++ if (!dcmap->len)
++ err = fb_alloc_cmap(dcmap, CMAP_MAX_SIZE, 0);
++
++ if (!err && con == cfb->currcon) {
++ err = fb_set_cmap(cmap, kspc, clps7111fb_setcolreg, &cfb->fb);
++ dcmap = &cfb->fb.cmap;
++ }
++
++ if (!err)
++ fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
++
++ return err;
++}
++
++/*
++ * Set the User Defined Part of the Display
++ */
++static int
++clps7111fb_set_var(struct fb_var_screeninfo *var, int con,
++ struct fb_info *info)
++{
++ struct display *display;
++ unsigned int lcdcon, syscon, pixclock;
++ int chgvar = 0;
++
++ if (var->activate & FB_ACTIVATE_TEST)
++ return 0;
++
++ if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
++ return -EINVAL;
++
++ if (cfb->fb.var.xres != var->xres)
++ chgvar = 1;
++ if (cfb->fb.var.yres != var->yres)
++ chgvar = 1;
++ if (cfb->fb.var.xres_virtual != var->xres_virtual)
++ chgvar = 1;
++ if (cfb->fb.var.yres_virtual != var->yres_virtual)
++ chgvar = 1;
++ if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
++ chgvar = 1;
++
++ if (con < 0) {
++ display = cfb->fb.disp;
++ chgvar = 0;
++ } else {
++ display = fb_display + con;
++ }
++
++ var->transp.msb_right = 0;
++ var->transp.offset = 0;
++ var->transp.length = 0;
++ var->red.msb_right = 0;
++ var->red.offset = 0;
++ var->red.length = var->bits_per_pixel;
++ var->green = var->red;
++ var->blue = var->red;
++
++ switch (var->bits_per_pixel) {
++#ifdef FBCON_HAS_MFB
++ case 1:
++ cfb->fb.fix.visual = FB_VISUAL_MONO01;
++ display->dispsw = &fbcon_mfb;
++ display->dispsw_data = NULL;
++ break;
++#endif
++#ifdef FBCON_HAS_CFB2
++ case 2:
++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ display->dispsw = &fbcon_cfb2;
++ display->dispsw_data = NULL;
++ break;
++#endif
++#ifdef FBCON_HAS_CFB4
++ case 4:
++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ display->dispsw = &fbcon_cfb4;
++ display->dispsw_data = NULL;
++ break;
++#endif
++ default:
++ return -EINVAL;
++ }
++
++ display->next_line = var->xres_virtual * var->bits_per_pixel / 8;
++
++ cfb->fb.fix.line_length = display->next_line;
++
++ display->screen_base = cfb->fb.screen_base;
++ display->line_length = cfb->fb.fix.line_length;
++ display->visual = cfb->fb.fix.visual;
++ display->type = cfb->fb.fix.type;
++ display->type_aux = cfb->fb.fix.type_aux;
++ display->ypanstep = cfb->fb.fix.ypanstep;
++ display->ywrapstep = cfb->fb.fix.ywrapstep;
++ display->can_soft_blank = 1;
++ display->inverse = 0;
++
++ cfb->fb.var = *var;
++ cfb->fb.var.activate &= ~FB_ACTIVATE_ALL;
++
++ /*
++ * Update the old var. The fbcon drivers still use this.
++ * Once they are using cfb->fb.var, this can be dropped.
++ * --rmk
++ */
++ display->var = cfb->fb.var;
++
++ /*
++ * If we are setting all the virtual consoles, also set the
++ * defaults used to create new consoles.
++ */
++ if (var->activate & FB_ACTIVATE_ALL)
++ cfb->fb.disp->var = cfb->fb.var;
++
++ if (chgvar && info && cfb->fb.changevar)
++ cfb->fb.changevar(con);
++
++ lcdcon = (var->xres_virtual * var->yres_virtual * var->bits_per_pixel) / 128 - 1;
++ lcdcon |= ((var->xres_virtual / 16) - 1) << 13;
++ lcdcon |= lcd_ac_prescale << 25;
++
++ /*
++ * Calculate pixel prescale value from the pixclock. This is:
++ * 36.864MHz / pixclock_mhz - 1.
++ * However, pixclock is in picoseconds, so this ends up being:
++ * 36864000 * pixclock_ps / 10^12 - 1
++ * and this will overflow the 32-bit math. We perform this as
++ * (9 * 4096000 == 36864000):
++ * pixclock_ps * 9 * (4096000 / 10^12) - 1
++ */
++ pixclock = 9 * info->var.pixclock / 244140 - 1;
++ lcdcon |= pixclock << 19;
++
++ if (info->var.bits_per_pixel == 4)
++ lcdcon |= LCDCON_GSMD;
++ if (info->var.bits_per_pixel >= 2)
++ lcdcon |= LCDCON_GSEN;
++
++ /*
++ * LCDCON must only be changed while the LCD is disabled
++ */
++ syscon = clps_readl(SYSCON1);
++ clps_writel(syscon & ~SYSCON1_LCDEN, SYSCON1);
++ clps_writel(lcdcon, LCDCON);
++ clps_writel(syscon | SYSCON1_LCDEN, SYSCON1);
++
++ fb_set_cmap(&cfb->fb.cmap, 1, clps7111fb_setcolreg, &cfb->fb);
++
++ return 0;
++}
++
++/*
++ * Get the currently displayed virtual consoles colormap.
++ */
++static int
++gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
++{
++ fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
++ return 0;
++}
++
++/*
++ * Get the currently displayed virtual consoles fixed part of the display.
++ */
++static int
++gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++{
++ *fix = info->fix;
++ return 0;
++}
++
++/*
++ * Get the current user defined part of the display.
++ */
++static int
++gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++ *var = info->var;
++ return 0;
++}
++
++static struct fb_ops clps7111fb_ops = {
++ owner: THIS_MODULE,
++ fb_set_var: clps7111fb_set_var,
++ fb_set_cmap: clps7111fb_set_cmap,
++ fb_get_fix: gen_get_fix,
++ fb_get_var: gen_get_var,
++ fb_get_cmap: gen_get_cmap,
++};
++
++static int clps7111fb_switch(int con, struct fb_info *info)
++{
++ struct clps7111fb_info *cfb = (struct clps7111fb_info *)info;
++ struct display *disp;
++ struct fb_cmap *cmap;
++
++ if (cfb->currcon >= 0) {
++ disp = fb_display + cfb->currcon;
++
++ /*
++ * Save the old colormap and video mode.
++ */
++ disp->var = cfb->fb.var;
++ if (disp->cmap.len)
++ fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0);
++ }
++
++ cfb->currcon = con;
++ disp = fb_display + con;
++
++ /*
++ * Install the new colormap and change the video mode. By default,
++ * fbcon sets all the colormaps and video modes to the default
++ * values at bootup.
++ */
++ if (disp->cmap.len)
++ cmap = &disp->cmap;
++ else
++ cmap = fb_default_cmap(CMAP_MAX_SIZE);
++
++ fb_copy_cmap(cmap, &cfb->fb.cmap, 0);
++
++ cfb->fb.var = disp->var;
++ cfb->fb.var.activate = FB_ACTIVATE_NOW;
++
++ clps7111fb_set_var(&cfb->fb.var, con, &cfb->fb);
++
++ return 0;
++}
++
++static int clps7111fb_updatevar(int con, struct fb_info *info)
++{
++ return -EINVAL;
++}
++
++static void clps7111fb_blank(int blank, struct fb_info *info)
++{
++ if (blank) {
++ if (machine_is_edb7211()) {
++ /* Turn off the LCD backlight. */
++ clps_writeb(clps_readb(PDDR) & ~EDB_PD3_LCDBL, PDDR);
++
++ /* Power off the LCD DC-DC converter. */
++ clps_writeb(clps_readb(PDDR) & ~EDB_PD1_LCD_DC_DC_EN, PDDR);
++
++ /* Delay for a little while (half a second). */
++ udelay(100);
++
++ /* Power off the LCD panel. */
++ clps_writeb(clps_readb(PDDR) & ~EDB_PD2_LCDEN, PDDR);
++
++ /* Power off the LCD controller. */
++ clps_writel(clps_readl(SYSCON1) & ~SYSCON1_LCDEN,
++ SYSCON1);
++ }
++ } else {
++ if (machine_is_edb7211()) {
++ /* Power up the LCD controller. */
++ clps_writel(clps_readl(SYSCON1) | SYSCON1_LCDEN,
++ SYSCON1);
++
++ /* Power up the LCD panel. */
++ clps_writeb(clps_readb(PDDR) | EDB_PD2_LCDEN, PDDR);
++
++ /* Delay for a little while. */
++ udelay(100);
++
++ /* Power up the LCD DC-DC converter. */
++ clps_writeb(clps_readb(PDDR) | EDB_PD1_LCD_DC_DC_EN,
++ PDDR);
++
++ /* Turn on the LCD backlight. */
++ clps_writeb(clps_readb(PDDR) | EDB_PD3_LCDBL, PDDR);
++ }
++ }
++}
++
++static int
++clps7111fb_proc_backlight_read(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ /* We need at least two characters, one for the digit, and one for
++ * the terminating NULL. */
++ if (count < 2)
++ return -EINVAL;
++
++ if (machine_is_edb7211()) {
++ return sprintf(page, "%d\n",
++ (clps_readb(PDDR) & EDB_PD3_LCDBL) ? 1 : 0);
++ }
++
++ return 0;
++}
++
++static int
++clps7111fb_proc_backlight_write(struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ unsigned char char_value;
++ int value;
++
++ if (count < 1) {
++ return -EINVAL;
++ }
++
++ if (copy_from_user(&char_value, buffer, 1))
++ return -EFAULT;
++
++ value = char_value - '0';
++
++ if (machine_is_edb7211()) {
++ unsigned char port_d;
++
++ port_d = clps_readb(PDDR);
++
++ if (value) {
++ port_d |= EDB_PD3_LCDBL;
++ } else {
++ port_d &= ~EDB_PD3_LCDBL;
++ }
++
++ clps_writeb(port_d, PDDR);
++ }
++
++ return count;
++}
++
++static void __init clps711x_guess_lcd_params(struct fb_info *info)
++{
++ unsigned int lcdcon, syscon, size;
++ unsigned long phys_base = PHYS_OFFSET;
++ void *virt_base = (void *)PAGE_OFFSET;
++
++ info->var.xres_virtual = 640;
++ info->var.yres_virtual = 240;
++ info->var.bits_per_pixel = 4;
++ info->var.activate = FB_ACTIVATE_NOW;
++ info->var.height = -1;
++ info->var.width = -1;
++ info->var.pixclock = 93006; /* 10.752MHz pixel clock */
++
++ /*
++ * If the LCD controller is already running, decode the values
++ * in LCDCON to xres/yres/bpp/pixclock/acprescale
++ */
++ syscon = clps_readl(SYSCON1);
++ if (syscon & SYSCON1_LCDEN) {
++ lcdcon = clps_readl(LCDCON);
++
++ /*
++ * Decode GSMD and GSEN bits to bits per pixel
++ * (in cs89712 docs, GSEN is GSMD1, GSMD is GSMD2)
++ */
++ switch (lcdcon & (LCDCON_GSMD | LCDCON_GSEN)) {
++ case LCDCON_GSMD | LCDCON_GSEN:
++ info->var.bits_per_pixel = 4;
++ break;
++
++ case LCDCON_GSEN:
++ info->var.bits_per_pixel = 2;
++ break;
++
++ default:
++ info->var.bits_per_pixel = 1;
++ break;
++ }
++
++ /*
++ * Decode xres/yres
++ */
++ info->var.xres_virtual = (((lcdcon >> 13) & 0x3f) + 1) * 16;
++ info->var.yres_virtual = (((lcdcon & 0x1fff) + 1) * 128) /
++ (info->var.xres_virtual *
++ info->var.bits_per_pixel);
++
++ /*
++ * Calculate pixclock
++ */
++ info->var.pixclock = (((lcdcon >> 19) & 0x3f) + 1) * 244140 / 9;
++
++ /*
++ * Grab AC prescale
++ */
++ lcd_ac_prescale = (lcdcon >> 25) & 0x1f;
++ }
++
++ info->var.xres = info->var.xres_virtual;
++ info->var.yres = info->var.yres_virtual;
++ info->var.grayscale = info->var.bits_per_pixel > 1;
++
++ size = info->var.xres * info->var.yres * info->var.bits_per_pixel / 8;
++
++ /*
++ * Might be worth checking to see if we can use the on-board
++ * RAM if size here...
++ * CLPS7110 - no on-board SRAM
++ * EP7212 - 38400 bytes
++ */
++ if (size <= 38400) {
++ printk(KERN_INFO "CLPS711xFB: could use on-board SRAM?\n");
++ }
++
++ if ((syscon & SYSCON1_LCDEN) == 0) {
++ /*
++ * The display isn't running. Ensure that
++ * the display memory is empty.
++ */
++ memset(virt_base, 0, size);
++ }
++
++ info->screen_base = virt_base;
++ info->fix.smem_start = phys_base;
++ info->fix.smem_len = PAGE_ALIGN(size);
++ info->fix.type = FB_TYPE_PACKED_PIXELS;
++}
++
++/* this function initializes the LCD registers as required by the Guide A07
++*/
++int __init clps711xfb_guidea07_init(void)
++{
++ unsigned long videomem, videosize;
++ unsigned int lcdcon, syscon;
++
++ //LCDCON must only be changed while the LCD is disabled
++ syscon = clps_readl(SYSCON1);
++ clps_writel(syscon & ~SYSCON1_LCDEN, SYSCON1);
++
++ printk(KERN_INFO "CLPS711xFB: setting up cs89712 SRAM as QVGA video framebuffer\n");
++ clps_writel(0x6, FBADDR); //internal SRAM location
++ videomem = (unsigned long)ioremap(SRAM_START, SRAM_SIZE);
++ if (!videomem) {
++ printk("ioremap failed!\n");
++ kfree(cfb);
++ return -EIO;
++ }
++
++ videosize = (320*240 * 4 / 8); //ie. 38400 or 0x9600
++ memset((void *)videomem, 0, videosize); //clear the vid memory
++
++ cfb->fb.screen_base = (void *)videomem;
++ cfb->fb.fix.smem_start = SRAM_START; //must be physical address
++ cfb->fb.fix.smem_len = videosize;
++
++ cfb->fb.var.grayscale = 1;
++ cfb->fb.var.bits_per_pixel = 4;
++ cfb->fb.var.xres_virtual = 320;
++ cfb->fb.var.activate = FB_ACTIVATE_NOW;
++
++ // change to 4bpp mode
++ lcdcon = clps_readl(LCDCON);
++ lcdcon |= LCDCON_GSMD | LCDCON_GSEN; //4bpp
++ //lcdcon |= 5 << 19; //pixel prescale value (pixclock)
++ //lcdcon |= 13 << 25; //AC prescale value
++ clps_writel(lcdcon, LCDCON);
++
++ //reenable LCD
++ clps_writel(syscon | SYSCON1_LCDEN, SYSCON1);
++
++ return 0;
++}
++
++int __init clps711xfb_init(void)
++{
++ int err = -ENOMEM;
++
++ cfb = kmalloc(sizeof(*cfb) + sizeof(struct display), GFP_KERNEL);
++ if (!cfb)
++ goto out;
++
++ memset(cfb, 0, sizeof(*cfb) + sizeof(struct display));
++ strcpy(cfb->fb.fix.id, "clps7111");
++
++ cfb->currcon = -1;
++ cfb->fb.screen_base = (void *)PAGE_OFFSET;
++ cfb->fb.fix.smem_start = PAGE_OFFSET;
++ cfb->fb.fix.smem_len = 0x14000;
++ cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
++
++ cfb->fb.fbops = &clps7111fb_ops;
++ cfb->fb.changevar = NULL;
++ cfb->fb.switch_con = clps7111fb_switch;
++ cfb->fb.updatevar = clps7111fb_updatevar;
++ cfb->fb.blank = clps7111fb_blank;
++ cfb->fb.flags = FBINFO_FLAG_DEFAULT;
++ cfb->fb.disp = (struct display *)(cfb + 1);
++
++ clps711x_guess_lcd_params(&cfb->fb);
++
++ if (machine_is_guidea07()) {
++ clps711xfb_guidea07_init();
++ }
++
++ if (machine_is_fortunet()) {
++ cfb->fb.fix.smem_len = 0x20000;
++ }
++
++ fb_alloc_cmap(&cfb->fb.cmap, CMAP_MAX_SIZE, 0);
++
++ /* Register the /proc entries. */
++ clps7111fb_backlight_proc_entry = create_proc_entry("backlight", 0444,
++ &proc_root);
++ if (clps7111fb_backlight_proc_entry == NULL) {
++ printk("Couldn't create the /proc entry for the backlight.\n");
++ return -EINVAL;
++ }
++
++ clps7111fb_backlight_proc_entry->read_proc =
++ &clps7111fb_proc_backlight_read;
++ clps7111fb_backlight_proc_entry->write_proc =
++ &clps7111fb_proc_backlight_write;
++
++ /*
++ * Power up the LCD
++ */
++ if (machine_is_p720t()) {
++ PLD_LCDEN = PLD_LCDEN_EN;
++ PLD_PWR |= (PLD_S4_ON|PLD_S3_ON|PLD_S2_ON|PLD_S1_ON);
++ }
++
++ if (machine_is_edb7211()) {
++ /* Power up the LCD panel. */
++ clps_writeb(clps_readb(PDDR) | EDB_PD2_LCDEN, PDDR);
++
++ /* Delay for a little while. */
++ udelay(100);
++
++ /* Power up the LCD DC-DC converter. */
++ clps_writeb(clps_readb(PDDR) | EDB_PD1_LCD_DC_DC_EN, PDDR);
++
++ /* Turn on the LCD backlight. */
++ clps_writeb(clps_readb(PDDR) | EDB_PD3_LCDBL, PDDR);
++ }
++
++ clps7111fb_set_var(&cfb->fb.var, -1, &cfb->fb);
++ err = register_framebuffer(&cfb->fb);
++
++out: return err;
++}
++
++static void __exit clps711xfb_exit(void)
++{
++ unregister_framebuffer(&cfb->fb);
++ kfree(cfb);
++
++ /*
++ * Power down the LCD
++ */
++ if (machine_is_p720t()) {
++ PLD_LCDEN = 0;
++ PLD_PWR &= ~(PLD_S4_ON|PLD_S3_ON|PLD_S2_ON|PLD_S1_ON);
++ }
++}
++
++#ifdef MODULE
++module_init(clps711xfb_init);
++#endif
++module_exit(clps711xfb_exit);
++
++MODULE_AUTHOR("Russell King <rmk at arm.linux.org.uk>");
++MODULE_DESCRIPTION("CLPS711x framebuffer driver");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/video/cyber2000fb.c kernel-source-2.4.27-8-arm-1/drivers/video/cyber2000fb.c
+--- kernel-source-2.4.27-8/drivers/video/cyber2000fb.c 2002-08-03 01:39:45.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/cyber2000fb.c 2005-02-18 17:48:44.000000000 +0000
+@@ -1,7 +1,7 @@
+ /*
+ * linux/drivers/video/cyber2000fb.c
+ *
+- * Copyright (C) 1998-2000 Russell King
++ * Copyright (C) 1998-2002 Russell King
+ *
+ * MIPS and 50xx clock support
+ * Copyright (C) 2001 Bradley D. LaRonde <brad at ltc.com>
+@@ -13,22 +13,28 @@
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+- * Intergraphics CyberPro 2000, 2010 and 5000 frame buffer device
++ * Integraphics CyberPro 2000, 2010 and 5000 frame buffer device
+ *
+ * Based on cyberfb.c.
+ *
+- * Note that we now use the new fbcon fix, var and cmap scheme. We do still
+- * have to check which console is the currently displayed one however, since
+- * especially for the colourmap stuff. Once fbcon has been fully migrated,
+- * we can kill the last 5 references to cfb->currcon.
++ * Note that we now use the new fbcon fix, var and cmap scheme. We do
++ * still have to check which console is the currently displayed one
++ * however, especially for the colourmap stuff.
+ *
+- * We also use the new hotplug PCI subsystem. I'm not sure if there are any
+- * such cards, but I'm erring on the side of caution. We don't want to go
+- * pop just because someone does have one.
++ * We also use the new hotplug PCI subsystem. I'm not sure if there
++ * are any such cards, but I'm erring on the side of caution. We don't
++ * want to go pop just because someone does have one.
+ *
+- * Note that this doesn't work fully in the case of multiple CyberPro cards
+- * with grabbers. We currently can only attach to the first CyberPro card
+- * found.
++ * Note that this doesn't work fully in the case of multiple CyberPro
++ * cards with grabbers. We currently can only attach to the first
++ * CyberPro card found.
++ *
++ * When we're in truecolour mode, we power down the LUT RAM as a power
++ * saving feature. Also, when we enter any of the powersaving modes
++ * (except soft blanking) we power down the RAMDACs. This saves about
++ * 1W, which is roughly 8% of the power consumption of a NetWinder
++ * (which, incidentally, is about the same saving as a 2.5in hard disk
++ * entering standby mode.)
+ */
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -55,20 +61,16 @@
+ #include <video/fbcon-cfb24.h>
+ #include <video/fbcon-cfb32.h>
+
+-/*
+- * Define this if you don't want RGB565, but RGB555 for 16bpp displays.
+- */
+-/*#define CFB16_IS_CFB15*/
+-
+ #include "cyber2000fb.h"
+
+ struct cfb_info {
+ struct fb_info fb;
+ struct display_switch *dispsw;
++ struct display *display;
+ struct pci_dev *dev;
+ unsigned char *region;
+ unsigned char *regs;
+- signed int currcon;
++ u_int id;
+ int func_use_count;
+ u_long ref_ps;
+
+@@ -81,10 +83,16 @@
+ u8 red, green, blue;
+ } palette[NR_PALETTE];
+
++ u_char mem_ctl0;
+ u_char mem_ctl1;
+ u_char mem_ctl2;
+ u_char mclk_mult;
+ u_char mclk_div;
++ /*
++ * RAMDAC control register is both of these or'ed together
++ */
++ u_char ramdac_ctrl;
++ u_char ramdac_powerdown;
+ };
+
+ static char default_font_storage[40];
+@@ -144,7 +152,7 @@
+ {
+ int count = 100000;
+
+- while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & 0x80) {
++ while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & CO_CTRL_BUSY) {
+ if (!count--) {
+ debug_printf("accel_wait timed out\n");
+ cyber2000fb_writeb(0, CO_REG_CONTROL, cfb);
+@@ -154,24 +162,23 @@
+ }
+ }
+
+-static void cyber2000_accel_setup(struct display *p)
++static void cyber2000_accel_setup(struct display *display)
+ {
+- struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++ struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+
+- cfb->dispsw->setup(p);
++ cfb->dispsw->setup(display);
+ }
+
+ static void
+-cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
++cyber2000_accel_bmove(struct display *display, int sy, int sx, int dy, int dx,
+ int height, int width)
+ {
+- struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
+- struct fb_var_screeninfo *var = &p->fb_info->var;
++ struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
++ struct fb_var_screeninfo *var = &display->var;
+ u_long src, dst;
+- u_int fh, fw;
+- int cmd = CO_CMD_L_PATTERN_FGCOL;
++ u_int fh, fw, cmd = CO_CMD_L_PATTERN_FGCOL;
+
+- fw = fontwidth(p);
++ fw = fontwidth(display);
+ sx *= fw;
+ dx *= fw;
+ width *= fw;
+@@ -183,7 +190,7 @@
+ cmd |= CO_CMD_L_INC_LEFT;
+ }
+
+- fh = fontheight(p);
++ fh = fontheight(display);
+ sy *= fh;
+ dy *= fh;
+ height *= fh;
+@@ -200,35 +207,34 @@
+
+ cyber2000_accel_wait(cfb);
+ cyber2000fb_writeb(0x00, CO_REG_CONTROL, cfb);
+- cyber2000fb_writeb(0x03, CO_REG_FORE_MIX, cfb);
+- cyber2000fb_writew(width, CO_REG_WIDTH, cfb);
++ cyber2000fb_writew(width, CO_REG_PIXWIDTH, cfb);
++ cyber2000fb_writew(height, CO_REG_PIXHEIGHT, cfb);
+
+- if (var->bits_per_pixel != 24) {
+- cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
+- cyber2000fb_writel(src, CO_REG_SRC_PTR, cfb);
+- } else {
+- cyber2000fb_writel(dst * 3, CO_REG_DEST_PTR, cfb);
++ if (var->bits_per_pixel == 24) {
+ cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb);
+- cyber2000fb_writel(src * 3, CO_REG_SRC_PTR, cfb);
++ dst *= 3;
++ src *= 3;
+ }
+
+- cyber2000fb_writew(height, CO_REG_HEIGHT, cfb);
++ cyber2000fb_writel(src, CO_REG_SRC1_PTR, cfb);
++ cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
++ cyber2000fb_writeb(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb);
+ cyber2000fb_writew(cmd, CO_REG_CMD_L, cfb);
+- cyber2000fb_writew(0x2800, CO_REG_CMD_H, cfb);
++ cyber2000fb_writew(CO_CMD_H_FGSRCMAP|CO_CMD_H_BLITTER, CO_REG_CMD_H, cfb);
+ }
+
+ static void
+-cyber2000_accel_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+- int height, int width)
++cyber2000_accel_clear(struct vc_data *conp, struct display *display, int sy,
++ int sx, int height, int width)
+ {
+- struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
+- struct fb_var_screeninfo *var = &p->fb_info->var;
++ struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
++ struct fb_var_screeninfo *var = &display->var;
+ u_long dst;
+ u_int fw, fh;
+- u32 bgx = attr_bgcol_ec(p, conp);
++ u32 bgx = attr_bgcol_ec(display, conp);
+
+- fw = fontwidth(p);
+- fh = fontheight(p);
++ fw = fontwidth(display);
++ fh = fontheight(display);
+
+ dst = sx * fw + sy * var->xres_virtual * fh;
+ width = width * fw - 1;
+@@ -236,190 +242,229 @@
+
+ cyber2000_accel_wait(cfb);
+ cyber2000fb_writeb(0x00, CO_REG_CONTROL, cfb);
+- cyber2000fb_writeb(0x03, CO_REG_FORE_MIX, cfb);
+- cyber2000fb_writew(width, CO_REG_WIDTH, cfb);
+- cyber2000fb_writew(height, CO_REG_HEIGHT, cfb);
++ cyber2000fb_writew(width, CO_REG_PIXWIDTH, cfb);
++ cyber2000fb_writew(height, CO_REG_PIXHEIGHT, cfb);
+
+- switch (var->bits_per_pixel) {
+- case 15:
+- case 16:
+- bgx = ((u16 *)p->dispsw_data)[bgx];
+- case 8:
+- cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
+- break;
+-
+- case 24:
+- cyber2000fb_writel(dst * 3, CO_REG_DEST_PTR, cfb);
++ if (var->bits_per_pixel == 24) {
+ cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb);
+- bgx = ((u32 *)p->dispsw_data)[bgx];
+- break;
+-
+- case 32:
+- bgx = ((u32 *)p->dispsw_data)[bgx];
+- cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
+- break;
++ dst *= 3;
+ }
+
+- cyber2000fb_writel(bgx, CO_REG_FOREGROUND, cfb);
++ if (var->bits_per_pixel == 16)
++ bgx = ((u16 *)display->dispsw_data)[bgx];
++ else if (var->bits_per_pixel >= 24)
++ bgx = ((u32 *)display->dispsw_data)[bgx];
++
++ cyber2000fb_writel(bgx, CO_REG_FGCOLOUR, cfb);
++ cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
++ cyber2000fb_writeb(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb);
+ cyber2000fb_writew(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L, cfb);
+- cyber2000fb_writew(0x0800, CO_REG_CMD_H, cfb);
++ cyber2000fb_writew(CO_CMD_H_BLITTER, CO_REG_CMD_H, cfb);
+ }
+
+ static void
+-cyber2000_accel_putc(struct vc_data *conp, struct display *p, int c,
++cyber2000_accel_putc(struct vc_data *conp, struct display *display, int c,
+ int yy, int xx)
+ {
+- struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++ struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+
+ cyber2000_accel_wait(cfb);
+- cfb->dispsw->putc(conp, p, c, yy, xx);
++ cfb->dispsw->putc(conp, display, c, yy, xx);
+ }
+
+ static void
+-cyber2000_accel_putcs(struct vc_data *conp, struct display *p,
++cyber2000_accel_putcs(struct vc_data *conp, struct display *display,
+ const unsigned short *s, int count, int yy, int xx)
+ {
+- struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++ struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+
+ cyber2000_accel_wait(cfb);
+- cfb->dispsw->putcs(conp, p, s, count, yy, xx);
++ cfb->dispsw->putcs(conp, display, s, count, yy, xx);
+ }
+
+-static void cyber2000_accel_revc(struct display *p, int xx, int yy)
++static void cyber2000_accel_revc(struct display *display, int xx, int yy)
+ {
+- struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++ struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+
+ cyber2000_accel_wait(cfb);
+- cfb->dispsw->revc(p, xx, yy);
++ cfb->dispsw->revc(display, xx, yy);
+ }
+
+ static void
+-cyber2000_accel_clear_margins(struct vc_data *conp, struct display *p,
++cyber2000_accel_clear_margins(struct vc_data *conp, struct display *display,
+ int bottom_only)
+ {
+- struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++ struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+
+- cfb->dispsw->clear_margins(conp, p, bottom_only);
++ cfb->dispsw->clear_margins(conp, display, bottom_only);
+ }
+
+ static struct display_switch fbcon_cyber_accel = {
+- setup: cyber2000_accel_setup,
+- bmove: cyber2000_accel_bmove,
+- clear: cyber2000_accel_clear,
+- putc: cyber2000_accel_putc,
+- putcs: cyber2000_accel_putcs,
+- revc: cyber2000_accel_revc,
+- clear_margins: cyber2000_accel_clear_margins,
+- fontwidthmask: FONTWIDTH(8)|FONTWIDTH(16)
++ .setup = cyber2000_accel_setup,
++ .bmove = cyber2000_accel_bmove,
++ .clear = cyber2000_accel_clear,
++ .putc = cyber2000_accel_putc,
++ .putcs = cyber2000_accel_putcs,
++ .revc = cyber2000_accel_revc,
++ .clear_margins = cyber2000_accel_clear_margins,
++ .fontwidthmask = FONTWIDTH(8)|FONTWIDTH(16)
+ };
+
++static inline u32 convert_bitfield(u_int val, struct fb_bitfield *bf)
++{
++ u_int mask = (1 << bf->length) - 1;
++
++ return (val >> (16 - bf->length) & mask) << bf->offset;
++}
++
+ /*
+ * Set a single color register. Return != 0 for invalid regno.
+ */
+ static int
+-cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++cyber2000fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+ {
+ struct cfb_info *cfb = (struct cfb_info *)info;
++ struct fb_var_screeninfo *var = &cfb->display->var;
++ u32 pseudo_val;
++ int ret = 1;
+
+- u_int alpha = transp ^ 0xFFFF;
++ switch (cfb->fb.fix.visual) {
++ default:
++ return 1;
+
++#ifdef FBCON_HAS_CFB8
++ /*
++ * Pseudocolour:
++ * 8 8
++ * pixel --/--+--/--> red lut --> red dac
++ * | 8
++ * +--/--> green lut --> green dac
++ * | 8
++ * +--/--> blue lut --> blue dac
++ */
++ case FB_VISUAL_PSEUDOCOLOR:
+ if (regno >= NR_PALETTE)
+ return 1;
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+- alpha >>= 8;
+
+ cfb->palette[regno].red = red;
+ cfb->palette[regno].green = green;
+ cfb->palette[regno].blue = blue;
+
+- switch (cfb->fb.var.bits_per_pixel) {
+-#ifdef FBCON_HAS_CFB8
+- case 8:
+ cyber2000fb_writeb(regno, 0x3c8, cfb);
+ cyber2000fb_writeb(red, 0x3c9, cfb);
+ cyber2000fb_writeb(green, 0x3c9, cfb);
+ cyber2000fb_writeb(blue, 0x3c9, cfb);
+- break;
++ return 0;
+ #endif
+
+-#ifdef FBCON_HAS_CFB16
+- case 16:
+-#ifndef CFB16_IS_CFB15
+- if (regno < 64) {
+- /* write green */
++ /*
++ * Direct colour:
++ * n rl
++ * pixel --/--+--/--> red lut --> red dac
++ * | gl
++ * +--/--> green lut --> green dac
++ * | bl
++ * +--/--> blue lut --> blue dac
++ * n = bpp, rl = red length, gl = green length, bl = blue length
++ */
++ case FB_VISUAL_DIRECTCOLOR:
++ red >>= 8;
++ green >>= 8;
++ blue >>= 8;
++
++ if (var->green.length == 6 && regno < 64) {
++ cfb->palette[regno << 2].green = green;
++
++ /*
++ * The 6 bits of the green component are applied
++ * to the high 6 bits of the LUT.
++ */
+ cyber2000fb_writeb(regno << 2, 0x3c8, cfb);
+ cyber2000fb_writeb(cfb->palette[regno >> 1].red, 0x3c9, cfb);
+ cyber2000fb_writeb(green, 0x3c9, cfb);
+ cyber2000fb_writeb(cfb->palette[regno >> 1].blue, 0x3c9, cfb);
+- }
+
+- if (regno < 32) {
+- /* write red,blue */
+- cyber2000fb_writeb(regno << 3, 0x3c8, cfb);
+- cyber2000fb_writeb(red, 0x3c9, cfb);
+- cyber2000fb_writeb(cfb->palette[regno << 1].green, 0x3c9, cfb);
+- cyber2000fb_writeb(blue, 0x3c9, cfb);
+- }
++ green = cfb->palette[regno << 3].green;
+
+- if (regno < 16)
+- ((u16 *)cfb->fb.pseudo_palette)[regno] =
+- ((red << 8) & 0xf800) |
+- ((green << 3) & 0x07e0) |
+- ((blue >> 3));
+- break;
+-#endif
++ ret = 0;
++ }
+
+- case 15:
+- if (regno < 32) {
++ if (var->green.length >= 5 && regno < 32) {
++ cfb->palette[regno << 3].red = red;
++ cfb->palette[regno << 3].green = green;
++ cfb->palette[regno << 3].blue = blue;
++
++ /*
++ * The 5 bits of each colour component are
++ * applied to the high 5 bits of the LUT.
++ */
+ cyber2000fb_writeb(regno << 3, 0x3c8, cfb);
+ cyber2000fb_writeb(red, 0x3c9, cfb);
+ cyber2000fb_writeb(green, 0x3c9, cfb);
+ cyber2000fb_writeb(blue, 0x3c9, cfb);
++ ret = 0;
+ }
+- if (regno < 16)
+- ((u16 *)cfb->fb.pseudo_palette)[regno] =
+- ((red << 7) & 0x7c00) |
+- ((green << 2) & 0x03e0) |
+- ((blue >> 3));
+- break;
+-
+-#endif
+
+-#ifdef FBCON_HAS_CFB24
+- case 24:
+- cyber2000fb_writeb(regno, 0x3c8, cfb);
++ if (var->green.length == 4 && regno < 16) {
++ cfb->palette[regno << 4].red = red;
++ cfb->palette[regno << 4].green = green;
++ cfb->palette[regno << 4].blue = blue;
++
++ /*
++ * The 5 bits of each colour component are
++ * applied to the high 5 bits of the LUT.
++ */
++ cyber2000fb_writeb(regno << 4, 0x3c8, cfb);
+ cyber2000fb_writeb(red, 0x3c9, cfb);
+ cyber2000fb_writeb(green, 0x3c9, cfb);
+ cyber2000fb_writeb(blue, 0x3c9, cfb);
++ ret = 0;
++ }
+
+- if (regno < 16)
+- ((u32 *)cfb->fb.pseudo_palette)[regno] =
+- (red << 16) | (green << 8) | blue;
++ /*
++ * Since this is only used for the first 16 colours, we
++ * don't have to care about overflowing for regno >= 32
++ */
++ pseudo_val = regno << var->red.offset |
++ regno << var->green.offset |
++ regno << var->blue.offset;
+ break;
+-#endif
+-
+-#ifdef FBCON_HAS_CFB32
+- case 32:
+- cyber2000fb_writeb(regno, 0x3c8, cfb);
+- cyber2000fb_writeb(red, 0x3c9, cfb);
+- cyber2000fb_writeb(green, 0x3c9, cfb);
+- cyber2000fb_writeb(blue, 0x3c9, cfb);
+
+- if (regno < 16)
+- ((u32 *)cfb->fb.pseudo_palette)[regno] =
+- (alpha << 24) | (red << 16) | (green << 8) | blue;
++ /*
++ * True colour:
++ * n rl
++ * pixel --/--+--/--> red dac
++ * | gl
++ * +--/--> green dac
++ * | bl
++ * +--/--> blue dac
++ * n = bpp, rl = red length, gl = green length, bl = blue length
++ */
++ case FB_VISUAL_TRUECOLOR:
++ pseudo_val = convert_bitfield(transp ^ 0xffff, &var->transp);
++ pseudo_val |= convert_bitfield(red, &var->red);
++ pseudo_val |= convert_bitfield(green, &var->green);
++ pseudo_val |= convert_bitfield(blue, &var->blue);
+ break;
+-#endif
++ }
+
+- default:
+- return 1;
++ /*
++ * Now set our pseudo palette for the CFB16/24/32 drivers.
++ */
++ if (regno < 16) {
++ if (var->bits_per_pixel == 16)
++ ((u16 *)cfb->fb.pseudo_palette)[regno] = pseudo_val;
++ else
++ ((u32 *)cfb->fb.pseudo_palette)[regno] = pseudo_val;
++ ret = 0;
+ }
+
+- return 0;
++ return ret;
+ }
+
+ struct par_info {
+@@ -428,8 +473,8 @@
+ */
+ u_char clock_mult;
+ u_char clock_div;
+- u_char visualid;
+- u_char pixformat;
++ u_char extseqmisc;
++ u_char co_pixfmt;
+ u_char crtc_ofl;
+ u_char crtc[19];
+ u_int width;
+@@ -439,8 +484,7 @@
+ /*
+ * Other
+ */
+- u_char palette_ctrl;
+- u_int vmode;
++ u_char ramdac;
+ };
+
+ static const u_char crtc_idx[] = {
+@@ -449,6 +493,18 @@
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
+ };
+
++static void cyber2000fb_write_ramdac_ctrl(struct cfb_info *cfb)
++{
++ unsigned int i;
++ unsigned int val = cfb->ramdac_ctrl | cfb->ramdac_powerdown;
++
++ cyber2000fb_writeb(0x56, 0x3ce, cfb);
++ i = cyber2000fb_readb(0x3cf, cfb);
++ cyber2000fb_writeb(i | 4, 0x3cf, cfb);
++ cyber2000fb_writeb(val, 0x3c6, cfb);
++ cyber2000fb_writeb(i, 0x3cf, cfb);
++}
++
+ static void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw)
+ {
+ u_int i;
+@@ -480,7 +536,7 @@
+ for (i = 0x0a; i < 0x10; i++)
+ cyber2000_crtcw(i, 0, cfb);
+
+- cyber2000_grphw(0x11, hw->crtc_ofl, cfb);
++ cyber2000_grphw(EXT_CRT_VRTOFL, hw->crtc_ofl, cfb);
+ cyber2000_grphw(0x00, 0x00, cfb);
+ cyber2000_grphw(0x01, 0x00, cfb);
+ cyber2000_grphw(0x02, 0x00, cfb);
+@@ -501,30 +557,17 @@
+ cyber2000_attrw(0x13, 0x00, cfb);
+ cyber2000_attrw(0x14, 0x00, cfb);
+
+- /* woody: set the interlaced bit... */
+- /* FIXME: what about doublescan? */
+- cyber2000fb_writeb(0x11, 0x3ce, cfb);
+- i = cyber2000fb_readb(0x3cf, cfb);
+- if (hw->vmode == FB_VMODE_INTERLACED)
+- i |= 0x20;
+- else
+- i &= ~0x20;
+- cyber2000fb_writeb(i, 0x3cf, cfb);
+-
+ /* PLL registers */
+- cyber2000_grphw(DCLK_MULT, hw->clock_mult, cfb);
+- cyber2000_grphw(DCLK_DIV, hw->clock_div, cfb);
+- cyber2000_grphw(MCLK_MULT, cfb->mclk_mult, cfb);
+- cyber2000_grphw(MCLK_DIV, cfb->mclk_div, cfb);
++ cyber2000_grphw(EXT_DCLK_MULT, hw->clock_mult, cfb);
++ cyber2000_grphw(EXT_DCLK_DIV, hw->clock_div, cfb);
++ cyber2000_grphw(EXT_MCLK_MULT, cfb->mclk_mult, cfb);
++ cyber2000_grphw(EXT_MCLK_DIV, cfb->mclk_div, cfb);
+ cyber2000_grphw(0x90, 0x01, cfb);
+ cyber2000_grphw(0xb9, 0x80, cfb);
+ cyber2000_grphw(0xb9, 0x00, cfb);
+
+- cyber2000fb_writeb(0x56, 0x3ce, cfb);
+- i = cyber2000fb_readb(0x3cf, cfb);
+- cyber2000fb_writeb(i | 4, 0x3cf, cfb);
+- cyber2000fb_writeb(hw->palette_ctrl, 0x3c6, cfb);
+- cyber2000fb_writeb(i, 0x3cf, cfb);
++ cfb->ramdac_ctrl = hw->ramdac;
++ cyber2000fb_write_ramdac_ctrl(cfb);
+
+ cyber2000fb_writeb(0x20, 0x3c0, cfb);
+ cyber2000fb_writeb(0xff, 0x3c6, cfb);
+@@ -532,31 +575,32 @@
+ cyber2000_grphw(0x14, hw->fetch, cfb);
+ cyber2000_grphw(0x15, ((hw->fetch >> 8) & 0x03) |
+ ((hw->pitch >> 4) & 0x30), cfb);
+- cyber2000_grphw(0x77, hw->visualid, cfb);
++ cyber2000_grphw(EXT_SEQ_MISC, hw->extseqmisc, cfb);
+
+- /* make sure we stay in linear mode */
+- cyber2000_grphw(0x33, 0x0d, cfb);
++// cyber2000_grphw(EXT_BIU_MISC, EXT_BIU_MISC_LIN_ENABLE |
++// EXT_BIU_MISC_COP_ENABLE |
++// EXT_BIU_MISC_COP_BFC, cfb);
+
+ /*
+ * Set up accelerator registers
+ */
+ cyber2000fb_writew(hw->width, CO_REG_SRC_WIDTH, cfb);
+ cyber2000fb_writew(hw->width, CO_REG_DEST_WIDTH, cfb);
+- cyber2000fb_writeb(hw->pixformat, CO_REG_PIX_FORMAT, cfb);
++ cyber2000fb_writeb(hw->co_pixfmt, CO_REG_PIXFMT, cfb);
+ }
+
+ static inline int
+ cyber2000fb_update_start(struct cfb_info *cfb, struct fb_var_screeninfo *var)
+ {
+- u_int base;
++ u_int base = var->yoffset * var->xres_virtual + var->xoffset;
+
+- base = var->yoffset * var->xres_virtual + var->xoffset;
++ base *= var->bits_per_pixel;
+
+- /* have to be careful, because bits_per_pixel might be 15
+- in this version of the driver -- dok at directfb.org 2002/06/13 */
+- base *= (var->bits_per_pixel + 7) >> 3;
+-
+- base >>= 2;
++ /*
++ * Convert to bytes and shift two extra bits because DAC
++ * can only start on 4 byte aligned data.
++ */
++ base >>= 5;
+
+ if (base >= 1 << 20)
+ return -EINVAL;
+@@ -576,27 +620,20 @@
+ struct fb_info *info)
+ {
+ struct cfb_info *cfb = (struct cfb_info *)info;
+- struct fb_cmap *dcmap = &fb_display[con].cmap;
++ struct display *display = fb_display + con;
++ struct fb_cmap *dcmap = &display->cmap;
+ int err = 0;
+
+ /* no colormap allocated? */
+- if (!dcmap->len) {
+- int size;
+-
+- if (cfb->fb.var.bits_per_pixel == 16)
+- size = 32;
+- else
+- size = 256;
+-
+- err = fb_alloc_cmap(dcmap, size, 0);
+- }
++ if (!dcmap->len)
++ err = fb_alloc_cmap(dcmap, 256, 0);
+
+ /*
+ * we should be able to remove this test once fbcon has been
+ * "improved" --rmk
+ */
+- if (!err && con == cfb->currcon) {
+- err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg, &cfb->fb);
++ if (!err && display == cfb->display) {
++ err = fb_set_cmap(cmap, kspc, cyber2000fb_setcolreg, &cfb->fb);
+ dcmap = &cfb->fb.cmap;
+ }
+
+@@ -672,8 +709,9 @@
+ hw->crtc[16] = Vblankend;
+ hw->crtc[18] = 0xff;
+
+- /* overflow - graphics reg 0x11 */
+- /* 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10
++ /*
++ * overflow - graphics reg 0x11
++ * 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10
+ * 4=LINECOMP:10 5-IVIDEO 6=FIXCNT
+ */
+ hw->crtc_ofl =
+@@ -681,7 +719,12 @@
+ BIT(Vdispend, 10, 0x01, 1) |
+ BIT(Vsyncstart, 10, 0x01, 2) |
+ BIT(Vblankstart,10, 0x01, 3) |
+- 1 << 4;
++ EXT_CRT_VRTOFL_LINECOMP10;
++
++ /* woody: set the interlaced bit... */
++ /* FIXME: what about doublescan? */
++ if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
++ hw->crtc_ofl |= EXT_CRT_VRTOFL_INTERLACE;
+
+ return 0;
+ }
+@@ -787,7 +830,7 @@
+ vco = ref_ps * best_div1 / best_mult;
+ if ((ref_ps == 40690) && (vco < 5556))
+ /* Set VFSEL when VCO > 180MHz (5.556 ps). */
+- hw->clock_div |= DCLK_DIV_VFSEL;
++ hw->clock_div |= EXT_DCLK_DIV_VFSEL;
+
+ return 0;
+ }
+@@ -801,58 +844,131 @@
+ cyber2000fb_decode_var(struct fb_var_screeninfo *var, struct cfb_info *cfb,
+ struct par_info *hw)
+ {
++ unsigned int mem;
+ int err;
+
+ hw->width = var->xres_virtual;
+- hw->palette_ctrl = 0x06;
+- hw->vmode = var->vmode;
++ hw->ramdac = RAMDAC_VREFEN | RAMDAC_DAC8BIT;
++
++ var->transp.msb_right = 0;
++ var->red.msb_right = 0;
++ var->green.msb_right = 0;
++ var->blue.msb_right = 0;
+
+ switch (var->bits_per_pixel) {
+ #ifdef FBCON_HAS_CFB8
+ case 8: /* PSEUDOCOLOUR, 256 */
+- hw->pixformat = PIXFORMAT_8BPP;
+- hw->visualid = VISUALID_256;
++ hw->co_pixfmt = CO_PIXFMT_8BPP;
+ hw->pitch = hw->width >> 3;
++ hw->extseqmisc = EXT_SEQ_MISC_8;
++
++ var->transp.offset = 0;
++ var->transp.length = 0;
++ var->red.offset = 0;
++ var->red.length = 8;
++ var->green.offset = 0;
++ var->green.length = 8;
++ var->blue.offset = 0;
++ var->blue.length = 8;
+ break;
+ #endif
+ #ifdef FBCON_HAS_CFB16
+- case 16:/* DIRECTCOLOUR, 64k */
+-#ifndef CFB16_IS_CFB15
+- hw->pixformat = PIXFORMAT_16BPP;
+- hw->visualid = VISUALID_64K;
+- hw->pitch = hw->width >> 2;
+- hw->palette_ctrl |= 0x10;
+- break;
+-#endif
+- case 15:/* DIRECTCOLOUR, 32k */
+- hw->pixformat = PIXFORMAT_16BPP;
+- hw->visualid = VISUALID_32K;
++ case 16:/* DIRECTCOLOUR, 64k or 32k */
++ hw->co_pixfmt = CO_PIXFMT_16BPP;
+ hw->pitch = hw->width >> 2;
+- hw->palette_ctrl |= 0x10;
+- break;
+
++ switch (var->green.length) {
++ case 6: /* RGB565, 64k */
++ hw->extseqmisc = EXT_SEQ_MISC_16_RGB565;
++
++ var->transp.offset = 0;
++ var->transp.length = 0;
++ var->red.offset = 11;
++ var->red.length = 5;
++ var->green.offset = 5;
++ var->green.length = 6;
++ var->blue.offset = 0;
++ var->blue.length = 5;
++ break;
++
++ default:
++ case 5: /* RGB555, 32k */
++ hw->extseqmisc = EXT_SEQ_MISC_16_RGB555;
++
++ var->transp.offset = 0;
++ var->transp.length = 0;
++ var->red.offset = 10;
++ var->red.length = 5;
++ var->green.offset = 5;
++ var->green.length = 5;
++ var->blue.offset = 0;
++ var->blue.length = 5;
++ break;
++
++ case 4: /* RGB444, 4k + transparency? */
++ hw->extseqmisc = EXT_SEQ_MISC_16_RGB444;
++
++ var->transp.offset = 12;
++ var->transp.length = 4;
++ var->red.offset = 8;
++ var->red.length = 4;
++ var->green.offset = 4;
++ var->green.length = 4;
++ var->blue.offset = 0;
++ var->blue.length = 4;
++ break;
++ }
++ break;
+ #endif
+ #ifdef FBCON_HAS_CFB24
+ case 24:/* TRUECOLOUR, 16m */
+- hw->pixformat = PIXFORMAT_24BPP;
+- hw->visualid = VISUALID_16M;
++ hw->co_pixfmt = CO_PIXFMT_24BPP;
+ hw->width *= 3;
+ hw->pitch = hw->width >> 3;
+- hw->palette_ctrl |= 0x10;
++ hw->ramdac |= (RAMDAC_BYPASS | RAMDAC_RAMPWRDN);
++ hw->extseqmisc = EXT_SEQ_MISC_24_RGB888;
++
++ var->transp.offset = 0;
++ var->transp.length = 0;
++ var->red.offset = 16;
++ var->red.length = 8;
++ var->green.offset = 8;
++ var->green.length = 8;
++ var->blue.offset = 0;
++ var->blue.length = 8;
+ break;
+ #endif
+ #ifdef FBCON_HAS_CFB32
+ case 32:/* TRUECOLOUR, 16m */
+- hw->pixformat = PIXFORMAT_32BPP;
+- hw->visualid = VISUALID_16M_32;
++ hw->co_pixfmt = CO_PIXFMT_32BPP;
+ hw->pitch = hw->width >> 1;
+- hw->palette_ctrl |= 0x10;
++ hw->ramdac |= (RAMDAC_BYPASS | RAMDAC_RAMPWRDN);
++ hw->extseqmisc = EXT_SEQ_MISC_32;
++
++ var->transp.offset = 24;
++ var->transp.length = 8;
++ var->red.offset = 16;
++ var->red.length = 8;
++ var->green.offset = 8;
++ var->green.length = 8;
++ var->blue.offset = 0;
++ var->blue.length = 8;
+ break;
+ #endif
+ default:
+ return -EINVAL;
+ }
+
++ mem = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8);
++ if (mem > cfb->fb.fix.smem_len)
++ var->yres_virtual = cfb->fb.fix.smem_len * 8 /
++ (var->bits_per_pixel * var->xres_virtual);
++
++ if (var->yres > var->yres_virtual)
++ var->yres = var->yres_virtual;
++ if (var->xres > var->xres_virtual)
++ var->xres = var->xres_virtual;
++
+ err = cyber2000fb_decode_clock(hw, cfb, var);
+ if (err)
+ return err;
+@@ -880,7 +996,7 @@
+ struct cfb_info *cfb = (struct cfb_info *)info;
+ struct display *display;
+ struct par_info hw;
+- int err, chgvar = 0;
++ int err, chgvar;
+
+ /*
+ * CONUPDATE and SMOOTH_XPAN are equal. However,
+@@ -888,11 +1004,11 @@
+ */
+ if (var->vmode & FB_VMODE_CONUPDATE) {
+ var->vmode |= FB_VMODE_YWRAP;
+- var->xoffset = cfb->fb.var.xoffset;
+- var->yoffset = cfb->fb.var.yoffset;
++ var->xoffset = cfb->display->var.xoffset;
++ var->yoffset = cfb->display->var.yoffset;
+ }
+
+- err = cyber2000fb_decode_var(var, (struct cfb_info *)info, &hw);
++ err = cyber2000fb_decode_var(var, cfb, &hw);
+ if (err)
+ return err;
+
+@@ -902,105 +1018,61 @@
+ if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
+ return -EINVAL;
+
+- if (cfb->fb.var.xres != var->xres)
+- chgvar = 1;
+- if (cfb->fb.var.yres != var->yres)
+- chgvar = 1;
+- if (cfb->fb.var.xres_virtual != var->xres_virtual)
+- chgvar = 1;
+- if (cfb->fb.var.yres_virtual != var->yres_virtual)
+- chgvar = 1;
+- if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
+- chgvar = 1;
+-
+ if (con < 0) {
+ display = cfb->fb.disp;
+- chgvar = 0;
+ } else {
+ display = fb_display + con;
+ }
+
+- var->red.msb_right = 0;
+- var->green.msb_right = 0;
+- var->blue.msb_right = 0;
++ chgvar = cfb->fb.var.xres != var->xres ||
++ cfb->fb.var.yres != var->yres ||
++ cfb->fb.var.xres_virtual != var->xres_virtual ||
++ cfb->fb.var.yres_virtual != var->yres_virtual ||
++ cfb->fb.var.bits_per_pixel != var->bits_per_pixel;
++
++ if (memcmp(&cfb->fb.var.red, &var->red, sizeof(var->red)) ||
++ memcmp(&cfb->fb.var.green, &var->green, sizeof(var->green)) ||
++ memcmp(&cfb->fb.var.blue, &var->blue, sizeof(var->blue)))
++ chgvar = 1;
++
++ if (con < 0)
++ chgvar = 0;
++
++ /*
++ * If we are setting all the virtual consoles, also set the
++ * defaults used to create new consoles.
++ */
++ err = var->activate;
++ var->activate = FB_ACTIVATE_NOW;
++ if (err & FB_ACTIVATE_ALL)
++ cfb->fb.disp->var = *var;
++
++ cfb->fb.var = *var;
++ cfb->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ switch (var->bits_per_pixel) {
+ #ifdef FBCON_HAS_CFB8
+ case 8: /* PSEUDOCOLOUR, 256 */
+- var->red.offset = 0;
+- var->red.length = 8;
+- var->green.offset = 0;
+- var->green.length = 8;
+- var->blue.offset = 0;
+- var->blue.length = 8;
+-
+- cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ cfb->dispsw = &fbcon_cfb8;
+ display->dispsw_data = NULL;
+- display->next_line = var->xres_virtual;
+ break;
+ #endif
+ #ifdef FBCON_HAS_CFB16
+- case 16:/* DIRECTCOLOUR, 64k */
+-#ifndef CFB16_IS_CFB15
+- var->red.offset = 11;
+- var->red.length = 5;
+- var->green.offset = 5;
+- var->green.length = 6;
+- var->blue.offset = 0;
+- var->blue.length = 5;
+-
+- cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR;
+- cfb->dispsw = &fbcon_cfb16;
+- display->dispsw_data = cfb->fb.pseudo_palette;
+- display->next_line = var->xres_virtual * 2;
+- break;
+-#endif
+- case 15:/* DIRECTCOLOUR, 32k */
+- var->bits_per_pixel = 15;
+- var->red.offset = 10;
+- var->red.length = 5;
+- var->green.offset = 5;
+- var->green.length = 5;
+- var->blue.offset = 0;
+- var->blue.length = 5;
+-
+- cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR;
++ case 16:/* DIRECTCOLOUR */
+ cfb->dispsw = &fbcon_cfb16;
+ display->dispsw_data = cfb->fb.pseudo_palette;
+- display->next_line = var->xres_virtual * 2;
+ break;
+ #endif
+ #ifdef FBCON_HAS_CFB24
+ case 24:/* TRUECOLOUR, 16m */
+- var->red.offset = 16;
+- var->red.length = 8;
+- var->green.offset = 8;
+- var->green.length = 8;
+- var->blue.offset = 0;
+- var->blue.length = 8;
+-
+- cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+ cfb->dispsw = &fbcon_cfb24;
+ display->dispsw_data = cfb->fb.pseudo_palette;
+- display->next_line = var->xres_virtual * 3;
+ break;
+ #endif
+ #ifdef FBCON_HAS_CFB32
+ case 32:/* TRUECOLOUR, 16m */
+- var->transp.offset = 24;
+- var->transp.length = 8;
+- var->red.offset = 16;
+- var->red.length = 8;
+- var->green.offset = 8;
+- var->green.length = 8;
+- var->blue.offset = 0;
+- var->blue.length = 8;
+-
+- cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+ cfb->dispsw = &fbcon_cfb32;
+ display->dispsw_data = cfb->fb.pseudo_palette;
+- display->next_line = var->xres_virtual * 4;
+ break;
+ #endif
+ default:/* in theory this should never happen */
+@@ -1010,15 +1082,27 @@
+ break;
+ }
+
++ /*
++ * 8bpp displays are always pseudo colour.
++ * 16bpp and above are direct colour or true colour, depending
++ * on whether the RAMDAC palettes are bypassed. (Direct colour
++ * has palettes, true colour does not.)
++ */
++ if (var->bits_per_pixel == 8)
++ cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ else if (hw.ramdac & RAMDAC_BYPASS)
++ cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
++ else
++ cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR;
++
+ if (var->accel_flags & FB_ACCELF_TEXT && cfb->dispsw != &fbcon_dummy)
+ display->dispsw = &fbcon_cyber_accel;
+ else
+ display->dispsw = cfb->dispsw;
+
+- cfb->fb.fix.line_length = display->next_line;
+-
+ display->screen_base = cfb->fb.screen_base;
+ display->line_length = cfb->fb.fix.line_length;
++ display->next_line = cfb->fb.fix.line_length;
+ display->visual = cfb->fb.fix.visual;
+ display->type = cfb->fb.fix.type;
+ display->type_aux = cfb->fb.fix.type_aux;
+@@ -1026,31 +1110,15 @@
+ display->ywrapstep = cfb->fb.fix.ywrapstep;
+ display->can_soft_blank = 1;
+ display->inverse = 0;
++ display->var = *var;
+
+- cfb->fb.var = *var;
+- cfb->fb.var.activate &= ~FB_ACTIVATE_ALL;
+-
+- /*
+- * Update the old var. The fbcon drivers still use this.
+- * Once they are using cfb->fb.var, this can be dropped.
+- * --rmk
+- */
+- display->var = cfb->fb.var;
+-
+- /*
+- * If we are setting all the virtual consoles, also set the
+- * defaults used to create new consoles.
+- */
+- if (var->activate & FB_ACTIVATE_ALL)
+- cfb->fb.disp->var = cfb->fb.var;
++ cyber2000fb_set_timing(cfb, &hw);
++ cyber2000fb_update_start(cfb, var);
++ fb_set_cmap(&cfb->fb.cmap, 1, cyber2000fb_setcolreg, &cfb->fb);
+
+- if (chgvar && info && cfb->fb.changevar)
++ if (chgvar && cfb->fb.changevar)
+ cfb->fb.changevar(con);
+
+- cyber2000fb_update_start(cfb, var);
+- cyber2000fb_set_timing(cfb, &hw);
+- fb_set_cmap(&cfb->fb.cmap, 1, cyber2000_setcolreg, &cfb->fb);
+-
+ return 0;
+ }
+
+@@ -1072,18 +1140,18 @@
+
+ if (var->xoffset > (var->xres_virtual - var->xres))
+ return -EINVAL;
+- if (y_bottom > cfb->fb.var.yres_virtual)
++ if (y_bottom > cfb->display->var.yres_virtual)
+ return -EINVAL;
+
+ if (cyber2000fb_update_start(cfb, var))
+ return -EINVAL;
+
+- cfb->fb.var.xoffset = var->xoffset;
+- cfb->fb.var.yoffset = var->yoffset;
++ cfb->display->var.xoffset = var->xoffset;
++ cfb->display->var.yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP) {
+- cfb->fb.var.vmode |= FB_VMODE_YWRAP;
++ cfb->display->var.vmode |= FB_VMODE_YWRAP;
+ } else {
+- cfb->fb.var.vmode &= ~FB_VMODE_YWRAP;
++ cfb->display->var.vmode &= ~FB_VMODE_YWRAP;
+ }
+
+ return 0;
+@@ -1106,22 +1174,18 @@
+ static int cyber2000fb_switch(int con, struct fb_info *info)
+ {
+ struct cfb_info *cfb = (struct cfb_info *)info;
+- struct display *disp;
++ struct display *display = cfb->display;
+ struct fb_cmap *cmap;
+
+- if (cfb->currcon >= 0) {
+- disp = fb_display + cfb->currcon;
+-
++ if (display) {
+ /*
+ * Save the old colormap and video mode.
+ */
+- disp->var = cfb->fb.var;
+- if (disp->cmap.len)
+- fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0);
++ if (display->cmap.len)
++ fb_copy_cmap(&cfb->fb.cmap, &display->cmap, 0);
+ }
+
+- cfb->currcon = con;
+- disp = fb_display + con;
++ cfb->display = display = fb_display + con;
+
+ /*
+ * Install the new colormap and change the video mode. By default,
+@@ -1132,30 +1196,22 @@
+ * depth of the new video mode. For now, we leave it at its
+ * default 256 entry.
+ */
+- if (disp->cmap.len)
+- cmap = &disp->cmap;
++ if (display->cmap.len)
++ cmap = &display->cmap;
+ else
+- cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
++ cmap = fb_default_cmap(1 << display->var.bits_per_pixel);
+
+ fb_copy_cmap(cmap, &cfb->fb.cmap, 0);
+
+- cfb->fb.var = disp->var;
+- cfb->fb.var.activate = FB_ACTIVATE_NOW;
+-
+- cyber2000fb_set_var(&cfb->fb.var, con, &cfb->fb);
++ display->var.activate = FB_ACTIVATE_NOW;
++ cyber2000fb_set_var(&display->var, con, &cfb->fb);
+
+ return 0;
+ }
+
+ /*
+ * (Un)Blank the display.
+- */
+-static void cyber2000fb_blank(int blank, struct fb_info *info)
+-{
+- struct cfb_info *cfb = (struct cfb_info *)info;
+- int i;
+-
+- /*
++ *
+ * Blank the screen if blank_mode != 0, else unblank. If
+ * blank == NULL then the caller blanks by setting the CLUT
+ * (Color Look Up Table) to all black. Return 0 if blanking
+@@ -1170,35 +1226,58 @@
+ * wms...Enable VESA DMPS compatible powerdown mode
+ * run "setterm -powersave powerdown" to take advantage
+ */
++static void cyber2000fb_blank(int blank, struct fb_info *info)
++{
++ struct cfb_info *cfb = (struct cfb_info *)info;
++ unsigned int sync = 0;
++ int i;
+
+ switch (blank) {
+ case 4: /* powerdown - both sync lines down */
+- cyber2000_grphw(0x16, 0x05, cfb);
++ sync = EXT_SYNC_CTL_VS_0 | EXT_SYNC_CTL_HS_0;
+ break;
+ case 3: /* hsync off */
+- cyber2000_grphw(0x16, 0x01, cfb);
++ sync = EXT_SYNC_CTL_VS_NORMAL | EXT_SYNC_CTL_HS_0;
+ break;
+ case 2: /* vsync off */
+- cyber2000_grphw(0x16, 0x04, cfb);
++ sync = EXT_SYNC_CTL_VS_0 | EXT_SYNC_CTL_HS_NORMAL;
+ break;
+ case 1: /* soft blank */
+- cyber2000_grphw(0x16, 0x00, cfb);
++ default: /* unblank */
++ break;
++ }
++
++ cyber2000_grphw(EXT_SYNC_CTL, sync, cfb);
++
++ if (blank <= 1) {
++ /* turn on ramdacs */
++ cfb->ramdac_powerdown &= ~(RAMDAC_DACPWRDN | RAMDAC_BYPASS | RAMDAC_RAMPWRDN);
++ cyber2000fb_write_ramdac_ctrl(cfb);
++ }
++
++ /*
++ * Soft blank/unblank the display.
++ */
++ if (blank) { /* soft blank */
+ for (i = 0; i < NR_PALETTE; i++) {
+ cyber2000fb_writeb(i, 0x3c8, cfb);
+ cyber2000fb_writeb(0, 0x3c9, cfb);
+ cyber2000fb_writeb(0, 0x3c9, cfb);
+ cyber2000fb_writeb(0, 0x3c9, cfb);
+ }
+- break;
+- default: /* unblank */
+- cyber2000_grphw(0x16, 0x00, cfb);
++ } else { /* unblank */
+ for (i = 0; i < NR_PALETTE; i++) {
+ cyber2000fb_writeb(i, 0x3c8, cfb);
+ cyber2000fb_writeb(cfb->palette[i].red, 0x3c9, cfb);
+ cyber2000fb_writeb(cfb->palette[i].green, 0x3c9, cfb);
+ cyber2000fb_writeb(cfb->palette[i].blue, 0x3c9, cfb);
+ }
+- break;
++ }
++
++ if (blank >= 2) {
++ /* turn off ramdacs */
++ cfb->ramdac_powerdown |= RAMDAC_DACPWRDN | RAMDAC_BYPASS | RAMDAC_RAMPWRDN;
++ cyber2000fb_write_ramdac_ctrl(cfb);
+ }
+ }
+
+@@ -1233,51 +1312,61 @@
+ }
+
+ static struct fb_ops cyber2000fb_ops = {
+- owner: THIS_MODULE,
+- fb_set_var: cyber2000fb_set_var,
+- fb_set_cmap: cyber2000fb_set_cmap,
+- fb_pan_display: cyber2000fb_pan_display,
+- fb_get_fix: gen_get_fix,
+- fb_get_var: gen_get_var,
+- fb_get_cmap: gen_get_cmap,
++ .owner = THIS_MODULE,
++ .fb_set_var = cyber2000fb_set_var,
++ .fb_set_cmap = cyber2000fb_set_cmap,
++ .fb_pan_display = cyber2000fb_pan_display,
++ .fb_get_fix = gen_get_fix,
++ .fb_get_var = gen_get_var,
++ .fb_get_cmap = gen_get_cmap,
+ };
+
+ /*
++ * This is the only "static" reference to the internal data structures
++ * of this driver. It is here solely at the moment to support the other
++ * CyberPro modules external to this driver.
++ */
++static struct cfb_info *int_cfb_info;
++
++/*
+ * Enable access to the extended registers
+ */
+-static void cyber2000fb_enable_extregs(struct cfb_info *cfb)
++void cyber2000fb_enable_extregs(struct cfb_info *cfb)
+ {
+ cfb->func_use_count += 1;
+
+ if (cfb->func_use_count == 1) {
+ int old;
+
+- old = cyber2000_grphr(FUNC_CTL, cfb);
+- cyber2000_grphw(FUNC_CTL, old | FUNC_CTL_EXTREGENBL, cfb);
++ old = cyber2000_grphr(EXT_FUNC_CTL, cfb);
++ old |= EXT_FUNC_CTL_EXTREGENBL;
++ cyber2000_grphw(EXT_FUNC_CTL, old, cfb);
+ }
+ }
+
+ /*
+ * Disable access to the extended registers
+ */
+-static void cyber2000fb_disable_extregs(struct cfb_info *cfb)
++void cyber2000fb_disable_extregs(struct cfb_info *cfb)
+ {
+ if (cfb->func_use_count == 1) {
+ int old;
+
+- old = cyber2000_grphr(FUNC_CTL, cfb);
+- cyber2000_grphw(FUNC_CTL, old & ~FUNC_CTL_EXTREGENBL, cfb);
++ old = cyber2000_grphr(EXT_FUNC_CTL, cfb);
++ old &= ~EXT_FUNC_CTL_EXTREGENBL;
++ cyber2000_grphw(EXT_FUNC_CTL, old, cfb);
+ }
+
++ if (cfb->func_use_count == 0)
++ printk(KERN_ERR "disable_extregs: count = 0\n");
++ else
+ cfb->func_use_count -= 1;
+ }
+
+-/*
+- * This is the only "static" reference to the internal data structures
+- * of this driver. It is here solely at the moment to support the other
+- * CyberPro modules external to this driver.
+- */
+-static struct cfb_info *int_cfb_info;
++void cyber2000fb_get_fb_var(struct cfb_info *cfb, struct fb_var_screeninfo *var)
++{
++ memcpy(var, &cfb->display->var, sizeof(struct fb_var_screeninfo));
++}
+
+ /*
+ * Attach a capture/tv driver to the core CyberX0X0 driver.
+@@ -1311,164 +1400,102 @@
+
+ EXPORT_SYMBOL(cyber2000fb_attach);
+ EXPORT_SYMBOL(cyber2000fb_detach);
++EXPORT_SYMBOL(cyber2000fb_enable_extregs);
++EXPORT_SYMBOL(cyber2000fb_disable_extregs);
++EXPORT_SYMBOL(cyber2000fb_get_fb_var);
+
+ /*
+ * These parameters give
+ * 640x480, hsync 31.5kHz, vsync 60Hz
+ */
+ static struct fb_videomode __devinitdata cyber2000fb_default_mode = {
+- refresh: 60,
+- xres: 640,
+- yres: 480,
+- pixclock: 39722,
+- left_margin: 56,
+- right_margin: 16,
+- upper_margin: 34,
+- lower_margin: 9,
+- hsync_len: 88,
+- vsync_len: 2,
+- sync: FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+- vmode: FB_VMODE_NONINTERLACED
++ .refresh = 60,
++ .xres = 640,
++ .yres = 480,
++ .pixclock = 39722,
++ .left_margin = 56,
++ .right_margin = 16,
++ .upper_margin = 34,
++ .lower_margin = 9,
++ .hsync_len = 88,
++ .vsync_len = 2,
++ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ .vmode = FB_VMODE_NONINTERLACED
+ };
+
++/* static register programming for all chips */
+ static char igs_regs[] __devinitdata = {
+- 0x12, 0x00, 0x13, 0x00,
+- 0x16, 0x00,
+- 0x31, 0x00, 0x32, 0x00,
+- 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+- 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x01,
+- 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00,
+- 0x70, 0x0b, 0x73, 0x30,
+- 0x74, 0x0b, 0x75, 0x17, 0x76, 0x00, 0x7a, 0xc8
++ EXT_CRT_IRQ, 0,
++ EXT_CRT_TEST, 0,
++ EXT_SYNC_CTL, 0,
++ EXT_SEG_WRITE_PTR, 0,
++ EXT_SEG_READ_PTR, 0,
++ EXT_BIU_MISC, EXT_BIU_MISC_LIN_ENABLE |
++ EXT_BIU_MISC_COP_ENABLE |
++ EXT_BIU_MISC_COP_BFC,
++ EXT_FUNC_CTL, 0,
++ CURS_H_START, 0,
++ CURS_H_START + 1, 0,
++ CURS_H_PRESET, 0,
++ CURS_V_START, 0,
++ CURS_V_START + 1, 0,
++ CURS_V_PRESET, 0,
++ CURS_CTL, 0,
++ EXT_ATTRIB_CTL, EXT_ATTRIB_CTL_EXT,
++ EXT_OVERSCAN_RED, 0,
++ EXT_OVERSCAN_GREEN, 0,
++ EXT_OVERSCAN_BLUE, 0,
++};
++
++/* specific register setting for the 2000 series */
++static char igs_2000_regs[] __devinitdata = {
++ /* some of these are questionable when we have a BIOS */
++ EXT_MEM_CTL0, EXT_MEM_CTL0_7CLK |
++ EXT_MEM_CTL0_RAS_1 |
++ EXT_MEM_CTL0_MULTCAS,
++ EXT_HIDDEN_CTL1, 0x30,
++ EXT_FIFO_CTL, 0x0b,
++ EXT_FIFO_CTL + 1, 0x17,
++ 0x76, 0x00,
++ EXT_HIDDEN_CTL4, 0xc8
+ };
+
+ /*
+- * We need to wake up the CyberPro, and make sure its in linear memory
+- * mode. Unfortunately, this is specific to the platform and card that
+- * we are running on.
+- *
+- * On x86 and ARM, should we be initialising the CyberPro first via the
+- * IO registers, and then the MMIO registers to catch all cases? Can we
+- * end up in the situation where the chip is in MMIO mode, but not awake
+- * on an x86 system?
+- *
+- * Note that on the NetWinder, the firmware automatically detects the
+- * type, width and size, and leaves this in extended registers 0x71 and
+- * 0x72 for us.
++ * Initialise the CyberPro hardware.
+ */
+-static inline void cyberpro_init_hw(struct cfb_info *cfb, int at_boot)
++static void cyberpro_init_hw(struct cfb_info *cfb)
+ {
+ int i;
+
+- /*
+- * Wake up the CyberPro.
+- */
+-#ifdef __sparc__
+-#ifdef __sparc_v9__
+-#error "You loose, consult DaveM."
+-#else
+- /*
+- * SPARC does not have an "outb" instruction, so we generate
+- * I/O cycles storing into a reserved memory space at
+- * physical address 0x3000000
+- */
+- {
+- unsigned char *iop;
+-
+- iop = ioremap(0x3000000, 0x5000);
+- if (iop == NULL) {
+- prom_printf("iga5000: cannot map I/O\n");
+- return -ENOMEM;
+- }
+-
+- writeb(0x18, iop + 0x46e8);
+- writeb(0x01, iop + 0x102);
+- writeb(0x08, iop + 0x46e8);
+- writeb(0x33, iop + 0x3ce);
+- writeb(0x01, iop + 0x3cf);
+-
+- iounmap((void *)iop);
+- }
+-#endif
+-
+- if (at_boot) {
+- /*
+- * Use mclk from BIOS. Only read this if we're
+- * initialising this card for the first time.
+- * FIXME: what about hotplug?
+- */
+- cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
+- cfb->mclk_div = cyber2000_grphr(MCLK_DIV, cfb);
+- }
+-#endif
+-#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+- /*
+- * x86 and MIPS are simple, we just do regular
+- * outb's instead of cyber2000fb_writeb.
+- */
+- outb(0x18, 0x46e8);
+- outb(0x01, 0x102);
+- outb(0x08, 0x46e8);
+- outb(0x33, 0x3ce);
+- outb(0x01, 0x3cf);
+-
+- if (at_boot) {
+- /*
+- * Use mclk from BIOS. Only read this if we're
+- * initialising this card for the first time.
+- * FIXME: what about hotplug?
+- */
+- cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
+- cfb->mclk_div = cyber2000_grphr(MCLK_DIV, cfb);
+- }
+-#endif
+-#ifdef __arm__
+- cyber2000fb_writeb(0x18, 0x46e8, cfb);
+- cyber2000fb_writeb(0x01, 0x102, cfb);
+- cyber2000fb_writeb(0x08, 0x46e8, cfb);
+- cyber2000fb_writeb(0x33, 0x3ce, cfb);
+- cyber2000fb_writeb(0x01, 0x3cf, cfb);
+-
+- /*
+- * MCLK on the NetWinder and the Shark is fixed at 75MHz
+- */
+- cfb->mclk_mult = 0xdb;
+- cfb->mclk_div = 0x54;
+-#endif
+-
+- /*
+- * Initialise the CyberPro
+- */
+ for (i = 0; i < sizeof(igs_regs); i += 2)
+ cyber2000_grphw(igs_regs[i], igs_regs[i+1], cfb);
+
+- if (at_boot) {
++ if (cfb->id == ID_CYBERPRO_5000) {
+ /*
+- * get the video RAM size and width from the VGA register.
+- * This should have been already initialised by the BIOS,
+- * but if it's garbage, claim default 1MB VRAM (woody)
++ * On the CyberPro5XXXX, ensure that we're using the correct
++ * PLL (5XXX's may be programmed to use an additional set of
++ * PLLs.)
+ */
+- cfb->mem_ctl1 = cyber2000_grphr(MEM_CTL1, cfb);
+- cfb->mem_ctl2 = cyber2000_grphr(MEM_CTL2, cfb);
++ unsigned char val;
++ cyber2000fb_writeb(0xba, 0x3ce, cfb);
++ val = cyber2000fb_readb(0x3cf, cfb) & 0x80;
++ cyber2000fb_writeb(val, 0x3cf, cfb);
++ cyber2000fb_ops.fb_pan_display = NULL; /* FIXME: panning broken */
+ } else {
+ /*
+- * Reprogram the MEM_CTL1 and MEM_CTL2 registers
++ * Other supported chips (2000 series) appear to need
++ * these registers programming
+ */
+- cyber2000_grphw(MEM_CTL1, cfb->mem_ctl1, cfb);
+- cyber2000_grphw(MEM_CTL2, cfb->mem_ctl2, cfb);
++ for (i = 0; i < sizeof(igs_2000_regs); i += 2)
++ cyber2000_grphw(igs_2000_regs[i],
++ igs_2000_regs[i+1],
++ cfb);
+ }
+
+- /*
+- * Ensure thatwe are using the correct PLL.
+- * (CyberPro 5000's may be programmed to use
+- * an additional set of PLLs.
+- */
+- cyber2000fb_writeb(0xba, 0x3ce, cfb);
+- cyber2000fb_writeb(cyber2000fb_readb(0x3cf, cfb) & 0x80, 0x3cf, cfb);
+ }
+
+ static struct cfb_info * __devinit
+-cyberpro_alloc_fb_info(struct pci_dev *dev, const struct pci_device_id *id, char *name)
++cyberpro_alloc_fb_info(unsigned int id, char *name)
+ {
+ struct cfb_info *cfb;
+
+@@ -1480,10 +1507,9 @@
+
+ memset(cfb, 0, sizeof(struct cfb_info) + sizeof(struct display));
+
+- cfb->currcon = -1;
+- cfb->dev = dev;
++ cfb->id = id;
+
+- if (id->driver_data == FB_ACCEL_IGS_CYBER5000)
++ if (id == ID_CYBERPRO_5000)
+ cfb->ref_ps = 40690; // 24.576 MHz
+ else
+ cfb->ref_ps = 69842; // 14.31818 MHz (69841?)
+@@ -1492,7 +1518,7 @@
+ cfb->divisors[1] = 2;
+ cfb->divisors[2] = 4;
+
+- if (id->driver_data == FB_ACCEL_IGS_CYBER2000)
++ if (id == ID_CYBERPRO_2000)
+ cfb->divisors[3] = 8;
+ else
+ cfb->divisors[3] = 6;
+@@ -1504,7 +1530,24 @@
+ cfb->fb.fix.xpanstep = 0;
+ cfb->fb.fix.ypanstep = 1;
+ cfb->fb.fix.ywrapstep = 0;
+- cfb->fb.fix.accel = id->driver_data;
++
++ switch (id) {
++ case ID_IGA_1682:
++ cfb->fb.fix.accel = 0;
++ break;
++
++ case ID_CYBERPRO_2000:
++ cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER2000;
++ break;
++
++ case ID_CYBERPRO_2010:
++ cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER2010;
++ break;
++
++ case ID_CYBERPRO_5000:
++ cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER5000;
++ break;
++ }
+
+ cfb->fb.var.nonstd = 0;
+ cfb->fb.var.activate = FB_ACTIVATE_NOW;
+@@ -1569,55 +1612,46 @@
+ return 0;
+ }
+
+-static int __devinit
+-cyberpro_probe(struct pci_dev *dev, const struct pci_device_id *id)
++/*
++ * The CyberPro chips can be placed on many different bus types.
++ * This probe function is common to all bus types. The bus-specific
++ * probe function is expected to have:
++ * - enabled access to the linear memory region
++ * - memory mapped access to the registers
++ * - initialised mem_ctl1 and mem_ctl2 appropriately.
++ */
++static int __devinit cyberpro_common_probe(struct cfb_info *cfb)
+ {
+- struct cfb_info *cfb;
+- u_int h_sync, v_sync;
+ u_long smem_size;
+- char name[16];
++ u_int h_sync, v_sync;
+ int err;
+
+- sprintf(name, "CyberPro%4X", id->device);
+-
+- err = pci_enable_device(dev);
+- if (err)
+- return err;
+-
+- err = pci_request_regions(dev, name);
+- if (err)
+- return err;
+-
+- err = -ENOMEM;
+- cfb = cyberpro_alloc_fb_info(dev, id, name);
+- if (!cfb)
+- goto failed_release;
+-
+- cfb->region = ioremap(pci_resource_start(dev, 0),
+- pci_resource_len(dev, 0));
+- if (!cfb->region)
+- goto failed_ioremap;
+-
+- cfb->regs = cfb->region + MMIO_OFFSET;
++ cyberpro_init_hw(cfb);
+
+- cyberpro_init_hw(cfb, 1);
++ /*
++ * Get the video RAM size and width from the VGA register.
++ * This should have been already initialised by the BIOS,
++ * but if it's garbage, claim default 1MB VRAM (woody)
++ */
++ cfb->mem_ctl0 = cyber2000_grphr(EXT_MEM_CTL0, cfb);
++ cfb->mem_ctl1 = cyber2000_grphr(EXT_MEM_CTL1, cfb);
++ cfb->mem_ctl2 = cyber2000_grphr(EXT_MEM_CTL2, cfb);
+
++ /*
++ * Determine the size of the memory.
++ */
+ switch (cfb->mem_ctl2 & MEM_CTL2_SIZE_MASK) {
+ case MEM_CTL2_SIZE_4MB: smem_size = 0x00400000; break;
+ case MEM_CTL2_SIZE_2MB: smem_size = 0x00200000; break;
++ case MEM_CTL2_SIZE_1MB: smem_size = 0x00100000; break;
+ default: smem_size = 0x00100000; break;
+ }
+
+- /*
+- * Hmm, we _need_ a portable way of finding the address for
+- * the remap stuff, both for mmio and for smem.
+- */
+- cfb->fb.fix.mmio_start = pci_resource_start(dev, 0) + MMIO_OFFSET;
+- cfb->fb.fix.smem_start = pci_resource_start(dev, 0);
+- cfb->fb.fix.mmio_len = MMIO_SIZE;
+ cfb->fb.fix.smem_len = smem_size;
++ cfb->fb.fix.mmio_len = MMIO_SIZE;
+ cfb->fb.screen_base = cfb->region;
+
++ err = -EINVAL;
+ if (!fb_find_mode(&cfb->fb.var, &cfb->fb, NULL, NULL, 0,
+ &cyber2000fb_default_mode, 8)) {
+ printk("%s: no valid mode found\n", cfb->fb.fix.id);
+@@ -1644,13 +1678,181 @@
+ v_sync = h_sync / (cfb->fb.var.yres + cfb->fb.var.upper_margin +
+ cfb->fb.var.lower_margin + cfb->fb.var.vsync_len);
+
+- printk(KERN_INFO "%s: %dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
++ printk(KERN_INFO "%s: %dKiB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+ cfb->fb.fix.id, cfb->fb.fix.smem_len >> 10,
+ cfb->fb.var.xres, cfb->fb.var.yres,
+ h_sync / 1000, h_sync % 1000, v_sync);
+
+ err = register_framebuffer(&cfb->fb);
+- if (err < 0)
++
++failed:
++ return err;
++}
++
++static void cyberpro_common_resume(struct cfb_info *cfb)
++{
++ cyberpro_init_hw(cfb);
++
++ /*
++ * Reprogram the MEM_CTL0, 1 and 2 registers
++ */
++ cyber2000_grphw(EXT_MEM_CTL0, cfb->mem_ctl0, cfb);
++ cyber2000_grphw(EXT_MEM_CTL1, cfb->mem_ctl1, cfb);
++ cyber2000_grphw(EXT_MEM_CTL2, cfb->mem_ctl2, cfb);
++
++ /*
++ * Restore the old video mode and the palette.
++ * We also need to tell fbcon to redraw the console.
++ */
++ cfb->fb.var.activate = FB_ACTIVATE_NOW;
++ cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb);
++}
++
++
++
++
++/*
++ * PCI specific support.
++ */
++
++/*
++ * We need to wake up the CyberPro, and make sure its in linear memory
++ * mode. Unfortunately, this is specific to the platform and card that
++ * we are running on.
++ *
++ * On x86 and ARM, should we be initialising the CyberPro first via the
++ * IO registers, and then the MMIO registers to catch all cases? Can we
++ * end up in the situation where the chip is in MMIO mode, but not awake
++ * on an x86 system?
++ */
++static int cyberpro_pci_enable_mmio(struct cfb_info *cfb)
++{
++ unsigned char val;
++
++#if defined(__sparc_v9__)
++#error "You loose, consult DaveM."
++#elif defined(__sparc__)
++ /*
++ * SPARC does not have an "outb" instruction, so we generate
++ * I/O cycles storing into a reserved memory space at
++ * physical address 0x3000000
++ */
++ unsigned char *iop;
++
++ iop = ioremap(0x3000000, 0x5000);
++ if (iop == NULL) {
++ prom_printf("iga5000: cannot map I/O\n");
++ return -ENOMEM;
++ }
++
++ writeb(0x18, iop + 0x46e8);
++ writeb(0x01, iop + 0x102);
++ writeb(0x08, iop + 0x46e8);
++ writeb(EXT_BIU_MISC, iop + 0x3ce);
++ writeb(EXT_BIU_MISC_LIN_ENABLE, iop + 0x3cf);
++
++ iounmap((void *)iop);
++#elif defined(CONFIG_ARCH_SHARK)
++ /*
++ * Shark probably needs to do it this way rather than use the
++ * IO method below. Since the CyberPro on the Shark isn't a
++ * PCI device, we probably want to move this to a bus-specific
++ * probe function. Do we even need to do this?
++ */
++ cyber2000fb_writeb(0x18, 0x46e8, cfb);
++ cyber2000fb_writeb(0x01, 0x102, cfb);
++ cyber2000fb_writeb(0x08, 0x46e8, cfb);
++ cyber2000fb_writeb(EXT_BIU_MISC, 0x3ce, cfb);
++ cyber2000fb_writeb(EXT_BIU_MISC_LIN_ENABLE, 0x3cf, cfb);
++#else
++ /*
++ * Most other machine types are "normal", so
++ * we use the standard IO-based wakeup.
++ */
++ outb(0x18, 0x46e8);
++ outb(0x01, 0x102);
++ outb(0x08, 0x46e8);
++ outb(EXT_BIU_MISC, 0x3ce);
++ outb(EXT_BIU_MISC_LIN_ENABLE, 0x3cf);
++#endif
++
++ /*
++ * Allow the CyberPro to accept PCI burst accesses
++ */
++ val = cyber2000_grphr(EXT_BUS_CTL, cfb);
++ if (!(val & EXT_BUS_CTL_PCIBURST_WRITE)) {
++ printk(KERN_INFO "%s: enabling PCI bursts\n", cfb->fb.fix.id);
++
++ val |= EXT_BUS_CTL_PCIBURST_WRITE;
++
++ if (cfb->id == ID_CYBERPRO_5000)
++ val |= EXT_BUS_CTL_PCIBURST_READ;
++
++ cyber2000_grphw(EXT_BUS_CTL, val, cfb);
++ }
++
++ return 0;
++}
++
++static int __devinit
++cyberpro_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
++{
++ struct cfb_info *cfb;
++ char name[16];
++ int err;
++
++ sprintf(name, "CyberPro%4X", id->device);
++
++ err = pci_enable_device(dev);
++ if (err)
++ return err;
++
++ err = pci_request_regions(dev, name);
++ if (err)
++ return err;
++
++ err = -ENOMEM;
++ cfb = cyberpro_alloc_fb_info(id->driver_data, name);
++ if (!cfb)
++ goto failed_release;
++
++ cfb->dev = dev;
++ cfb->region = ioremap(pci_resource_start(dev, 0),
++ pci_resource_len(dev, 0));
++ if (!cfb->region)
++ goto failed_ioremap;
++
++ cfb->regs = cfb->region + MMIO_OFFSET;
++ cfb->fb.fix.mmio_start = pci_resource_start(dev, 0) + MMIO_OFFSET;
++ cfb->fb.fix.smem_start = pci_resource_start(dev, 0);
++
++ /*
++ * Bring up the hardware. This is expected to enable access
++ * to the linear memory region, and allow access to the memory
++ * mapped registers. Also, mem_ctl1 and mem_ctl2 must be
++ * initialised.
++ */
++ err = cyberpro_pci_enable_mmio(cfb);
++ if (err)
++ goto failed;
++
++ /*
++ * Use MCLK from BIOS. FIXME: what about hotplug?
++ */
++ cfb->mclk_mult = cyber2000_grphr(EXT_MCLK_MULT, cfb);
++ cfb->mclk_div = cyber2000_grphr(EXT_MCLK_DIV, cfb);
++#ifdef __arm__
++ if (machine_is_netwinder() || machine_is_shark()) {
++ /*
++ * MCLK on the NetWinder and the Shark is fixed at 75MHz
++ */
++ cfb->mclk_mult = 0xdb;
++ cfb->mclk_div = 0x54;
++ }
++#endif
++
++ err = cyberpro_common_probe(cfb);
++ if (err)
+ goto failed;
+
+ /*
+@@ -1672,7 +1874,7 @@
+ return err;
+ }
+
+-static void __devexit cyberpro_remove(struct pci_dev *dev)
++static void __devexit cyberpro_pci_remove(struct pci_dev *dev)
+ {
+ struct cfb_info *cfb = pci_get_drvdata(dev);
+
+@@ -1701,7 +1903,7 @@
+ }
+ }
+
+-static int cyberpro_suspend(struct pci_dev *dev, u32 state)
++static int cyberpro_pci_suspend(struct pci_dev *dev, u32 state)
+ {
+ return 0;
+ }
+@@ -1709,41 +1911,44 @@
+ /*
+ * Re-initialise the CyberPro hardware
+ */
+-static int cyberpro_resume(struct pci_dev *dev)
++static int cyberpro_pci_resume(struct pci_dev *dev)
+ {
+ struct cfb_info *cfb = pci_get_drvdata(dev);
+
+ if (cfb) {
+- cyberpro_init_hw(cfb, 0);
+-
+- /*
+- * Restore the old video mode and the palette.
+- * We also need to tell fbcon to redraw the console.
+- */
+- cfb->fb.var.activate = FB_ACTIVATE_NOW;
+- cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb);
++ cyberpro_pci_enable_mmio(cfb);
++ cyberpro_common_resume(cfb);
+ }
+
+ return 0;
+ }
+
+ static struct pci_device_id cyberpro_pci_table[] __devinitdata = {
++// Not yet
++// { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_1682,
++// PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_IGA_1682 },
+ { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2000,
+- PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER2000 },
++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_2000 },
+ { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2010,
+- PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER2010 },
++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_2010 },
+ { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5000,
+- PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER5000 },
++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_5000 },
+ { 0, }
+ };
+
++MODULE_DEVICE_TABLE(pci,cyberpro_pci_table);
++
++#ifndef __devexit_p
++#define __devexit_p(x) (x)
++#endif
++
+ static struct pci_driver cyberpro_driver = {
+- name: "CyberPro",
+- probe: cyberpro_probe,
+- remove: __devexit_p(cyberpro_remove),
+- suspend: cyberpro_suspend,
+- resume: cyberpro_resume,
+- id_table: cyberpro_pci_table
++ .name = "CyberPro",
++ .probe = cyberpro_pci_probe,
++ .remove = __devexit_p(cyberpro_pci_remove),
++ .suspend = cyberpro_pci_suspend,
++ .resume = cyberpro_pci_resume,
++ .id_table = cyberpro_pci_table
+ };
+
+ /*
+@@ -1768,5 +1973,4 @@
+
+ MODULE_AUTHOR("Russell King");
+ MODULE_DESCRIPTION("CyberPro 2000, 2010 and 5000 framebuffer driver");
+-MODULE_DEVICE_TABLE(pci,cyberpro_pci_table);
+ MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/video/cyber2000fb.h kernel-source-2.4.27-8-arm-1/drivers/video/cyber2000fb.h
+--- kernel-source-2.4.27-8/drivers/video/cyber2000fb.h 2002-08-03 01:39:45.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/cyber2000fb.h 2005-02-18 17:48:44.000000000 +0000
+@@ -36,24 +36,55 @@
+ #define debug_printf(x...) do { } while (0)
+ #endif
+
+-#define PIXFORMAT_8BPP 0
+-#define PIXFORMAT_16BPP 1
+-#define PIXFORMAT_24BPP 2
+-#define PIXFORMAT_32BPP 3
+-
+-#define VISUALID_256 1
+-#define VISUALID_64K 2
+-#define VISUALID_16M_32 3
+-#define VISUALID_16M 4
+-#define VISUALID_32K 6
+-
+-#define FUNC_CTL 0x3c
+-#define FUNC_CTL_EXTREGENBL 0x80 /* enable access to 0xbcxxx */
+-
+-#define BIU_BM_CONTROL 0x3e
+-#define BIU_BM_CONTROL_ENABLE 0x01 /* enable bus-master */
+-#define BIU_BM_CONTROL_BURST 0x02 /* enable burst */
+-#define BIU_BM_CONTROL_BACK2BACK 0x04 /* enable back to back */
++#define RAMDAC_RAMPWRDN 0x01
++#define RAMDAC_DAC8BIT 0x02
++#define RAMDAC_VREFEN 0x04
++#define RAMDAC_BYPASS 0x10
++#define RAMDAC_DACPWRDN 0x40
++
++#define EXT_CRT_VRTOFL 0x11
++#define EXT_CRT_VRTOFL_LINECOMP10 0x10
++#define EXT_CRT_VRTOFL_INTERLACE 0x20
++
++#define EXT_CRT_IRQ 0x12
++#define EXT_CRT_IRQ_ENABLE 0x01
++#define EXT_CRT_IRQ_ACT_HIGH 0x04
++
++#define EXT_CRT_TEST 0x13
++
++#define EXT_SYNC_CTL 0x16
++#define EXT_SYNC_CTL_HS_NORMAL 0x00
++#define EXT_SYNC_CTL_HS_0 0x01
++#define EXT_SYNC_CTL_HS_1 0x02
++#define EXT_SYNC_CTL_HS_HSVS 0x03
++#define EXT_SYNC_CTL_VS_NORMAL 0x00
++#define EXT_SYNC_CTL_VS_0 0x04
++#define EXT_SYNC_CTL_VS_1 0x08
++#define EXT_SYNC_CTL_VS_COMP 0x0c
++
++#define EXT_BUS_CTL 0x30
++#define EXT_BUS_CTL_LIN_1MB 0x00
++#define EXT_BUS_CTL_LIN_2MB 0x01
++#define EXT_BUS_CTL_LIN_4MB 0x02
++#define EXT_BUS_CTL_ZEROWAIT 0x04
++#define EXT_BUS_CTL_PCIBURST_WRITE 0x20
++#define EXT_BUS_CTL_PCIBURST_READ 0x80 /* CyberPro 5000 only */
++
++#define EXT_SEG_WRITE_PTR 0x31
++#define EXT_SEG_READ_PTR 0x32
++#define EXT_BIU_MISC 0x33
++#define EXT_BIU_MISC_LIN_ENABLE 0x01
++#define EXT_BIU_MISC_COP_ENABLE 0x04
++#define EXT_BIU_MISC_COP_BFC 0x08
++
++#define EXT_FUNC_CTL 0x3c
++#define EXT_FUNC_CTL_EXTREGENBL 0x80 /* enable access to 0xbcxxx */
++
++#define PCI_BM_CTL 0x3e
++#define PCI_BM_CTL_ENABLE 0x01 /* enable bus-master */
++#define PCI_BM_CTL_BURST 0x02 /* enable burst */
++#define PCI_BM_CTL_BACK2BACK 0x04 /* enable back to back */
++#define PCI_BM_CTL_DUMMY 0x08 /* insert dummy cycle */
+
+ #define X_V2_VID_MEM_START 0x40
+ #define X_V2_VID_SRC_WIDTH 0x43
+@@ -87,6 +118,19 @@
+
+ #define K_CAP_X2_CTL1 0x49
+
++#define CURS_H_START 0x50
++#define CURS_H_PRESET 0x52
++#define CURS_V_START 0x53
++#define CURS_V_PRESET 0x55
++#define CURS_CTL 0x56
++
++#define EXT_ATTRIB_CTL 0x57
++#define EXT_ATTRIB_CTL_EXT 0x01
++
++#define EXT_OVERSCAN_RED 0x58
++#define EXT_OVERSCAN_GREEN 0x59
++#define EXT_OVERSCAN_BLUE 0x5a
++
+ #define CAP_X_START 0x60
+ #define CAP_X_END 0x62
+ #define CAP_Y_START 0x64
+@@ -96,46 +140,112 @@
+ #define CAP_DDA_Y_INIT 0x6c
+ #define CAP_DDA_Y_INC 0x6e
+
+-#define MEM_CTL1 0x71
++#define EXT_MEM_CTL0 0x70
++#define EXT_MEM_CTL0_7CLK 0x01
++#define EXT_MEM_CTL0_RAS_1 0x02
++#define EXT_MEM_CTL0_RAS2CAS_1 0x04
++#define EXT_MEM_CTL0_MULTCAS 0x08
++#define EXT_MEM_CTL0_ASYM 0x10
++#define EXT_MEM_CTL0_CAS1ON 0x20
++#define EXT_MEM_CTL0_FIFOFLUSH 0x40
++#define EXT_MEM_CTL0_SEQRESET 0x80
++
++#define EXT_MEM_CTL1 0x71
++#define EXT_MEM_CTL1_PAR 0x00
++#define EXT_MEM_CTL1_SERPAR 0x01
++#define EXT_MEM_CTL1_SER 0x03
++#define EXT_MEM_CTL1_SYNC 0x04
++#define EXT_MEM_CTL1_VRAM 0x08
++#define EXT_MEM_CTL1_4K_REFRESH 0x10
++#define EXT_MEM_CTL1_256Kx4 0x00
++#define EXT_MEM_CTL1_512Kx8 0x40
++#define EXT_MEM_CTL1_1Mx16 0x60
+
+-#define MEM_CTL2 0x72
++#define EXT_MEM_CTL2 0x72
++#define MEM_CTL2_SIZE_1MB 0x00
+ #define MEM_CTL2_SIZE_2MB 0x01
+ #define MEM_CTL2_SIZE_4MB 0x02
+ #define MEM_CTL2_SIZE_MASK 0x03
+ #define MEM_CTL2_64BIT 0x04
+
++#define EXT_HIDDEN_CTL1 0x73
++
+ #define EXT_FIFO_CTL 0x74
+
++#define EXT_SEQ_MISC 0x77
++#define EXT_SEQ_MISC_8 0x01
++#define EXT_SEQ_MISC_16_RGB565 0x02
++#define EXT_SEQ_MISC_32 0x03
++#define EXT_SEQ_MISC_24_RGB888 0x04
++#define EXT_SEQ_MISC_16_RGB555 0x06
++#define EXT_SEQ_MISC_8_RGB332 0x09
++#define EXT_SEQ_MISC_16_RGB444 0x0a
++
++#define EXT_HIDDEN_CTL4 0x7a
++
++#define CURS_MEM_START 0x7e /* bits 23..12 */
++
+ #define CAP_PIP_X_START 0x80
+ #define CAP_PIP_X_END 0x82
+ #define CAP_PIP_Y_START 0x84
+ #define CAP_PIP_Y_END 0x86
+
+-#define CAP_NEW_CTL1 0x88
++#define EXT_CAP_CTL1 0x88
+
+-#define CAP_NEW_CTL2 0x89
++#define EXT_CAP_CTL2 0x89
++#define EXT_CAP_CTL2_ODDFRAMEIRQ 0x01
++#define EXT_CAP_CTL2_ANYFRAMEIRQ 0x02
+
+ #define BM_CTRL0 0x9c
+ #define BM_CTRL1 0x9d
+
+-#define CAP_MODE1 0xa4
+-#define CAP_MODE1_8BIT 0x01 /* enable 8bit capture mode */
+-#define CAP_MODE1_CCIR656 0x02 /* CCIR656 mode */
+-#define CAP_MODE1_IGNOREVGT 0x04 /* ignore VGT */
+-#define CAP_MODE1_ALTFIFO 0x10 /* use alternate FIFO for capture */
+-#define CAP_MODE1_SWAPUV 0x20 /* swap UV bytes */
+-#define CAP_MODE1_MIRRORY 0x40 /* mirror vertically */
+-#define CAP_MODE1_MIRRORX 0x80 /* mirror horizontally */
+-
+-#define DCLK_MULT 0xb0
+-#define DCLK_DIV 0xb1
+-#define DCLK_DIV_VFSEL 0x20
+-#define MCLK_MULT 0xb2
+-#define MCLK_DIV 0xb3
+-
+-#define CAP_MODE2 0xa5
+-
+-#define Y_TV_CTL 0xae
++#define EXT_CAP_MODE1 0xa4
++#define EXT_CAP_MODE1_8BIT 0x01 /* enable 8bit capture mode */
++#define EXT_CAP_MODE1_CCIR656 0x02 /* CCIR656 mode */
++#define EXT_CAP_MODE1_IGNOREVGT 0x04 /* ignore VGT */
++#define EXT_CAP_MODE1_ALTFIFO 0x10 /* use alternate FIFO for capture */
++#define EXT_CAP_MODE1_SWAPUV 0x20 /* swap UV bytes */
++#define EXT_CAP_MODE1_MIRRORY 0x40 /* mirror vertically */
++#define EXT_CAP_MODE1_MIRRORX 0x80 /* mirror horizontally */
++
++#define EXT_CAP_MODE2 0xa5
++#define EXT_CAP_MODE2_CCIRINVOE 0x01
++#define EXT_CAP_MODE2_CCIRINVVGT 0x02
++#define EXT_CAP_MODE2_CCIRINVHGT 0x04
++#define EXT_CAP_MODE2_CCIRINVDG 0x08
++#define EXT_CAP_MODE2_DATEND 0x10
++#define EXT_CAP_MODE2_CCIRDGH 0x20
++#define EXT_CAP_MODE2_FIXSONY 0x40
++#define EXT_CAP_MODE2_SYNCFREEZE 0x80
++
++#define EXT_TV_CTL 0xae
++
++#define EXT_DCLK_MULT 0xb0
++#define EXT_DCLK_DIV 0xb1
++#define EXT_DCLK_DIV_VFSEL 0x20
++#define EXT_MCLK_MULT 0xb2
++#define EXT_MCLK_DIV 0xb3
++
++#define EXT_LATCH1 0xb5
++#define EXT_LATCH1_VAFC_EN 0x01 /* enable VAFC */
++
++#define EXT_FEATURE 0xb7
++#define EXT_FEATURE_BUS_MASK 0x07 /* host bus mask */
++#define EXT_FEATURE_BUS_PCI 0x00
++#define EXT_FEATURE_BUS_VL_STD 0x04
++#define EXT_FEATURE_BUS_VL_LINEAR 0x05
++#define EXT_FEATURE_1682 0x20 /* IGS 1682 compatibility */
++
++#define EXT_LATCH2 0xb6
++#define EXT_LATCH2_I2C_CLKEN 0x10
++#define EXT_LATCH2_I2C_CLK 0x20
++#define EXT_LATCH2_I2C_DATEN 0x40
++#define EXT_LATCH2_I2C_DAT 0x80
++
++#define EXT_XT_CTL 0xbe
++#define EXT_XT_CAP16 0x04
++#define EXT_XT_LINEARFB 0x08
++#define EXT_XT_PAL 0x10
+
+ #define EXT_MEM_START 0xc0 /* ext start address 21 bits */
+ #define HOR_PHASE_SHIFT 0xc2 /* high 3 bits */
+@@ -160,25 +270,37 @@
+ #define EXT_VID_FMT_RGB565 0x02
+ #define EXT_VID_FMT_RGB888_24 0x03
+ #define EXT_VID_FMT_RGB888_32 0x04
++#define EXT_VID_FMT_RGB8 0x05
++#define EXT_VID_FMT_RGB4444 0x06
++#define EXT_VID_FMT_RGB8T 0x07
+ #define EXT_VID_FMT_DUP_PIX_ZOON 0x08 /* duplicate pixel zoom */
+ #define EXT_VID_FMT_MOD_3RD_PIX 0x20 /* modify 3rd duplicated pixel */
+ #define EXT_VID_FMT_DBL_H_PIX 0x40 /* double horiz pixels */
+-#define EXT_VID_FMT_UV128 0x80 /* UV data offset by 128 */
++#define EXT_VID_FMT_YUV128 0x80 /* YUV data offset by 128 */
+
+ #define EXT_VID_DISP_CTL1 0xdc
+ #define EXT_VID_DISP_CTL1_INTRAM 0x01 /* video pixels go to internal RAM */
+ #define EXT_VID_DISP_CTL1_IGNORE_CCOMP 0x02 /* ignore colour compare registers */
+ #define EXT_VID_DISP_CTL1_NOCLIP 0x04 /* do not clip to 16235,16240 */
+ #define EXT_VID_DISP_CTL1_UV_AVG 0x08 /* U/V data is averaged */
+-#define EXT_VID_DISP_CTL1_Y128 0x10 /* Y data offset by 128 */
+-#define EXT_VID_DISP_CTL1_VINTERPOL_OFF 0x20 /* vertical interpolation off */
++#define EXT_VID_DISP_CTL1_Y128 0x10 /* Y data offset by 128 (if YUV128 set) */
++#define EXT_VID_DISP_CTL1_VINTERPOL_OFF 0x20 /* disable vertical interpolation */
+ #define EXT_VID_DISP_CTL1_FULL_WIN 0x40 /* video out window full */
+ #define EXT_VID_DISP_CTL1_ENABLE_WINDOW 0x80 /* enable video window */
+
+ #define EXT_VID_FIFO_CTL1 0xdd
++#define EXT_VID_FIFO_CTL1_OE_HIGH 0x02
++#define EXT_VID_FIFO_CTL1_INTERLEAVE 0x04 /* enable interleaved memory read */
++
++#define EXT_ROM_UCB4GH 0xe5
++#define EXT_ROM_UCB4GH_FREEZE 0x02 /* capture frozen */
++#define EXT_ROM_UCB4GH_ODDFRAME 0x04 /* 1 = odd frame captured */
++#define EXT_ROM_UCB4GH_1HL 0x08 /* first horizonal line after VGT falling edge */
++#define EXT_ROM_UCB4GH_ODD 0x10 /* odd frame indicator */
++#define EXT_ROM_UCB4GH_INTSTAT 0x20 /* video interrupt */
+
+ #define VFAC_CTL1 0xe8
+-#define VFAC_CTL1_CAPTURE 0x01 /* capture enable */
++#define VFAC_CTL1_CAPTURE 0x01 /* capture enable (only when VSYNC high)*/
+ #define VFAC_CTL1_VFAC_ENABLE 0x02 /* vfac enable */
+ #define VFAC_CTL1_FREEZE_CAPTURE 0x04 /* freeze capture */
+ #define VFAC_CTL1_FREEZE_CAPTURE_SYNC 0x08 /* sync freeze capture */
+@@ -197,6 +319,13 @@
+ #define VFAC_CTL2_INVERT_OVSYNC 0x80 /* invert other vsync input */
+
+ #define VFAC_CTL3 0xea
++#define VFAC_CTL3_CAP_LARGE_FIFO 0x01 /* large capture fifo */
++#define VFAC_CTL3_CAP_INTERLACE 0x02 /* capture odd and even fields */
++#define VFAC_CTL3_CAP_HOLD_4NS 0x00 /* hold capture data for 4ns */
++#define VFAC_CTL3_CAP_HOLD_2NS 0x04 /* hold capture data for 2ns */
++#define VFAC_CTL3_CAP_HOLD_6NS 0x08 /* hold capture data for 6ns */
++#define VFAC_CTL3_CAP_HOLD_0NS 0x0c /* hold capture data for 0ns */
++#define VFAC_CTL3_CHROMAKEY 0x20 /* capture data will be chromakeyed */
+ #define VFAC_CTL3_CAP_IRQ 0x40 /* enable capture interrupt */
+
+ #define CAP_MEM_START 0xeb /* 18 bits */
+@@ -235,26 +364,98 @@
+ #define BM_COUNT 0xbc090 /* read-only */
+
+ /*
++ * TV registers
++ */
++#define TV_VBLANK_EVEN_START 0xbe43c
++#define TV_VBLANK_EVEN_END 0xbe440
++#define TV_VBLANK_ODD_START 0xbe444
++#define TV_VBLANK_ODD_END 0xbe448
++#define TV_SYNC_YGAIN 0xbe44c
++#define TV_UV_GAIN 0xbe450
++#define TV_PED_UVDET 0xbe454
++#define TV_UV_BURST_AMP 0xbe458
++#define TV_HSYNC_START 0xbe45c
++#define TV_HSYNC_END 0xbe460
++#define TV_Y_DELAY1 0xbe464
++#define TV_Y_DELAY2 0xbe468
++#define TV_UV_DELAY1 0xbe46c
++#define TV_BURST_START 0xbe470
++#define TV_BURST_END 0xbe474
++#define TV_HBLANK_START 0xbe478
++#define TV_HBLANK_END 0xbe47c
++#define TV_PED_EVEN_START 0xbe480
++#define TV_PED_EVEN_END 0xbe484
++#define TV_PED_ODD_START 0xbe488
++#define TV_PED_ODD_END 0xbe48c
++#define TV_VSYNC_EVEN_START 0xbe490
++#define TV_VSYNC_EVEN_END 0xbe494
++#define TV_VSYNC_ODD_START 0xbe498
++#define TV_VSYNC_ODD_END 0xbe49c
++#define TV_SCFL 0xbe4a0
++#define TV_SCFH 0xbe4a4
++#define TV_SCP 0xbe4a8
++#define TV_DELAYBYPASS 0xbe4b4
++#define TV_EQL_END 0xbe4bc
++#define TV_SERR_START 0xbe4c0
++#define TV_SERR_END 0xbe4c4
++#define TV_CTL 0xbe4dc /* reflects a previous register- MVFCLR, MVPCLR etc P241*/
++#define TV_VSYNC_VGA_HS 0xbe4e8
++#define TV_FLICK_XMIN 0xbe514
++#define TV_FLICK_XMAX 0xbe518
++#define TV_FLICK_YMIN 0xbe51c
++#define TV_FLICK_YMAX 0xbe520
++
++/*
+ * Graphics Co-processor
+ */
++#define CO_REG_CONTROL 0xbf011
++#define CO_CTRL_BUSY 0x80
++#define CO_CTRL_CMDFULL 0x04
++#define CO_CTRL_FIFOEMPTY 0x02
++#define CO_CTRL_READY 0x01
++
++#define CO_REG_SRC_WIDTH 0xbf018
++#define CO_REG_PIXFMT 0xbf01c
++#define CO_PIXFMT_32BPP 0x03
++#define CO_PIXFMT_24BPP 0x02
++#define CO_PIXFMT_16BPP 0x01
++#define CO_PIXFMT_8BPP 0x00
++
++#define CO_REG_FGMIX 0xbf048
++#define CO_FG_MIX_ZERO 0x00
++#define CO_FG_MIX_SRC_AND_DST 0x01
++#define CO_FG_MIX_SRC_AND_NDST 0x02
++#define CO_FG_MIX_SRC 0x03
++#define CO_FG_MIX_NSRC_AND_DST 0x04
++#define CO_FG_MIX_DST 0x05
++#define CO_FG_MIX_SRC_XOR_DST 0x06
++#define CO_FG_MIX_SRC_OR_DST 0x07
++#define CO_FG_MIX_NSRC_AND_NDST 0x08
++#define CO_FG_MIX_SRC_XOR_NDST 0x09
++#define CO_FG_MIX_NDST 0x0a
++#define CO_FG_MIX_SRC_OR_NDST 0x0b
++#define CO_FG_MIX_NSRC 0x0c
++#define CO_FG_MIX_NSRC_OR_DST 0x0d
++#define CO_FG_MIX_NSRC_OR_NDST 0x0e
++#define CO_FG_MIX_ONES 0x0f
++
++#define CO_REG_FGCOLOUR 0xbf058
++#define CO_REG_BGCOLOUR 0xbf05c
++#define CO_REG_PIXWIDTH 0xbf060
++#define CO_REG_PIXHEIGHT 0xbf062
++#define CO_REG_X_PHASE 0xbf078
++#define CO_REG_CMD_L 0xbf07c
+ #define CO_CMD_L_PATTERN_FGCOL 0x8000
+ #define CO_CMD_L_INC_LEFT 0x0004
+ #define CO_CMD_L_INC_UP 0x0002
+
+-#define CO_CMD_H_SRC_PIXMAP 0x2000
++#define CO_REG_CMD_H 0xbf07e
++#define CO_CMD_H_BGSRCMAP 0x8000 /* otherwise bg colour */
++#define CO_CMD_H_FGSRCMAP 0x2000 /* otherwise fg colour */
+ #define CO_CMD_H_BLITTER 0x0800
+
+-#define CO_REG_CONTROL 0xbf011
+-#define CO_REG_SRC_WIDTH 0xbf018
+-#define CO_REG_PIX_FORMAT 0xbf01c
+-#define CO_REG_FORE_MIX 0xbf048
+-#define CO_REG_FOREGROUND 0xbf058
+-#define CO_REG_WIDTH 0xbf060
+-#define CO_REG_HEIGHT 0xbf062
+-#define CO_REG_X_PHASE 0xbf078
+-#define CO_REG_CMD_L 0xbf07c
+-#define CO_REG_CMD_H 0xbf07e
+-#define CO_REG_SRC_PTR 0xbf170
++#define CO_REG_SRC1_PTR 0xbf170
++#define CO_REG_SRC2_PTR 0xbf174
+ #define CO_REG_DEST_PTR 0xbf178
+ #define CO_REG_DEST_WIDTH 0xbf218
+
+@@ -269,6 +470,7 @@
+ char *fb;
+ char dev_name[32];
+ unsigned int fb_size;
++ unsigned int chip_id;
+
+ /*
+ * The following is a pointer to be passed into the
+@@ -288,10 +490,19 @@
+ void (*disable_extregs)(struct cfb_info *);
+ };
+
++#define ID_IGA_1682 0
++#define ID_CYBERPRO_2000 1
++#define ID_CYBERPRO_2010 2
++#define ID_CYBERPRO_5000 3
++
++struct fb_var_screeninfo;
++
+ /*
+ * Note! Writing to the Cyber20x0 registers from an interrupt
+ * routine is definitely a bad idea atm.
+ */
+ int cyber2000fb_attach(struct cyberpro_info *info, int idx);
+ void cyber2000fb_detach(int idx);
+-
++void cyber2000fb_enable_extregs(struct cfb_info *cfb);
++void cyber2000fb_disable_extregs(struct cfb_info *cfb);
++void cyber2000fb_get_fb_var(struct cfb_info *cfb, struct fb_var_screeninfo *var);
+diff -urN kernel-source-2.4.27-8/drivers/video/dbmx1fb.c kernel-source-2.4.27-8-arm-1/drivers/video/dbmx1fb.c
+--- kernel-source-2.4.27-8/drivers/video/dbmx1fb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/dbmx1fb.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,2002 @@
++/******************************************************************************
++ Copyright (C) 2002 Motorola GSG-China
++
++ 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.
++*******************************************************************************/
++/*****************************************************************************
++ * File Name: dbmx1fb.c
++ *
++ * Progammers: Chen Ning, Zhang Juan
++ *
++ * Date of Creations: 10 DEC,2001
++ *
++ * Synopsis:
++ *
++ * Descirption: DB-MX1 LCD controller Linux frame buffer driver
++ * This file is subject to the terms and conditions of the
++ * GNU General Public License. See the file COPYING in the main
++ * directory of this archive for more details.
++ *
++ * Modification History:
++ * 10 DEC, 2001, initialization version, frame work for frame buffer driver
++ *
++*******************************************************************************/
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++#include <linux/mm.h>
++#include <linux/tty.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/fb.h>
++#include <linux/delay.h>
++#include <linux/wrapper.h>
++#include <linux/selection.h>
++#include <linux/console.h>
++#include <linux/kd.h>
++#include <linux/vt_kern.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++#include <asm/proc/pgtable.h>
++
++#include <video/fbcon.h>
++#include <video/fbcon-mfb.h>
++#include <video/fbcon-cfb4.h>
++#include <video/fbcon-cfb8.h>
++#include <video/fbcon-cfb16.h>
++
++#include "asm/arch/hardware.h"
++#include "asm/arch/platform.h"
++#include "asm/arch/memory.h"
++
++#include "dbmx1fb.h"
++
++#undef SUP_TTY0
++
++#define LCD_PM
++#ifdef LCD_PM
++#include <linux/pm.h>
++struct pm_dev *pm;
++#endif
++
++// PLAM - make sure fbmem.c also has this defined for full screen frame
++// buffer support in SDRAM
++#define FULL_SCREEN
++
++#undef HARDWARE_CURSOR
++// #undef HARDWARE_CURSOR
++#undef DEBUG
++
++
++/********************************************************************************/
++#ifdef DEBUG
++# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
++#define FUNC_START DPRINTK(KERN_ERR"start\n");
++#define FUNC_END DPRINTK(KERN_ERR"end\n");
++#else
++# define DPRINTK(fmt, args...)
++#define FUNC_START
++#define FUNC_END
++#endif
++
++#define _IO_ADDRESS(r) ((r)+0xf0000000)
++unsigned int READREG(unsigned int r)
++{
++ volatile unsigned int * reg;
++ reg = (volatile unsigned int*) _IO_ADDRESS(r);
++ return *reg;
++}
++void WRITEREG(unsigned int r, unsigned int val)
++{
++ volatile unsigned int *reg;
++ reg = (volatile unsigned int*) _IO_ADDRESS(r);
++ *reg = val;
++ return;
++}
++
++#define FONT_DATA ((unsigned char *)font->data)
++struct fbcon_font_desc *font;
++
++/* Local LCD controller parameters */
++struct dbmx1fb_par{
++ u_char *screen_start_address; /* Screen Start Address */
++ u_char *v_screen_start_address;/* Virtul Screen Start Address */
++ unsigned long screen_memory_size; /* screen memory size */
++ unsigned int palette_size;
++ unsigned int max_xres;
++ unsigned int max_yres;
++ unsigned int xres;
++ unsigned int yres;
++ unsigned int xres_virtual;
++ unsigned int yres_virtual;
++ unsigned int max_bpp;
++ unsigned int bits_per_pixel;
++ unsigned int currcon;
++ unsigned int visual;
++ unsigned int TFT :1;
++ unsigned int color :1 ;
++ unsigned int sharp :1 ;
++
++ unsigned short cfb16[16];
++};
++
++#ifdef HARDWARE_CURSOR
++/* hardware cursor parameters */
++struct dbmx1fb_cursor{
++ // int enable;
++ int startx;
++ int starty;
++ int blinkenable;
++ int blink_rate;
++ int width;
++ int height;
++ int color[3];
++ int state;
++};
++
++/* Frame buffer of LCD information */
++struct dbmx1fb_info{
++ struct display_switch dispsw;
++ struct dbmx1fb_cursor cursor;
++};
++#endif // HARDWARE_CURSOR
++
++static u_char* p_framebuffer_memory_address;
++static u_char* v_framebuffer_memory_address;
++
++/* Fake monspecs to fill in fbinfo structure */
++static struct fb_monspecs monspecs __initdata = {
++ 30000, 70000, 50, 65, 0 /* Generic */
++};
++
++/* color map initial */
++static unsigned short __attribute__((unused)) color4map[16] = {
++ 0x0000, 0x000f, 0x00f0, 0x0f2a, 0x0f00, 0x0f0f, 0x0f88, 0x0ccc,
++ 0x0888, 0x00ff, 0x00f8, 0x0f44, 0x0fa6, 0x0f22, 0x0ff0, 0x0fff
++};
++
++static unsigned short gray4map[16] = {
++ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
++ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f
++};
++
++static struct display global_disp; /* Initial (default) Display Settings */
++static struct fb_info fb_info;
++static struct fb_var_screeninfo init_var = {};
++static struct dbmx1fb_par current_par={ };
++
++/* Frame buffer device API */
++static int dbmx1fb_get_fix(struct fb_fix_screeninfo *fix, int con,
++ struct fb_info *info);
++static int dbmx1fb_get_var(struct fb_var_screeninfo *var, int con,
++ struct fb_info *info);
++static int dbmx1fb_set_var(struct fb_var_screeninfo *var, int con,
++ struct fb_info *info);
++static int dbmx1fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
++ struct fb_info *info);
++static int dbmx1fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++ struct fb_info *info);
++
++/* Interface to the low level driver */
++static int dbmx1fb_switch(int con, struct fb_info *info);
++static void dbmx1fb_blank(int blank, struct fb_info *info);
++static int dbmx1fb_updatevar(int con, struct fb_info *info);
++
++/* Internal routines */
++static int _reserve_fb_memory(void);
++static void _install_cmap(int con, struct fb_info *info);
++static void _enable_lcd_controller(void);
++static void _disable_lcd_controller(void);
++static int _encode_var(struct fb_var_screeninfo *var,
++ struct dbmx1fb_par *par);
++static int _decode_var(struct fb_var_screeninfo *var,
++ struct dbmx1fb_par *par);
++
++/* initialization routines */
++static void __init _init_lcd_system(void);
++static int __init _init_lcd(void);
++static void __init _init_fbinfo(void);
++static int __init _reserve_fb_memory(void);
++
++/* frame buffer ops */
++static struct fb_ops dbmx1fb_ops = {
++ owner: THIS_MODULE,
++ fb_get_fix: dbmx1fb_get_fix,
++ fb_get_var: dbmx1fb_get_var,
++ fb_set_var: dbmx1fb_set_var,
++ fb_get_cmap: dbmx1fb_get_cmap,
++ fb_set_cmap: dbmx1fb_set_cmap,
++};
++
++#ifdef HARDWARE_CURSOR
++/* Hardware Cursor */
++static void dbmx1fb_cursor(struct display *p, int mode, int x, int y);
++static int dbmx1fb_set_font(struct display *d, int width, int height);
++static UINT8 cursor_color_map[] = {0xf8};
++static void dbmx1fb_set_cursor_state(struct dbmx1fb_info *fb,UINT32 state);
++static void dbmx1fb_set_cursor(struct dbmx1fb_info *fb);
++static void dbmx1fb_set_cursor_blink(struct dbmx1fb_info *fb,int blink);
++
++struct display_switch dbmx1fb_cfb4 = {
++ setup: fbcon_cfb4_setup,
++ bmove: fbcon_cfb4_bmove,
++ clear: fbcon_cfb4_clear,
++ putc: fbcon_cfb4_putc,
++ putcs: fbcon_cfb4_putcs,
++ revc: fbcon_cfb4_revc,
++ cursor: dbmx1fb_cursor,
++ set_font: dbmx1fb_set_font,
++ fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(16)
++};
++
++struct display_switch dbmx1fb_cfb8 = {
++ setup: fbcon_cfb8_setup,
++ bmove: fbcon_cfb8_bmove,
++ clear: fbcon_cfb8_clear,
++ putc: fbcon_cfb8_putc,
++ putcs: fbcon_cfb8_putcs,
++ revc: fbcon_cfb8_revc,
++ cursor: dbmx1fb_cursor,
++ set_font: dbmx1fb_set_font,
++ fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(16)
++};
++
++struct display_switch dbmx1fb_cfb16 = {
++ setup: fbcon_cfb16_setup,
++ bmove: fbcon_cfb16_bmove,
++ clear: fbcon_cfb16_clear,
++ putc: fbcon_cfb16_putc,
++ putcs: fbcon_cfb16_putcs,
++ revc: fbcon_cfb16_revc,
++ cursor: dbmx1fb_cursor,
++ set_font: dbmx1fb_set_font,
++ fontwidthmask: FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(16)
++};
++#endif // HARDWARE_CURSOR
++
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_getcolreg()
++ *
++ * Input: regno : Color register ID
++ * red : Color map red[]
++ * green : Color map green[]
++ * blue : Color map blue[]
++ * transparent : Flag
++ * info : Fb_info database
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Description: Transfer to fb_xxx_cmap handlers as parameters to
++ * control color registers
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++#define RED 0xf00
++#define GREEN 0xf0
++#define BLUE 0x0f
++static int dbmx1fb_getcolreg(u_int regno, u_int *red, u_int *green,
++ u_int *blue, u_int *trans, struct fb_info *info)
++{
++ unsigned int val;
++
++ FUNC_START;
++
++ if(regno >= current_par.palette_size)
++ return 1;
++
++ val = READREG(DBMX1_LCD_MAPRAM+regno);
++
++ if((current_par.bits_per_pixel == 4)&&(!current_par.color))
++ {
++ *red = *green = *blue = (val & BLUE) << 4;//TODO:
++ *trans = 0;
++ }
++ else
++ {
++ *red = (val & RED) << 4;
++ *green = (val & GREEN) << 8;
++ *blue = (val & BLUE) << 12;
++ *trans = 0;
++ }
++
++ FUNC_END;
++ return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_setcolreg()
++ *
++ * Input: regno : Color register ID
++ * red : Color map red[]
++ * green : Color map green[]
++ * blue : Color map blue[]
++ * transparent : Flag
++ * info : Fb_info database
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Description: Transfer to fb_xxx_cmap handlers as parameters to
++ * control color registers
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++ *****************************************************************************/
++static int
++dbmx1fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++ u_int trans, struct fb_info *info)
++{
++ unsigned int val=0;
++ FUNC_START
++ if(regno >= current_par.palette_size)
++ return 1;
++
++ if((current_par.bits_per_pixel == 4)&&(!current_par.color))
++ val = (blue & 0x00f) << 12;//TODO:
++ else
++ {
++ val = (blue >> 12 ) & BLUE;
++ val |= (green >> 8) & GREEN;
++ val |= (red >> 4) & RED;
++ }
++
++ if (regno < 16) {
++ current_par.cfb16[regno] =
++ regno | regno << 5 | regno << 10;
++}
++
++ WRITEREG(DBMX1_LCD_MAPRAM+regno, val);
++ FUNC_END;
++ return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_get_cmap()
++ *
++ * Input: cmap : Ouput data pointer
++ * kspc : Kernel space flag
++ * con : Console ID
++ * info : Frame buffer information
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Description: Data is copied from hardware or local or system DISPAY,
++ * and copied to cmap.
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
++ struct fb_info *info)
++{
++ int err = 0;
++
++ FUNC_START;
++ DPRINTK("current_par.visual=%d\n", current_par.visual);
++ if (con == current_par.currcon)
++ err = fb_get_cmap(cmap, kspc, dbmx1fb_getcolreg, info);
++ else if (fb_display[con].cmap.len)
++ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
++ else
++ fb_copy_cmap(fb_default_cmap(current_par.palette_size),
++ cmap, kspc ? 0 : 2);
++ FUNC_END;
++ return err;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_set_cmap()
++ *
++ * Input: cmap : Ouput data pointer
++ * kspc : Kernel space flag
++ * con : Console ID
++ * info : Frame buffer information
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Description: Copy data from cmap and copy to DISPLAY. If DISPLAy has no cmap,
++ * allocate memory for it. If DISPLAY is current console and visible,
++ * then hardware color map shall be set.
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
++{
++ int err = 0;
++
++ FUNC_START;
++ DPRINTK("current_par.visual=%d\n", current_par.visual);
++ if (!fb_display[con].cmap.len)
++ err = fb_alloc_cmap(&fb_display[con].cmap,
++ current_par.palette_size, 0);
++
++ if (!err) {
++ if (con == current_par.currcon)
++ err = fb_set_cmap(cmap, kspc, dbmx1fb_setcolreg,
++ info);
++ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
++ }
++ FUNC_END;
++ return err;
++}
++/*****************************************************************************
++ * Function Name: dbmx1fb_get_var()
++ *
++ * Input: var : Iuput data pointer
++ * con : Console ID
++ * info : Frame buffer information
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Functions Called: _encode_var()
++ *
++ * Description: Get color map from current, or global display[console]
++ * used by ioctl
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++ if (con == -1) {
++ _encode_var(var, ¤t_par);
++ } else
++ *var = fb_display[con].var;
++ return 0;
++}
++
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_updatevar()
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Fill in display switch with LCD information,
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int dbmx1fb_updatevar(int con, struct fb_info *info)
++{
++ DPRINTK("entered\n");
++ return 0;
++}
++
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_set_dispsw()
++ *
++ * Input: display : Iuput data pointer
++ * dbmx1fb_info : Frame buffer of LCD information
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Fill in display switch with LCD information,
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static void dbmx1fb_set_dispsw(struct display *disp
++#ifdef HARDWARE_CURSOR
++ ,struct dbmx1fb_info *info
++#endif
++ )
++{
++ FUNC_START;
++ switch (disp->var.bits_per_pixel) {
++#ifdef HARDWARE_CURSOR
++#ifdef FBCON_HAS_CFB4
++ case 4:
++ fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ info->dispsw = dbmx1fb_cfb4;
++ disp->dispsw = &info->dispsw;
++ disp->dispsw_data = NULL;
++ break;
++#endif
++#ifdef FBCON_HAS_CFB8
++ case 8:
++ fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ info->dispsw = dbmx1fb_cfb8;
++ disp->dispsw = &info->dispsw;
++ disp->dispsw_data = NULL;
++ break;
++#endif
++#ifdef FBCON_HAS_CFB16
++ case 12:
++ case 16:
++ fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR;
++ info->dispsw = dbmx1fb_cfb16;
++ disp->dispsw = &info->dispsw;
++ disp->dispsw_data = current_par.cfb16;
++ break;
++#endif
++#else //!HARDWARE_CURSOR
++ /* first step disable the hardware cursor */
++#ifdef FBCON_HAS_CFB4
++ case 4:
++ fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ disp->dispsw = &fbcon_cfb4;
++ disp->dispsw_data = NULL;
++ break;
++#endif
++#ifdef FBCON_HAS_CFB8
++ case 8:
++ fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++ disp->dispsw = &fbcon_cfb8;
++ disp->dispsw_data = NULL;
++ break;
++#endif
++#ifdef FBCON_HAS_CFB16
++ case 12:
++ case 16:
++ fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR;
++ disp->dispsw = &fbcon_cfb16;
++ disp->dispsw_data = current_par.cfb16;
++ break;
++#endif
++
++#endif // HARDWARE_CURSOR
++ default:
++ disp->dispsw = &fbcon_dummy;
++ disp->dispsw_data = NULL;
++ }
++#ifdef HARDWARE_CURSOR
++ if (&info->cursor)
++ {
++ info->dispsw.cursor = dbmx1fb_cursor;
++ info->dispsw.set_font = dbmx1fb_set_font;
++ }
++#endif // HARDWARE_CURSOR
++ FUNC_END;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_set_var()
++ *
++ * Input: var : Iuput data pointer
++ * con : Console ID
++ * info : Frame buffer information
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Functions Called: dbmx1fb_decode_var()
++ * dbmx1fb_encode_var()
++ * dbmx1fb_set_dispsw()
++ *
++ * Description: set current_par by var, also set display data, specially the console
++ * related fileops, then enable the lcd controller, and set cmap to
++ * hardware.
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++ struct display *display;
++ int err, chgvar = 0;
++ struct dbmx1fb_par par;
++
++ FUNC_START;
++ if (con >= 0)
++ display = &fb_display[con]; /* Display settings for console */
++ else
++ display = &global_disp; /* Default display settings */
++
++ /* Decode var contents into a par structure, adjusting any */
++ /* out of range values. */
++ if ((err = _decode_var(var, &par))){
++ DPRINTK("decode var error!");
++ return err;
++ }
++
++ // Store adjusted par values into var structure
++ _encode_var(var, &par);
++
++ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
++ return 0;
++
++ else if (((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) &&
++ ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NXTOPEN))
++ return -EINVAL;
++
++ if (con >= 0) {
++ if ((display->var.xres != var->xres) ||
++ (display->var.yres != var->yres) ||
++ (display->var.xres_virtual != var->xres_virtual) ||
++ (display->var.yres_virtual != var->yres_virtual) ||
++ (display->var.sync != var->sync) ||
++ (display->var.bits_per_pixel != var->bits_per_pixel) ||
++ (memcmp(&display->var.red, &var->red, sizeof(var->red))) ||
++ (memcmp(&display->var.green, &var->green, sizeof(var->green)
++ )) ||
++ (memcmp(&display->var.blue, &var->blue, sizeof(var->blue))))
++ chgvar = 1;
++ }
++
++ display->var = *var;
++ display->screen_base = par.v_screen_start_address;
++ display->visual = par.visual;
++ display->type = FB_TYPE_PACKED_PIXELS;
++ display->type_aux = 0;
++ display->ypanstep = 0;
++ display->ywrapstep = 0;
++ display->line_length =
++ display->next_line = (var->xres * 16) / 8;
++
++ display->can_soft_blank = 1;
++ display->inverse = 0;
++
++ dbmx1fb_set_dispsw(display
++#ifdef HARDWARE_CURSOR
++ , (struct dbmx1fb_info *)info
++#endif // HARDWARE_CURSOR
++ );
++
++ /* If the console has changed and the console has defined */
++ /* a changevar function, call that function. */
++ if (chgvar && info && info->changevar)
++ info->changevar(con); // TODO:
++
++ /* If the current console is selected and it's not truecolor,
++ * update the palette
++ */
++ if ((con == current_par.currcon) &&
++ (current_par.visual != FB_VISUAL_TRUECOLOR)) {
++ struct fb_cmap *cmap;
++
++ current_par = par; // TODO ?
++ if (display->cmap.len)
++ cmap = &display->cmap;
++ else
++ cmap = fb_default_cmap(current_par.palette_size);
++
++ fb_set_cmap(cmap, 1, dbmx1fb_setcolreg, info);
++ }
++
++ /* If the current console is selected, activate the new var. */
++ if (con == current_par.currcon){
++ init_var = *var; // TODO:gcc support structure copy?
++ _enable_lcd_controller();
++ }
++
++ FUNC_END;
++ return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_get_fix()
++ *
++ * Input: fix : Ouput data pointer
++ * con : Console ID
++ * info : Frame buffer information
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Functions Called: VOID
++ *
++ * Description: get fix from display data, current_par data
++ * used by ioctl
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++{
++ struct display *display;
++
++ FUNC_START;
++ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
++ strcpy(fix->id, DBMX1_NAME);
++
++ if (con >= 0)
++ {
++ DPRINTK("Using console specific display for con=%d\n",con);
++ display = &fb_display[con]; /* Display settings for console */
++ }
++ else
++ display = &global_disp; /* Default display settings */
++
++ fix->smem_start = (unsigned long)current_par.screen_start_address;
++ fix->smem_len = current_par.screen_memory_size;
++//printk("dbmx1fb_get_fix, pointer fix: 0x%08x, smem_len: 0x%08x\n",fix,fix->smem_len);
++ fix->type = display->type;
++ fix->type_aux = display->type_aux;
++ fix->xpanstep = 0;
++ fix->ypanstep = display->ypanstep;
++ fix->ywrapstep = display->ywrapstep;
++ fix->visual = display->visual;
++ fix->line_length = display->line_length;
++ fix->accel = FB_ACCEL_NONE;
++
++ FUNC_END;
++ return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_inter_handler()
++ *
++ * Input:
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Interrupt handler
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static void dbmx1fb_inter_handler(int irq, void *dev_id, struct pt_regs *regs)
++{
++ unsigned int intsr;
++ FUNC_START;
++ intsr = READREG(DBMX1_LCD_INTSR); // read to clear status
++ printk(KERN_ERR"lcd interrupt!\n");
++ FUNC_END;
++ // handle
++}
++
++#ifdef LCD_PM
++#define PM_OPT " [pm]"
++#define LCD_PMST_RESUME 0
++#define LCD_PMST_SUSPEND 1
++static unsigned int lcd_pm_status = LCD_PMST_RESUME;
++
++void lcd_pm_resume(void)
++{
++ if(lcd_pm_status == LCD_PMST_RESUME)
++ return;
++ WRITEREG(0x21c21c, 0x10000); // light on
++ WRITEREG(DBMX1_LCD_REFMCR, 0xf000002);
++ WRITEREG(DBMX1_LCD_PWMR, 0x00a9008a);
++ lcd_pm_status = LCD_PMST_RESUME;
++// printk(KERN_ERR"lcd resumed\n");
++}
++
++void lcd_pm_suspend(void)
++{
++ unsigned val;
++ if(lcd_pm_status == LCD_PMST_SUSPEND)
++ return;
++ val = READREG(0x20502c);
++ val |= 0x8000;
++ WRITEREG(0x20502c, val);
++ //To produce enough dealy time before trun off the LCDC.
++ for(val=0;val<=600000;val++);
++ val = READREG(0x21c21c);
++ val &= ~0x10000;
++ WRITEREG(0x21c21c, val); // light off
++ WRITEREG(DBMX1_LCD_REFMCR, 0x0);
++ lcd_pm_status = LCD_PMST_SUSPEND;
++// printk(KERN_ERR"lcd suspended\n");
++}
++
++int lcd_pm_handler(struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++ switch(rqst){
++ case PM_RESUME:
++ lcd_pm_resume();
++ break;
++ case PM_SUSPEND:
++ lcd_pm_suspend();
++ break;
++ default:
++ break;
++ }
++ return 0;
++}
++#endif // LCD_PM
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_init()
++ *
++ * Input: VOID
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Functions Called: _init_fbinfo()
++ * disable_irq()
++ * enable_irq()
++ * _init_lcd()
++ * dbmx1fb_init_cursor()
++ *
++ * Description: initialization module, all of init routine's entry point
++ * initialize fb_info, init_var, current_par
++ * and setup interrupt, memory, lcd controller
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++int __init dbmx1fb_init(void)
++{
++ int ret;
++#ifdef HARDWARE_CURSOR
++ struct dbmx1fb_info *info;
++#endif // HARDWARE_CURSOR
++
++ _init_lcd_system();
++
++ _init_fbinfo();
++
++ if ((ret = _reserve_fb_memory()) != 0){
++ printk(KERN_ERR"failed for reserved DBMX frame buffer memory\n");
++ return ret;
++ }
++
++#if 0
++ if (request_irq(IRQ_LCD,
++ dbmx1fb_inter_handler,
++ SA_INTERRUPT,
++ DEV_NAME,
++ NULL) != 0) {
++ printk(KERN_ERR "dbmx1fb: failed in request_irq\n");
++ return -EBUSY;
++ }
++
++ disable_irq(IRQ_LCD);
++#endif
++ if (dbmx1fb_set_var(&init_var, -1, &fb_info))
++ ; //current_par.allow_modeset = 0;
++
++ _init_lcd();
++ _enable_lcd_controller();
++
++#ifdef HARDWARE_CURSOR
++ info = kmalloc(sizeof(struct dbmx1fb_info), GFP_KERNEL);
++ if(info == NULL){
++ printk(KERN_ERR"can not kmalloc dbmx1fb_info memory\n");
++ return -1;
++ }
++
++ memset(info,0,sizeof(struct dbmx1fb_info));
++
++ info->cursor.blink_rate = DEFAULT_CURSOR_BLINK_RATE;
++ info->cursor.blinkenable = 0;
++ info->cursor.state = LCD_CURSOR_OFF;
++ WRITEREG(DBMX1_LCD_LCXYP,0x90010001);
++ WRITEREG(DBMX1_LCD_CURBLKCR,0x1F1F0000);
++ WRITEREG(DBMX1_LCD_LCHCC,0x0000F800);
++
++ DPRINTK(KERN_ERR"LCXYP = %x\n",READREG(DBMX1_LCD_LCXYP));
++ DPRINTK(KERN_ERR"CURBLICR = %x\n",READREG(DBMX1_LCD_CURBLKCR));
++ DPRINTK(KERN_ERR"LCHCC = %x\n",READREG(DBMX1_LCD_LCHCC));
++
++ //dbmx1fb_set_cursor(info);
++ //info->cursor = dbmx1fb_init_cursor(info);
++#endif // HARDWARE_CURSOR
++
++ register_framebuffer(&fb_info);
++
++#ifdef LCD_PM
++ pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, lcd_pm_handler);
++ printk("register LCD power management successfully.\n");
++#endif
++#if 0
++ enable_irq(IRQ_LCD); // TODO:
++#endif
++ /* This driver cannot be unloaded at the moment */
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_setup()
++ *
++ * Input: info : VOID
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Functions Called: VOID
++ *
++ * Description: basically, this routine used to parse command line parameters, which
++ * is initialization parameters for lcd controller, such as freq, xres,
++ * yres, and so on
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++int __init dbmx1fb_setup(char *options)
++{
++ FUNC_START;
++ FUNC_END;
++ return 0;
++}
++
++/*****************************************************************************
++ * Function Name: _init_fbinfo()
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: while 16bpp is used to store a 12 bits pixels packet, but
++ * it is not a really 16bpp system. maybe in-compatiable with
++ * other system or GUI.There are some field in var which specify
++ * the red/green/blue offset in a 16bit word, just little endian is
++ * concerned
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static void __init _init_fbinfo(void)
++{
++// Thomas Wong add this for debugging
++// *((unsigned char *)0xF5000008) = '&';
++
++ FUNC_START;
++ strcpy(fb_info.modename, DBMX1_NAME);
++ strcpy(fb_info.fontname, "Acorn8x8");
++
++ fb_info.node = -1;
++ fb_info.flags = FBINFO_FLAG_DEFAULT; // Low-level driver is not a module
++ fb_info.fbops = &dbmx1fb_ops;
++ fb_info.monspecs = monspecs;
++ fb_info.disp = &global_disp;
++ fb_info.changevar = NULL;
++ fb_info.switch_con = dbmx1fb_switch;
++ fb_info.updatevar = dbmx1fb_updatevar;
++ fb_info.blank = dbmx1fb_blank;
++
++/*
++ * * setup initial parameters
++ * */
++ memset(&init_var, 0, sizeof(init_var));
++
++ init_var.transp.length = 0;
++ init_var.nonstd = 0;
++ init_var.activate = FB_ACTIVATE_NOW;
++ init_var.xoffset = 0;
++ init_var.yoffset = 0;
++ init_var.height = -1;
++ init_var.width = -1;
++ init_var.vmode = FB_VMODE_NONINTERLACED;
++
++ if (1) {
++ current_par.max_xres = LCD_MAXX;
++ current_par.max_yres = LCD_MAXY;
++ current_par.max_bpp = LCD_MAX_BPP; // 12
++ init_var.red.length = 5; // 5;
++ init_var.green.length = 6; // 6;
++ init_var.blue.length = 5; // 5;
++#ifdef __LITTLE_ENDIAN
++ init_var.red.offset = 11;
++ init_var.green.offset = 5;
++ init_var.blue.offset = 0;
++#endif //__LITTLE_ENDIAN
++ init_var.grayscale = 16; // i suppose, TODO
++ init_var.sync = 0;
++ init_var.pixclock = 171521; // TODO
++ }
++
++ current_par.screen_start_address = NULL;
++ current_par.v_screen_start_address = NULL;
++ current_par.screen_memory_size = MAX_PIXEL_MEM_SIZE;
++// Thomas Wong add this for debugging
++//printk("_init_fbinfo, pointer to current_par: 0x%08x, screen_memory_size: 0x%08x\n", ¤t_par,current_par.screen_memory_size);
++ current_par.currcon = -1; // TODO
++
++ init_var.xres = current_par.max_xres;
++ init_var.yres = current_par.max_yres;
++ init_var.xres_virtual = init_var.xres;
++ init_var.yres_virtual = init_var.yres;
++ init_var.bits_per_pixel = current_par.max_bpp;
++
++ FUNC_END;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_blank()
++ *
++ * Input: blank : Blank flag
++ * info : Frame buffer database
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: _enable_lcd_controller()
++ * _disable_lcd_controller()
++ *
++ * Description: blank the screen, if blank, disable lcd controller, while if no blank
++ * set cmap and enable lcd controller
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static void
++dbmx1fb_blank(int blank, struct fb_info *info)
++{
++#ifdef SUP_TTY0
++ int i;
++
++ FUNC_START;
++ DPRINTK("blank=%d info->modename=%s\n", blank, info->modename);
++ if (blank) {
++ if (current_par.visual != FB_VISUAL_TRUECOLOR)
++ for (i = 0; i < current_par.palette_size; i++)
++ ; // TODO
++//printk("Disable LCD\n");
++ _disable_lcd_controller();
++ }
++ else {
++ if (current_par.visual != FB_VISUAL_TRUECOLOR)
++ dbmx1fb_set_cmap(&fb_display[current_par.currcon].cmap,
++ 1,
++ current_par.currcon, info);
++//printk("Enable LCD\n");
++ _enable_lcd_controller();
++ }
++ FUNC_END;
++#endif //SUP_TTY0
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_switch()
++ *
++ * Input: info : Frame buffer database
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: Switch to another console
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int dbmx1fb_switch(int con, struct fb_info *info)
++{
++ FUNC_START;
++ if (current_par.visual != FB_VISUAL_TRUECOLOR) {
++ struct fb_cmap *cmap;
++ if (current_par.currcon >= 0) {
++ // Get the colormap for the selected console
++ cmap = &fb_display[current_par.currcon].cmap;
++
++ if (cmap->len)
++ fb_get_cmap(cmap, 1, dbmx1fb_getcolreg, info);
++ }
++ }
++
++ current_par.currcon = con;
++ fb_display[con].var.activate = FB_ACTIVATE_NOW;
++ dbmx1fb_set_var(&fb_display[con].var, con, info);
++ FUNC_END;
++ return 0;
++}
++
++/*****************************************************************************
++ * Function Name: _encode_par()
++ *
++ * Input: var : Input var data
++ * par : LCD controller parameters
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: use current_par to set a var structure
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int _encode_var(struct fb_var_screeninfo *var,
++ struct dbmx1fb_par *par)
++{
++ // Don't know if really want to zero var on entry.
++ // Look at set_var to see. If so, may need to add extra params to par
++
++ // memset(var, 0, sizeof(struct fb_var_screeninfo));
++
++ var->xres = par->xres;
++ var->yres = par->yres;
++ var->xres_virtual = par->xres_virtual;
++ var->yres_virtual = par->yres_virtual;
++
++ var->bits_per_pixel = par->bits_per_pixel;
++
++ DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
++ switch(var->bits_per_pixel) {
++ case 2:
++ case 4:
++ case 8:
++ var->red.length = 4;
++ var->green = var->red;
++ var->blue = var->red;
++ var->transp.length = 0;
++ break;
++ case 12: // This case should differ for Active/Passive mode
++ case 16:
++ if (1) {
++ var->red.length = 4;
++ var->blue.length = 4;
++ var->green.length = 4;
++ var->transp.length = 0;
++#ifdef __LITTLE_ENDIAN
++ var->red.offset = 8;
++ var->green.offset = 4;
++ var->blue.offset = 0;
++ var->transp.offset = 0;
++#endif // __LITTLE_ENDIAN
++ }
++ else
++ {
++ var->red.length = 5;
++ var->blue.length = 5;
++ var->green.length = 6;
++ var->transp.length = 0;
++ var->red.offset = 11;
++ var->green.offset = 5;
++ var->blue.offset = 0;
++ var->transp.offset = 0;
++ }
++ break;
++ }
++
++ return 0;
++}
++
++/*****************************************************************************
++ * Function Name: _decode_var
++ *
++ * Input: var : Input var data
++ * par : LCD controller parameters
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Get the video params out of 'var'. If a value doesn't fit,
++ * round it up,if it's too big, return -EINVAL.
++ *
++ * Cautions: Round up in the following order: bits_per_pixel, xres,
++ * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
++ * bitfields, horizontal timing, vertical timing.
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int _decode_var(struct fb_var_screeninfo *var,
++ struct dbmx1fb_par *par)
++{
++ *par = current_par;
++
++ if ((par->xres = var->xres) < MIN_XRES)
++ par->xres = MIN_XRES;
++ if ((par->yres = var->yres) < MIN_YRES)
++ par->yres = MIN_YRES;
++ if (par->xres > current_par.max_xres)
++ par->xres = current_par.max_xres;
++ if (par->yres > current_par.max_yres)
++ par->yres = current_par.max_yres;
++ par->xres_virtual =
++ var->xres_virtual < par->xres ? par->xres : var->xres_virtual;
++ par->yres_virtual =
++ var->yres_virtual < par->yres ? par->yres : var->yres_virtual;
++ par->bits_per_pixel = var->bits_per_pixel;
++
++ switch (par->bits_per_pixel) {
++#ifdef FBCON_HAS_CFB4
++ case 4:
++ par->visual = FB_VISUAL_PSEUDOCOLOR;
++ par->palette_size = 16;
++ break;
++#endif
++#ifdef FBCON_HAS_CFB8
++ case 8:
++ par->visual = FB_VISUAL_PSEUDOCOLOR;
++ par->palette_size = 256;
++ break;
++#endif
++#ifdef FBCON_HAS_CFB16
++ case 12: // RGB 444
++ case 16: /* RGB 565 */
++ par->visual = FB_VISUAL_TRUECOLOR;
++ par->palette_size = 0;
++ break;
++#endif
++ default:
++ return -EINVAL;
++ }
++
++ par->screen_start_address =(u_char*)(
++ (u_long)p_framebuffer_memory_address+PAGE_SIZE);
++ par->v_screen_start_address =(u_char*)(
++ (u_long)v_framebuffer_memory_address+PAGE_SIZE);
++
++// Thomas Wong - try to change start address here (map to SRAM, instead of SDRAM)
++#ifndef FULL_SCREEN
++ par->screen_start_address =(u_char*)(0x00300000);
++ par->v_screen_start_address =(u_char*)(0xF0300000);
++#endif
++
++// par->screen_start_address =(u_char*)(0x0BE00000);
++// par->v_screen_start_address =(u_char*)(0xFBE00000);
++
++// par->screen_start_address =(u_char*)(0x12000000);
++// par->v_screen_start_address =(u_char*)(0xF2000000);
++
++ return 0;
++}
++
++
++/*****************************************************************************
++ * Function Name: _reserve_fb_memory()
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: get data out of var structure and set related LCD controller registers
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int __init _reserve_fb_memory(void)
++{
++ u_int required_pages;
++ u_int extra_pages;
++ u_int order;
++ struct page *page;
++ char *allocated_region;
++
++ DPRINTK("frame buffer memory size = %x\n", (unsigned int)ALLOCATED_FB_MEM_SIZE);
++ if (v_framebuffer_memory_address != NULL)
++ return -EINVAL;
++
++ /* Find order required to allocate enough memory for framebuffer */
++ required_pages = ALLOCATED_FB_MEM_SIZE >> PAGE_SHIFT;
++ for (order = 0 ; required_pages >> order ; order++) {;}
++ extra_pages = (1 << order) - required_pages;
++
++ if ((allocated_region =
++ (char *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)) == NULL){
++
++ DPRINTK("can not allocated memory\n");
++ return -ENOMEM;
++ }
++
++
++ v_framebuffer_memory_address = (u_char *)allocated_region +
++ (extra_pages << PAGE_SHIFT);
++ p_framebuffer_memory_address = (u_char *)__virt_to_phys(
++ (u_long)v_framebuffer_memory_address);
++#if 0
++ printk(KERN_ERR"Frame buffer __get_free_pages vd:= %x, pd= %x",
++ (unsigned int)v_framebuffer_memory_address,
++ (unsigned int)p_framebuffer_memory_address);
++#endif
++ /* Free all pages that we don't need but were given to us because */
++ /* __get_free_pages() works on powers of 2. */
++ for (;extra_pages;extra_pages--)
++ free_page((u_int)allocated_region + ((extra_pages-1) << PAGE_SHIFT));
++
++ /* Set reserved flag for fb memory to allow it to be remapped into */
++ /* user space by the common fbmem driver using remap_page_range(). */
++ for(page = virt_to_page(v_framebuffer_memory_address);
++ page < virt_to_page(v_framebuffer_memory_address
++ + ALLOCATED_FB_MEM_SIZE);
++ page++)
++ mem_map_reserve(page);
++#if 0
++ /* Remap the fb memory to a non-buffered, non-cached region */
++ v_framebuffer_memory_address = (u_char *)__ioremap(
++ (u_long)p_framebuffer_memory_address,
++ ALLOCATED_FB_MEM_SIZE,
++ L_PTE_PRESENT |
++ L_PTE_YOUNG |
++ L_PTE_DIRTY |
++ L_PTE_WRITE);
++#endif
++ current_par.screen_start_address =(u_char*)(
++ (u_long)p_framebuffer_memory_address+PAGE_SIZE);
++ current_par.v_screen_start_address =(u_char*)(
++ (u_long)v_framebuffer_memory_address+PAGE_SIZE);
++
++ DPRINTK("physical screen start addres: %x\n",
++ (u_long)p_framebuffer_memory_address+PAGE_SIZE);
++
++#ifndef FULL_SCREEN
++// Thomas Wong - we'll try to change the screen start address here
++// printk("\n\rMap LCD screen to SDRAM.\n\r");
++
++ printk("\n\rMap LCD screen to embedded SRAM.\n\r");
++ current_par.screen_start_address =(u_char*)(0x00300000);
++ current_par.v_screen_start_address =(u_char*)(0xF0300000);
++#endif
++
++// printk("\n\rMap LCD screen to SDRAM 0xFBE00000.\n\r");
++// current_par.screen_start_address =(u_char*)(0x0BE00000);
++// current_par.v_screen_start_address =(u_char*)(0xFBE00000);
++
++// printk("\n\rMap LCD screen to SRAM 0x12000000.\n\r");
++// current_par.screen_start_address =(u_char*)(0x12000000);
++// current_par.v_screen_start_address =(u_char*)(0xF2000000);
++
++ return (v_framebuffer_memory_address == NULL ? -EINVAL : 0);
++}
++
++/*****************************************************************************
++ * Function Name: _enable_lcd_controller()
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: enable Lcd controller, setup registers,
++ * base on current_par value
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static void _enable_lcd_controller(void)
++{
++ unsigned int val;
++#if 0
++ int i;
++ val =0;
++
++ FUNC_START;
++ _decode_var(&init_var, ¤t_par);
++
++ val = current_par.xres/16;
++ val <<= 20;
++ val += current_par.yres;
++ WRITEREG(DBMX1_LCD_XYMAX, val);
++
++ val=0;
++ val=current_par.xres_virtual /2;
++ WRITEREG(DBMX1_LCD_VPW, val);
++
++ switch(current_par.bits_per_pixel ){
++ case 4: // for gray only
++ for(i=0; i<16; i++){
++ WRITEREG(DBMX1_LCD_MAPRAM+i, gray4map[i]);
++ }
++ WRITEREG(DBMX1_LCD_PANELCFG, PANELCFG_VAL_4);
++ WRITEREG(DBMX1_LCD_VCFG, VCFG_VAL_4);
++ WRITEREG(DBMX1_LCD_HCFG, HCFG_VAL_4);
++ WRITEREG(DBMX1_LCD_INTCR, INTCR_VAL_4); // no interrupt
++ WRITEREG(DBMX1_LCD_REFMCR, REFMCR_VAL_4);
++ WRITEREG(DBMX1_LCD_DMACR, DMACR_VAL_4);
++ WRITEREG(DBMX1_LCD_PWMR, PWMR_VAL);
++ break;
++ case 12:
++ case 16:
++ WRITEREG(DBMX1_LCD_PANELCFG, PANELCFG_VAL_12);
++ WRITEREG(DBMX1_LCD_HCFG, HCFG_VAL_12);
++ WRITEREG(DBMX1_LCD_VCFG, VCFG_VAL_12);
++ WRITEREG(DBMX1_LCD_REFMCR, 0x0);
++ WRITEREG(DBMX1_LCD_DMACR, DMACR_VAL_12);
++ WRITEREG(DBMX1_LCD_PWMR, 0x00008200);
++ WRITEREG(DBMX1_LCD_REFMCR, 0x0f000002);
++ WRITEREG(DBMX1_LCD_PWMR, 0x0000008a);
++
++ break;
++ }
++#else
++ val = READREG(0x21c21c);
++ val |= 0x0010000;
++ WRITEREG(0x21c21c, val);
++
++ WRITEREG(DBMX1_LCD_PWMR, 0x8200);
++ WRITEREG(DBMX1_LCD_REFMCR, 0xf000002);
++
++ // Thomas Wong - we want 0x8A not 0x200
++// WRITEREG(DBMX1_LCD_PWMR, 0x200);
++// PLAM -- for rev2 (endian bit)
++// WRITEREG(DBMX1_LCD_PWMR, 0x0000008A);
++ WRITEREG(DBMX1_LCD_PWMR, 0x00A9008A);
++// end PLAM
++#endif
++
++ FUNC_END;
++}
++
++/*****************************************************************************
++ * Function Name: _disable_lcd_controller()
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: just disable the LCD controller
++ * disable lcd interrupt. others, i have no ideas
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static void _disable_lcd_controller(void)
++{
++ unsigned int val;
++ val = READREG(0x21c21c);
++ val &= ~0x0010000;
++ WRITEREG(0x21c21c, val);
++
++ WRITEREG(DBMX1_LCD_PWMR, 0x8200);
++// WRITEREG(DBMX1_LCD_REFMCR, DISABLELCD_VAL);
++ WRITEREG(0x205034, 0x0);
++}
++
++/*****************************************************************************
++ * Function Name: _install_cmap
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: set color map to hardware
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static void _install_cmap(int con, struct fb_info *info)
++{
++ if (con != current_par.currcon)
++ return ;
++ if (fb_display[con].cmap.len)
++ fb_set_cmap(&fb_display[con].cmap, 1, dbmx1fb_setcolreg, info);
++ else
++ fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
++ 1,
++ dbmx1fb_setcolreg,
++ info);
++ return ;
++}
++
++/*****************************************************************************
++ * Function Name: _init_lcd_system
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: initialize the gpio port C and port D with DMA enable
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static void __init _init_lcd_system(void)
++{
++unsigned int val;
++
++/* Thomas Wong - we don't need DMA here
++ // dma reset & enable
++ val = READREG(0x209000);
++ val |= 0x02;
++ WRITEREG(0x209000, val);
++ val = READREG(0x209000);
++ val |= 0x01;
++ WRITEREG(0x209000, val);
++*/
++ // gpio
++ //clear port D for LCD signal
++ WRITEREG(0x21c320, 0x0);
++ WRITEREG(0x21c338, 0x0);
++
++ val = READREG(0x21c220);
++ val |= 0x10000;
++ WRITEREG(0x21c220, val);
++ val = READREG(0x21c208);
++ val |= 0x03;
++ WRITEREG(0x21c208, val);
++ val = READREG(0x21c200);
++ val |= 0x10000;
++ WRITEREG(0x21c200, val);
++ val = READREG(0x21c238);
++ val |= 0x10000;
++ WRITEREG(0x21c238, val);
++ val = READREG(0x21c21c);
++ val |= 0x10000;
++ WRITEREG(0x21c21c, val);
++
++}
++
++static void set_pclk(unsigned int fmhz)
++{
++ unsigned int div= 96/fmhz;
++ unsigned int reg;
++ reg = READREG(0x21b020);
++ reg &= ~0xf0;
++ WRITEREG(0x21b020, reg);
++ reg |= ((div-1)<<4) &0xf0;
++ WRITEREG(0x21b020, reg);
++}
++
++/*****************************************************************************
++ * Function Name: _init_lcd
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: _decode_var()
++ *
++ * Description: initialize the LCD controller, use current_par for 12bpp
++ *
++ * Modification History:
++ * 10 DEC,2001, Chen Ning
++******************************************************************************/
++static int __init _init_lcd()
++{
++
++ unsigned int val, rate;
++ int i;
++ unsigned char * pscr;
++ //unsigned long pclk,temp,PCLKDIV2;
++ //unsigned long MFI,MFN,PD,MFD,tempReg,MCUPLLCLK;
++
++
++ _decode_var(&init_var, ¤t_par);
++
++ // gpio begin
++ val = READREG(0x21c21c);
++ val &= ~0x00010000;
++ WRITEREG(0x21c21c, val);
++ DPRINTK(KERN_ERR"DR=%x\n", READREG(0x21c21c));
++
++ // LCD regs
++ DPRINTK(KERN_ERR"write SSA by %x\n",
++ (unsigned int)current_par.screen_start_address);
++ WRITEREG(DBMX1_LCD_SSA, (unsigned int)current_par.screen_start_address);
++ DPRINTK(KERN_ERR"SSA=%x\n", READREG(DBMX1_LCD_SSA));
++
++ val =0;
++ val = current_par.xres/16;
++ val = val<<20;
++ val += current_par.yres;
++ DPRINTK(KERN_ERR"par.x=%x, y=%x\n",
++ current_par.xres,
++ current_par.yres);
++ WRITEREG(DBMX1_LCD_XYMAX, val);
++ DPRINTK(KERN_ERR"XYMAX=%x\n", READREG(DBMX1_LCD_XYMAX));
++
++ val=0;
++ val=current_par.xres_virtual/2;
++ WRITEREG(DBMX1_LCD_VPW, val);
++ DPRINTK(KERN_ERR"VPW=%x\n", READREG(DBMX1_LCD_VPW));
++
++ set_pclk(4);
++
++ DPRINTK(KERN_ERR"DBMX1_LCD_PANELCFG=%x\n",
++ READREG(DBMX1_LCD_PANELCFG));
++
++ WRITEREG(DBMX1_LCD_HCFG, 0x04000f06);
++ DPRINTK(KERN_ERR"DBMX1_LCD_HCFG=%x\n",
++ READREG(DBMX1_LCD_HCFG));
++
++ WRITEREG(DBMX1_LCD_VCFG, 0x04000907);
++ DPRINTK(KERN_ERR"DBMX1_LCD_VCFG=%x\n",
++ READREG(DBMX1_LCD_VCFG));
++
++ WRITEREG(DBMX1_LCD_REFMCR, 0x0);
++ DPRINTK(KERN_ERR"DBMX1_LCD_REFMCR=%x\n",
++ READREG(DBMX1_LCD_REFMCR));
++
++ WRITEREG(DBMX1_LCD_DMACR, DMACR_VAL_12);
++ DPRINTK(KERN_ERR"DBMX1_LCD_DMACR=%x\n",
++ READREG(DBMX1_LCD_DMACR));
++
++ WRITEREG(DBMX1_LCD_PWMR, 0x00008200);
++ DPRINTK(KERN_ERR"DBMX1_LCD_PWMR=%x\n",
++ READREG(DBMX1_LCD_PWMR));
++
++ // Thomas Wong - we don't want to turn it on here
++ /*
++ WRITEREG(DBMX1_LCD_REFMCR, 0x0f000002)
++ DPRINTK(KERN_ERR"DBMX1_LCD_REFMCR=%x\n",
++ READREG(DBMX1_LCD_REFMCR));
++ */
++
++ WRITEREG(DBMX1_LCD_PWMR, 0x0000008a);
++ DPRINTK(KERN_ERR"DBMX1_LCD_PWMR=%x\n",
++ READREG(DBMX1_LCD_PWMR));
++
++ // Thomas Wong
++ WRITEREG(0x21B020, 0x000B005B);
++// PLAM -- for rev2 (new register and endian bits)
++ WRITEREG(DBMX1_LCD_PANELCFG, 0xF8008B42); // little endian
++// WRITEREG(DBMX1_LCD_PANELCFG, 0xF8048B42); // big endian
++ WRITEREG(DBMX1_LCD_LGPMR, 0x00090300);
++// WRITEREG(DBMX1_LCD_PWMR, 0x0000008A);
++ WRITEREG (DBMX1_LCD_PWMR, 0x009A008A);
++// end PLAM
++
++// PLAM -- for rev2 (new DMACR setting)
++// WRITEREG(DBMX1_LCD_DMACR, 0x800C0002);
++ WRITEREG(DBMX1_LCD_DMACR, 0x00040008);
++//end PLAM
++
++
++#define DMACR_VAL_12 0x800C0002
++
++
++// WRITEREG(DBMX1_LCD_INTCR, INTCR_VAL_12);
++// DPRINTK(KERN_ERR"DBMX1_LCD_INTCR=%x\n",
++// READREG(DBMX1_LCD_INTCR));
++
++ // warm up LCD
++ for(i=0;i<10000000;i++) ;
++
++ // gpio end
++ val = READREG(0x21c21c);
++ val |= 0x00010000;
++ WRITEREG(0x21c21c, val);
++ DPRINTK(KERN_ERR"DR=%x\n",
++ READREG(0x21c21c));
++#if 0
++ // clear screen
++ pscr = current_par.v_screen_start_address;
++ for(i=0; i< (current_par.screen_memory_size - 2*PAGE_SIZE); i++){
++ *pscr++ = 0xff;
++ }
++#endif
++ DPRINTK(KERN_ERR"_init_lcd end \n");
++
++ return 0;
++}
++
++#ifdef HARDWARE_CURSOR
++
++/*
++ * Hardware cursor support
++ */
++ /*****************************************************************************
++ * Function Name: dbmx1fb_set_cursor_color()
++ *
++ * Input: fb : frame buffer database
++ * red : red component level in the cursor
++ * green : green component level in the cursor
++ * blue : blue component level in the cursor
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Set color of hardware cursor
++ *
++ * Modification History:
++ * 10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_set_cursor_color(struct dbmx1fb_info *fb, UINT8 *red, UINT8 *green, UINT8 *blue)
++{
++ struct dbmx1fb_cursor *c = &fb->cursor;
++ UINT32 color;
++
++ FUNC_START;
++ c->color[0] = *red;
++ c->color[1] = *green;
++ c->color[2] = *blue;
++ color = (UINT32)*red;
++ color |= (UINT32)(*green>>5);
++ color |= (UINT32)(*blue>>11);
++
++ WRITEREG(DBMX1_LCD_LCHCC, color);
++ FUNC_END;
++}
++
++ /*****************************************************************************
++ * Function Name: dbmx1fb_set_cursor()
++ *
++ * Input: fb : frame buffer database
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Load information of hardware cursor
++ *
++ * Modification History:
++ * 10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_set_cursor(struct dbmx1fb_info *fb)
++{
++ struct dbmx1fb_cursor *c = &fb->cursor;
++ UINT32 temp,tempReg,x,y;
++
++ FUNC_START;
++ //DPRINTK(KERN_ERR"BLINK_RATE=%x\n",c->blink_rate);
++// DPRINTK(KERN_ERR"width=%x\n",c->width);
++// DPRINTK(KERN_ERR"height=%x\n",c->height);
++
++ x = c->startx << 16;
++ if (c->state == LCD_CURSOR_ON)
++ x |= CURSOR_ON_MASK;
++
++#ifdef FBCON_HAS_CFB4
++ else if(c->state == LCD_CURSOR_REVERSED)
++ x |= CURSOR_REVERSED_MASK;
++ else if(c->state == LCD_CURSOR_ON_WHITE)
++ x |= CURSOR_WHITE_MASK;
++#elif defined(FBCON_HAS_CFB8)||defined(FBCON_HAS_CFB16)
++ else if(c->state == LCD_CURSOR_INVERT_BGD)
++ x |= CURSOR_INVERT_MASK;
++ else if(c->state == LCD_CURSOR_AND_BGD)
++ x |= CURSOR_AND_BGD_MASK;
++ else if(c->state == LCD_CURSOR_OR_BGD)
++ x |= CURSOR_OR_BGD_MASK;
++ else if(c->state == LCD_CURSOR_XOR_BGD)
++ x |= CURSOR_XOR_BGD_MASK;
++#endif
++ else
++ x = c->startx;
++
++ y = c->starty;
++
++ temp = (UINT32)x | (UINT32)y;
++ WRITEREG(DBMX1_LCD_LCXYP, temp);
++ //DPRINTK(KERN_ERR"lcxyp=%x\n",READREG(DBMX1_LCD_LCXYP));
++
++ temp = (UINT32)((c->width<<8) | (c->height));
++ tempReg = (UINT32)((temp<<16) | c->blink_rate);
++
++ WRITEREG(DBMX1_LCD_CURBLKCR, tempReg);
++
++ //c->blink_rate = 10;
++ if (c->blinkenable)
++ dbmx1fb_set_cursor_blink(fb,c->blink_rate);
++ DPRINTK(KERN_ERR"CURBLKCR=%x\n",READREG(DBMX1_LCD_CURBLKCR));
++ FUNC_END;
++}
++
++ /*****************************************************************************
++ * Function Name: dbmx1fb_set_cursor_blink()
++ *
++ * Input: fb : frame buffer database
++ * blink : input blink frequency of cursor
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Set blink frequency of hardware cursor
++ *
++ * Modification History:
++ * 10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_set_cursor_blink(struct dbmx1fb_info *fb,int blink)
++{
++ struct dbmx1fb_cursor *c = &fb->cursor;
++
++ unsigned long temp,tempReg;
++ unsigned long PCD, XMAX, YMAX, PCLKDIV2;
++ unsigned long tempMicroPeriod;
++
++ FUNC_START;
++ DPRINTK(KERN_ERR"dbmx1fb_set_cursor_blink\n");
++
++ if(!c){
++ DPRINTK(KERN_ERR"dangerouts, for c == null\n");
++ }
++ c->blink_rate = blink;
++
++ tempReg = READREG(DBMX1_LCD_XYMAX);
++ XMAX = (tempReg & XMAX_MASK) >> 20;
++ YMAX = tempReg & YMAX_MASK;
++ //XMAX = 240;
++ //YMAX = 320;
++ tempReg = READREG(PCDR);
++ PCLKDIV2 = (tempReg & PCLKDIV2_MASK) >> 4;
++ tempReg = READREG(DBMX1_LCD_PANELCFG);
++ PCD = tempReg & PCD_MASK;
++
++ temp = (PCLKDIV2 + 1);
++
++ if (!blink)
++ {
++ /* disable the blinking cursor function when frequency is 0 */
++ tempReg = READREG(DBMX1_LCD_CURBLKCR);
++ tempReg &= CURSORBLINK_DIS_MASK;
++ WRITEREG(DBMX1_LCD_CURBLKCR,tempReg);
++ }
++ else
++ {
++
++ tempMicroPeriod = temp * XMAX * YMAX * (PCD + 1);
++ temp = 96*10000000/(blink * tempMicroPeriod);
++ tempReg = READREG(DBMX1_LCD_CURBLKCR);
++ tempReg |= CURSORBLINK_EN_MASK;
++ tempReg |= temp;
++ WRITEREG(DBMX1_LCD_CURBLKCR,tempReg);
++ DPRINTK(KERN_ERR"Inter_CURBLKCR=%x\n",READREG(DBMX1_LCD_CURBLKCR));
++ }
++
++ FUNC_END;
++}
++
++ /*****************************************************************************
++ * Function Name: dbmx1fb_set_cursor_state()
++ *
++ * Input: fb : frame buffer database
++ * state : The status of the cursor to be set. e.g.
++ * LCD_CURSOR_OFF
++ * LCD_CURSOR_ON
++ * LCD_CURSOR_REVERSED
++ * LCD_CURSOR_ON_WHITE
++ * LCD_CURSOR_OR_BGD
++ * LCD_CURSOR_XOR_BGD
++ * LCD_CURSOR_AND_BGD
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Set state of cursor
++ *
++ * Modification History:
++ * 10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_set_cursor_state(struct dbmx1fb_info *fb,UINT32 state)
++{
++ struct dbmx1fb_cursor *c = &fb->cursor;
++ UINT32 temp;
++
++ FUNC_START;
++ c->state = state;
++ temp = READREG(DBMX1_LCD_LCXYP);
++ temp &= CURSOR_OFF_MASK;
++
++ if (state == LCD_CURSOR_OFF)
++ temp = temp;
++ else if (state == LCD_CURSOR_ON)
++ temp |= CURSOR_ON_MASK;
++#ifdef FBCON_HAS_CFB4
++ else if (state == LCD_CURSOR_REVERSED)
++ temp |= CURSOR_REVERSED_MASK;
++ else if (state == LCD_CURSOR_ON_WHITE)
++ temp |= CURSOR_WHITE_MASK;
++#elif defined(FBCON_HAS_CFB8)||defined(FBCON_HAS_CFB16)
++ else if(state == LCD_CURSOR_INVERT_BGD)
++ temp |= CURSOR_INVERT_MASK;
++ else if (state == LCD_CURSOR_OR_BGD)
++ temp |= CURSOR_OR_BGD_MASK;
++ else if (state == LCD_CURSOR_XOR_BGD)
++ temp |= CURSOR_XOR_BGD_MASK;
++ else if (state == LCD_CURSOR_AND_BGD)
++ temp |= CURSOR_AND_BGD_MASK;
++#endif
++ WRITEREG(DBMX1_LCD_LCXYP,temp);
++ DPRINTK(KERN_ERR"LCDXYP=%x\n",READREG(DBMX1_LCD_LCXYP));
++ FUNC_END;
++}
++
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_cursor()
++ *
++ * Input: fb : frame buffer database
++ *
++ * Value Returned: cursor : The structure of hardware cursor
++ *
++ * Functions Called: dbmx1fb_set_cursor()
++ * dbmx1fb_set_cursor_state()
++ *
++ * Description: The entry for display switch to operate hardware cursor
++ *
++ * Modification History:
++ * 10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_cursor(struct display *p, int mode, int x, int y)
++{
++ struct dbmx1fb_info *fb = (struct dbmx1fb_info *)p->fb_info;
++ struct dbmx1fb_cursor *c = &fb->cursor;
++
++ FUNC_START;
++ if (c==0) return;
++
++
++ x *= fontwidth(p);
++ y *= fontheight(p);
++
++ c->startx = x;
++ c->starty = y;
++
++ switch (mode) {
++ case CM_ERASE:
++ dbmx1fb_set_cursor_state(fb,LCD_CURSOR_OFF);
++ break;
++
++ case CM_DRAW:
++ case CM_MOVE:
++ c->state = LCD_CURSOR_ON;
++ dbmx1fb_set_cursor(fb);
++ dbmx1fb_set_cursor_state(fb, c->state);
++ break;
++ }
++ FUNC_END;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_set_font()
++ *
++ * Input: display : console datebase
++ * width : The new width of cursor to be set.
++ * height : The new height of cursor position to be set
++ *
++ * Value Returned: int : Return status.If no error, return 0.
++ *
++ * Functions Called: dbmx1fb_set_cursor()
++ * dbmx1fb_set_cursor_color()
++ *
++ * Description: Set font for cursor
++ *
++ * Modification History:
++ * 10 DEC,2001, Zhang Juan
++******************************************************************************/
++static int dbmx1fb_set_font(struct display *d, int width, int height)
++{
++ struct dbmx1fb_info *fb=(struct dbmx1fb_info *)d->fb_info;
++ struct dbmx1fb_cursor *c = &fb->cursor;
++
++ FUNC_START;
++ if(!d){
++ printk(KERN_ERR"dbmx1fb_set_font d=null\n");
++ return -1;
++ }
++
++ if (c) {
++ if (!width || !height) {
++ width = 16;
++ height = 16;
++ }
++
++ c->width = width;
++ c->height = height;
++
++ DPRINTK(KERN_ERR"set cursor\n");
++ dbmx1fb_set_cursor(fb);
++ DPRINTK(KERN_ERR"set color cursor\n");
++ dbmx1fb_set_cursor_color(fb, cursor_color_map,
++ cursor_color_map, cursor_color_map);
++ }else{
++ DPRINTK(KERN_ERR"set cursor failed, cursor == null\n");
++ }
++
++ FUNC_END;
++ return 1;
++}
++#endif // HARDWARE_CURSOR
++/* end of file */
++
+diff -urN kernel-source-2.4.27-8/drivers/video/dbmx1fb.h kernel-source-2.4.27-8-arm-1/drivers/video/dbmx1fb.h
+--- kernel-source-2.4.27-8/drivers/video/dbmx1fb.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/dbmx1fb.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,213 @@
++/****************************************************************************
++ *
++ * Copyright (C) 2001, Chen Ning All Rights Reserved
++ *
++ * File Name: dbmx1fb.h
++ *
++ * Progammers: Chen Ning, Zhang Juan
++ *
++ * Date of Creations: 10 DEC,2001
++ *
++ * Synopsis:
++ *
++ * Descirption:
++ *
++ * Modification History:
++ * 10 DEC, 2001, initialization version, frame work for frame buffer driver
++ *
++*******************************************************************************/
++
++#ifndef LCDFB_H
++#define LCDFB_H
++#include "asm/arch/hardware.h"
++#include "asm/arch/platform.h"
++
++typedef signed char BOOLEAN;
++typedef unsigned char UINT8; /* Unsigned 8 bit quantity */
++typedef signed char SINT8; /* Signed 8 bit quantity */
++typedef unsigned short UINT16; /* Unsigned 16 bit quantity */
++typedef signed short SINT16; /* Signed 16 bit quantity */
++typedef unsigned long UINT32; /* Unsigned 32 bit quantity */
++typedef signed long SINT32; /* Signed 32 bit quantity */
++
++typedef volatile BOOLEAN VBOOLEAN;
++typedef volatile UINT8 VUINT8; /* Unsigned 8 bit quantity */
++typedef volatile SINT8 VSINT8; /* Signed 8 bit quantity */
++typedef volatile UINT16 VUINT16; /* Unsigned 16 bit quantity */
++typedef volatile SINT16 VSINT16; /* Signed 16 bit quantity */
++typedef volatile UINT32 VUINT32; /* Unsigned 32 bit quantity */
++typedef volatile SINT32 VSINT32; /* Signed 32 bit quantity */
++
++#define LCDBASE 0x00205000
++#define LCD_REGIONSIZE 0xc00
++
++#define DBMX1_LCD_SSA 0x00205000
++#define DBMX1_LCD_XYMAX 0x00205004
++#define DBMX1_LCD_VPW 0x00205008
++#define DBMX1_LCD_LCXYP 0x0020500c
++#define DBMX1_LCD_CURBLKCR 0x00205010
++#define DBMX1_LCD_LCHCC 0x00205014
++#define DBMX1_LCD_PANELCFG 0x00205018
++#define DBMX1_LCD_HCFG 0x0020501c
++#define DBMX1_LCD_VCFG 0x00205020
++#define DBMX1_LCD_POS 0x00205024
++#define DBMX1_LCD_LGPMR 0x00205028
++#define DBMX1_LCD_PWMR 0x0020502c
++#define DBMX1_LCD_DMACR 0x00205030
++#define DBMX1_LCD_REFMCR 0x00205034
++#define DBMX1_LCD_INTCR 0x00205038
++#define DBMX1_LCD_INTSR 0x00205040
++#define DBMX1_LCD_MAPRAM 0x00205800
++
++#define MPCTL0 0x0021B004
++#define PCDR 0X0021B020
++
++#define SSA DBMX1_LCD_SSA
++#define XYMAX DBMX1_LCD_XYMAX
++#define VPW DBMX1_LCD_VPW
++#define LCXYP DBMX1_LCD_LCXYP
++#define CURBLKCR DBMX1_LCD_CURBLKCR
++#define LCHCC DBMX1_LCD_LCHCC
++#define PANELCFG DBMX1_LCD_PANELCFG
++#define VCFG DBMX1_LCD_VCFG
++#define HCFG DBMX1_LCD_HCFG
++#define POS DBMX1_LCD_POS
++#define LGPMR DBMX1_LCD_LGPMR
++#define PWMR DBMX1_LCD_PWMR
++#define DMACR DBMX1_LCD_DMACR
++#define REFMCR DBMX1_LCD_REFMCR
++#define INTCR DBMX1_LCD_INTCR
++#define INTSR DBMX1_LCD_INTSR
++#define MAPRAM DBMX1_LCD_MAPRAM
++// default value
++#define SHARP_TFT_240x320
++
++#ifdef SHARP_TFT_240x320
++#define PANELCFG_VAL_12 0xf8008b48
++#define HCFG_VAL_12 0x04000f06
++#define VCFG_VAL_12 0x04000907
++#define PWMR_VAL 0x0000008a
++#define LCD_MAXX 240
++#define LCD_MAXY 320
++#else //SHARP_TFT_240x320
++
++#define PANELCFG_VAL_12 0xf8088c6b
++#define HCFG_VAL_12 0x04000f06
++#define VCFG_VAL_12 0x04010c03
++#define PWMR_VAL 0x00000200
++#define LCD_MAXX 320
++#define LCD_MAXY 240
++#endif // SHARP_TFT_240x320
++
++#define PANELCFG_VAL_4 0x20008c09
++#define PANELCFG_VAL_4C 0x60008c09
++
++#define HCFG_VAL_4 0x04000f07
++
++#define VCFG_VAL_4 0x04010c03
++
++
++#define REFMCR_VAL_4 0x00000003
++#define REFMCR_VAL_12 0x00000003
++#define DISABLELCD_VAL 0x00000000
++
++#define DMACR_VAL_4 0x800c0003 // 12 & 3 TRIGGER
++#define DMACR_VAL_12 0x00020008
++
++#define INTCR_VAL_4 0x00000000
++#define INTCR_VAL_12 0x00000000
++
++#define INTSR_UDRERR 0x00000008
++#define INTSR_ERRRESP 0x00000004
++#define INTSR_EOF 0x00000002
++#define INTSR_BOF 0x00000001
++
++#define MIN_XRES 64
++#define MIN_YRES 64
++
++#define LCD_MAX_BPP 16
++
++#if 0
++#define READREG(r) \
++ inl(IO_ADDRESS(r))
++
++#define WRITEREG(r, val) \
++ outl(val, IO_ADDRESS(r))
++#else
++#endif // 0
++
++#define MAX_PALETTE_NUM_ENTRIES 256
++#define MAX_PIXEL_MEM_SIZE \
++ ((current_par.max_xres * current_par.max_yres * current_par.max_bpp)/8)
++#define MAX_FRAMEBUFFER_MEM_SIZE \
++ (MAX_PIXEL_MEM_SIZE + 32)
++#define ALLOCATED_FB_MEM_SIZE \
++ (PAGE_ALIGN(MAX_FRAMEBUFFER_MEM_SIZE + PAGE_SIZE * 2))
++
++ // TODO:
++#define FBCON_HAS_CFB4
++#define FBCON_HAS_CFB8
++#define FBCON_HAS_CFB16
++
++#define IRQ_LCD 12 // TODO: which irq?
++#define DBMX1_NAME "DBMX1FB"
++#define DEV_NAME DBMX1_NAME
++#define MAX_PALETTE_NUM_ENTRIES 256
++#define DEFAULT_CURSOR_BLINK_RATE (20)
++#define CURSOR_DRAW_DELAY (2)
++/*cursor status*/
++#define LCD_CURSOR_OFF 0
++#define LCD_CURSOR_ON 1
++
++#ifdef FBCON_HAS_CFB4
++ #define LCD_CURSOR_REVERSED 2
++ #define LCD_CURSOR_ON_WHITE 3
++#elif defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB16)
++ #define LCD_CURSOR_INVERT_BGD 2
++ #define LCD_CURSOR_AND_BGD 3
++ #define LCD_CURSOR_OR_BGD 4
++ #define LCD_CURSOR_XOR_BGD 5
++#endif //FBCON_HAS_CFB4
++
++/* MASK use for caculating MCDUPLLCLK */
++#define MFI_MASK 0x00003C00
++#define MFN_MASK 0x000003FF
++#define PD_MASK 0x3C000000
++#define MFD_MASK 0x03FF0000
++#define PCLKDIV2_MASK 0x000000F0
++#define PCD_MASK 0x0000003F
++#define XMAX_MASK 0xF3F00000
++#define YMAX_MASK 0x000001FF
++#define HWAIT1_MASK 0x0000FF00
++#define HWAIT2_MASK 0x000000FF
++#define HWIDTH_MASK 0xFC000000
++#define PASSDIV_MASK 0x00FF0000
++#define VWAIT1_MASK 0x0000FF00
++#define VWAIT2_MASK 0x000000FF
++#define VWIDTH_MASK 0xFC000000
++#define CURSORBLINK_DIS_MASK 0x80000000
++
++#define DISPLAY_MODE_MASK 0x80000000
++
++#define MCU_FREQUENCE 32768
++#define COLOR_MASK 0x40000000
++
++/* MASK use for indicating the cursor status */
++#define CURSOR_ON_MASK 0x40000000
++#define CURSOR_OFF_MASK 0x0FFFFFFF
++#define MAX_CURSOR_WIDTH 31
++#define MAX_CURSOR_HEIGHT 31
++#define CURSORBLINK_EN_MASK 0x80000000
++
++#ifdef FBCON_HAS_CFB4
++#define CURSOR_REVERSED_MASK 0x80000000
++#define CURSOR_WHITE_MASK 0xC0000000
++#else
++#define CURSOR_INVERT_MASK 0x80000000
++#define CURSOR_AND_BGD_MASK 0xC0000000
++#define CURSOR_OR_BGD_MASK 0x50000000
++#define CURSOR_XOR_BGD_MASK 0x90000000
++#endif // FBCON_HAS_CFB4
++
++#endif //LCDFB_H
++
+diff -urN kernel-source-2.4.27-8/drivers/video/fbmem.c kernel-source-2.4.27-8-arm-1/drivers/video/fbmem.c
+--- kernel-source-2.4.27-8/drivers/video/fbmem.c 2004-08-08 00:26:05.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/fbmem.c 2005-02-18 17:48:44.000000000 +0000
+@@ -61,7 +61,9 @@
+ extern int pm2fb_setup(char*);
+ extern int pm3fb_init(void);
+ extern int pm3fb_setup(char*);
++extern int clps711xfb_init(void);
+ extern int cyber2000fb_init(void);
++extern int cyber2000fb_setup(char*);
+ extern int retz3fb_init(void);
+ extern int retz3fb_setup(char*);
+ extern int clgenfb_init(void);
+@@ -107,6 +109,7 @@
+ extern int chips_init(void);
+ extern int g364fb_init(void);
+ extern int sa1100fb_init(void);
++extern int s3c2410fb_init(void);
+ extern int fm2fb_init(void);
+ extern int fm2fb_setup(char*);
+ extern int q40fb_init(void);
+@@ -145,6 +148,8 @@
+ extern int sstfb_setup(char*);
+ extern int it8181fb_init(void);
+ extern int it8181fb_setup(char*);
++extern int anakinfb_init(void);
++extern int dbmx1fb_init(void);
+
+ static struct {
+ const char *name;
+@@ -170,11 +175,14 @@
+ #ifdef CONFIG_FB_AMIGA
+ { "amifb", amifb_init, amifb_setup },
+ #endif
++#ifdef CONFIG_FB_CLPS711X
++ { "clps711xfb", clps711xfb_init, NULL },
++#endif
+ #ifdef CONFIG_FB_CYBER
+ { "cyber", cyberfb_init, cyberfb_setup },
+ #endif
+ #ifdef CONFIG_FB_CYBER2000
+- { "cyber2000", cyber2000fb_init, NULL },
++ { "cyber2000", cyber2000fb_init, cyber2000fb_setup },
+ #endif
+ #ifdef CONFIG_FB_PM2
+ { "pm2fb", pm2fb_init, pm2fb_setup },
+@@ -227,6 +235,9 @@
+ #ifdef CONFIG_FB_S3TRIO
+ { "s3trio", s3triofb_init, NULL },
+ #endif
++#ifdef CONFIG_FB_S3C2410
++ { "s3c2410", s3c2410fb_init, NULL },
++#endif
+ #ifdef CONFIG_FB_FM2
+ { "fm2fb", fm2fb_init, fm2fb_setup },
+ #endif
+@@ -307,6 +318,9 @@
+ #ifdef CONFIG_FB_TX3912
+ { "tx3912", tx3912fb_init, NULL },
+ #endif
++#ifdef CONFIG_FB_ANAKIN
++ { "anakinfb", anakinfb_init, NULL },
++#endif
+ #ifdef CONFIG_FB_E1355
+ { "e1355fb", e1355fb_init, e1355fb_setup },
+ #endif
+@@ -334,6 +348,9 @@
+ #ifdef CONFIG_FB_IT8181
+ { "it8181fb", it8181fb_init, it8181fb_setup },
+ #endif
++#ifdef CONFIG_FB_DBMX1
++ { "dbmx1fb", dbmx1fb_init, NULL },
++#endif
+
+
+ /*
+diff -urN kernel-source-2.4.27-8/drivers/video/font_acorn_8x8.c kernel-source-2.4.27-8-arm-1/drivers/video/font_acorn_8x8.c
+--- kernel-source-2.4.27-8/drivers/video/font_acorn_8x8.c 1998-09-30 04:56:33.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/font_acorn_8x8.c 2005-02-18 17:48:44.000000000 +0000
+@@ -11,30 +11,30 @@
+ /* 03 */ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* ^C */
+ /* 04 */ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* ^D */
+ /* 05 */ 0x00, 0x18, 0x3c, 0xe7, 0xe7, 0x3c, 0x18, 0x00, /* ^E */
+-/* 06 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 07 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 09 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0A */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0B */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0C */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0D */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0E */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0F */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* 06 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 07 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 08 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 09 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0A */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0B */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0C */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0D */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0E */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0F */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ /* 10 */ 0x00, 0x60, 0x78, 0x7e, 0x7e, 0x78, 0x60, 0x00, /* |> */
+ /* 11 */ 0x00, 0x06, 0x1e, 0x7e, 0x7e, 0x1e, 0x06, 0x00, /* <| */
+-/* 12 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 13 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 14 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 15 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 16 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 17 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 19 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 1A */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 1B */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 1C */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 1D */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* 12 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 13 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 14 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 15 */ 0x3c, 0x60, 0x3c, 0x66, 0x3c, 0x06, 0x3c, 0x00,
++/* 16 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 17 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 18 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 19 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 1A */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 1B */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 1C */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 1D */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ /* 1E */ 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x7e, 0x7e, 0x00, /* /\ */
+ /* 1F */ 0x00, 0x7e, 0x7e, 0x3c, 0x3c, 0x18, 0x18, 0x00, /* \/ */
+ /* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* */
+@@ -133,54 +133,54 @@
+ /* 7D */ 0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00, /* } */
+ /* 7E */ 0x31, 0x6B, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, /* ~ */
+ /* 7F */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* */
+-/* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 81 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 82 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 83 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 84 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 85 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 86 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 87 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 89 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8A */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8B */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8C */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8D */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8E */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8F */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 91 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 92 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 93 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 94 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 95 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 97 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 99 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9A */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9B */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9C */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9D */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9E */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9F */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A2 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A3 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A4 */ 0x00, 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,
+-/* A8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A9 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AA */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AB */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AC */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AD */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AE */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AF */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* 80 */ 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x30, 0x60,
++/* 81 */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
++/* 82 */ 0x0c, 0x18, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
++/* 83 */ 0x18, 0x66, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* 84 */ 0x66, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* 85 */ 0x30, 0x18, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* 86 */ 0x3c, 0x66, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* 87 */ 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x60,
++/* 88 */ 0x3c, 0x66, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
++/* 89 */ 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
++/* 8A */ 0x30, 0x18, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
++/* 8B */ 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
++/* 8C */ 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
++/* 8D */ 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
++/* 8E */ 0x66, 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x66, 0x00,
++/* 8F */ 0x18, 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x66, 0x00,
++/* 90 */ 0x0c, 0x18, 0x7e, 0x60, 0x7c, 0x60, 0x7e, 0x00,
++/* 91 */ 0x00, 0x00, 0x3f, 0x0d, 0x3f, 0x6c, 0x3f, 0x00,
++/* 92 */ 0x3f, 0x66, 0x66, 0x7f, 0x66, 0x66, 0x67, 0x00,
++/* 93 */ 0x3c, 0x66, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
++/* 94 */ 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
++/* 95 */ 0x30, 0x18, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
++/* 96 */ 0x3c, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
++/* 97 */ 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
++/* 98 */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c,
++/* 99 */ 0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
++/* 9A */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
++/* 9B */ 0x08, 0x3e, 0x6b, 0x68, 0x6b, 0x3e, 0x08, 0x00,
++/* 9C */ 0x1c, 0x36, 0x30, 0x7c, 0x30, 0x30, 0x7e, 0x00,
++/* 9D */ 0x66, 0x3c, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00,
++/* 9E */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 9F */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* A0 */ 0x0c, 0x18, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* A1 */ 0x0c, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
++/* A2 */ 0x0c, 0x18, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
++/* A3 */ 0x0c, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
++/* A4 */ 0x36, 0x6c, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x00,
++/* A5 */ 0x36, 0x6c, 0x00, 0x66, 0x76, 0x6e, 0x66, 0x00,
++/* A6 */ 0x1c, 0x06, 0x1e, 0x36, 0x1e, 0x00, 0x3e, 0x00,
++/* A7 */ 0x1c, 0x36, 0x36, 0x36, 0x1c, 0x00, 0x3e, 0x00,
++/* A8 */ 0x18, 0x00, 0x18, 0x18, 0x30, 0x66, 0x3c, 0x00,
++/* A9 */ 0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* AA */ 0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* AB */ 0x40, 0xc0, 0x40, 0x4f, 0x41, 0x0f, 0x08, 0x0f,
++/* AC */ 0x40, 0xc0, 0x40, 0x48, 0x48, 0x0a, 0x0f, 0x02,
++/* AD */ 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
++/* AE */ 0x00, 0x33, 0x66, 0xcc, 0xcc, 0x66, 0x33, 0x00,
++/* AF */ 0x00, 0xcc, 0x66, 0x33, 0x33, 0x66, 0xcc, 0x00,
+ /* B0 */ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
+ /* B1 */ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ /* B2 */ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+@@ -229,37 +229,37 @@
+ /* DD */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ /* DE */ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ /* DF */ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+-/* E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E2 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E3 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E4 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E9 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EA */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EB */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EC */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* ED */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EE */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EF */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F2 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F3 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F4 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F9 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FA */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FB */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FC */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FD */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FE */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* E0 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E1 */ 0x3c, 0x66, 0x66, 0x6c, 0x66, 0x66, 0x6c, 0xc0,
++/* E2 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E3 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E4 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E5 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E6 */ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x60,
++/* E7 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E8 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E9 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EA */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EB */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EC */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* ED */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EE */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EF */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F0 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F1 */ 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00,
++/* F2 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F3 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F4 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F5 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F6 */ 0x00, 0x18, 0x00, 0xff, 0x00, 0x18, 0x00, 0x00,
++/* F7 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F8 */ 0x3c, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* F9 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* FA */ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
++/* FB */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* FC */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* FD */ 0x38, 0x04, 0x18, 0x20, 0x3c, 0x00, 0x00, 0x00,
++/* FE */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ /* FF */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+diff -urN kernel-source-2.4.27-8/drivers/video/pm3fb.c kernel-source-2.4.27-8-arm-1/drivers/video/pm3fb.c
+--- kernel-source-2.4.27-8/drivers/video/pm3fb.c 2004-04-14 14:05:38.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/pm3fb.c 2005-02-18 17:48:44.000000000 +0000
+@@ -5,7 +5,6 @@
+ * Based on code written by:
+ * Sven Luther, <luther at dpt-info.u-strasbg.fr>
+ * Alan Hourihane, <alanh at fairlite.demon.co.uk>
+- * Russel King, <rmk at arm.linux.org.uk>
+ * Based on linux/drivers/video/skeletonfb.c:
+ * Copyright (C) 1997 Geert Uytterhoeven
+ * Based on linux/driver/video/pm2fb.c:
+@@ -16,14 +15,9 @@
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+- * $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $
++ * $Header: /home/pm3fb/pm3fb/pm3fb.c,v 1.139 2001/08/28 08:13:54 dolbeau Exp $
+ *
+ * CHANGELOG:
+- * Wed Nov 13 11:19:34 MET 2002, v 1.4.11C: option flatpanel: wasn't available in module, fixed.
+- * Mon Feb 11 10:35:48 MET 2002, v 1.4.11B: Cosmetic update.
+- * Wed Jan 23 14:16:59 MET 2002, v 1.4.11: Preliminary 2.5.x support, patch for 2.5.2.
+- * Wed Nov 28 11:08:29 MET 2001, v 1.4.10: potential bug fix for SDRAM-based board, patch for 2.4.16.
+- * Thu Sep 20 10:24:42 MET DST 2001, v 1.4.9: sync bug fix, preliminary flatpanel support, better timings.
+ * Tue Aug 28 10:13:01 MET DST 2001, v 1.4.8: memory timings check, minor bug fixes.
+ * Wed Jul 18 19:06:14 CEST 2001, v 1.4.7: Mode fix (800x600-100, 1024x768-100 changed), using HW panning + accel bug fix.
+ * Mon Jun 25 10:33:56 MET DST 2001, v 1.4.6: Depth 12 fix, chip reset ioctl, moved memory erase ioctl to DEBUG.
+@@ -54,8 +48,8 @@
+ */
+
+ #include <linux/config.h>
+-#include <linux/module.h>
+ #include <linux/version.h>
++#include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+@@ -129,7 +123,6 @@
+ unsigned long refresh;
+ unsigned long powerdown;
+ };
+-typedef enum pm3fb_timing_result { pm3fb_timing_ok, pm3fb_timing_problem, pm3fb_timing_retry } pm3fb_timing_result;
+ #define PM3FB_UNKNOWN_TIMING_VALUE ((unsigned long)-1)
+ #define PM3FB_UNKNOWN_TIMINGS { PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE }
+
+@@ -199,14 +192,6 @@
+ |
+ PM3VideoControl_VSYNC_ACTIVE_HIGH
+ | PM3VideoControl_PIXELSIZE_32BIT}},
+-/* Generated mode : "1600x1024", for the SGI 1600SW flat panel*/
+- {
+- "SGI1600SW", {
+- 108000, 1600, 1024, 16, 56, 104, 1704, 3, 6, 32,
+- 1056, 1600, 0, 8,
+- PM3VideoControl_ENABLE|
+- PM3VideoControl_HSYNC_ACTIVE_LOW|PM3VideoControl_VSYNC_ACTIVE_LOW|
+- PM3VideoControl_PIXELSIZE_32BIT}},
+ /* ##### auto-generated mode, by fbtimings2pm3 */
+ /* Generated mode : "640x480-60" */
+ {
+@@ -555,11 +540,9 @@
+ short noaccel[PM3_MAX_BOARD];
+ char fontn[PM3_MAX_BOARD][PM3_FONTNAME_SIZE];
+ short depth[PM3_MAX_BOARD];
+-short flatpanel[PM3_MAX_BOARD];
+ static struct display disp[PM3_MAX_BOARD];
+ static char g_options[PM3_OPTIONS_SIZE] __initdata = "pm3fb,dummy";
+ short printtimings = 0;
+-short forcesize[PM3_MAX_BOARD];
+
+ /* ********************* */
+ /* ***** prototype ***** */
+@@ -567,8 +550,7 @@
+ /* card-specific */
+ static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info);
+ /* permedia3-specific */
+-static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info);
+-static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info);
++static int pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info);
+ static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info);
+ static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info,
+ unsigned long r);
+@@ -684,7 +666,7 @@
+ NULL, NULL
+ };
+ #endif /* KERNEL_2_2 */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ struct fbgen_hwswitch pm3fb_switch = {
+ pm3fb_detect, pm3fb_encode_fix, pm3fb_decode_var, pm3fb_encode_var,
+ pm3fb_get_par, pm3fb_set_par, pm3fb_getcolreg, pm3fb_setcolreg,
+@@ -697,7 +679,7 @@
+ fbgen_get_fix, fbgen_get_var, fbgen_set_var,
+ fbgen_get_cmap, fbgen_set_cmap, fbgen_pan_display, pm3fb_ioctl, NULL, NULL
+ };
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+ #ifdef PM3FB_USE_ACCEL
+ #ifdef FBCON_HAS_CFB32
+ static struct display_switch pm3fb_cfb32 = {
+@@ -728,75 +710,52 @@
+ #endif /* FBCON_HAS_CFB8 */
+ #endif /* PM3FB_USE_ACCEL */
+
+-/* ****************************** */
+-/* ***** card-specific data ***** */
+-/* ****************************** */
+-struct pm3fb_card_timings {
+- unsigned long memsize; /* 0 for last value (i.e. default) */
+- struct pm3fb_timings memt;
+-};
+-
+-static struct pm3fb_card_timings t_FormacProFormance3[] = {
+- { 16, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} },
+- { 0, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} } /* from 16 MB PF3 */
+-};
+-
+-static struct pm3fb_card_timings t_AppianJeronimo2000[] = {
+- { 32, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} },
+- { 0, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} } /* from 32MB J2000 */
+-};
+-
+-static struct pm3fb_card_timings t_3DLabsOxygenVX1[] = {
+- { 32, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} },
+- { 0, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} } /* from 32MB VX1 */
+-};
+-
++/* ********************************** */
++/* ***** card-specific function ***** */
++/* ********************************** */
+ static struct {
+ char cardname[32]; /* recognized card name */
+ u16 subvendor; /* subvendor of the card */
+ u16 subdevice; /* subdevice of the card */
+ u8 func; /* function of the card to which the extra init apply */
+ void (*specific_setup)(struct pm3fb_info *l_fb_info); /* card/func specific setup, done before _any_ FB access */
+- struct pm3fb_card_timings *c_memt; /* defauls timings for the boards */
++ struct pm3fb_timings memt; /* default timing for the board WARNING : might be *card* - specific */
+ } cardbase[] = {
+- { "Unknown Permedia3 board", 0xFFFF, 0xFFFF, 0xFF, NULL, NULL },
+- { "Appian Jeronimo 2000 head 1", 0x1097, 0x3d32, 1, NULL,
+- t_AppianJeronimo2000
+- },
++ { "Unknown Permedia3 board", 0xFFFF, 0xFFFF, 0xFF, NULL, PM3FB_UNKNOWN_TIMINGS },
++ { "Appian Jeronimo 2000 head 1", 0x1097, 0x3d32, 1, NULL, PM3FB_UNKNOWN_TIMINGS },
+ { "Appian Jeronimo 2000 head 2", 0x1097, 0x3d32, 2, pm3fb_j2000_setup,
+- t_AppianJeronimo2000
++ {0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} /* also in pm3fb_j2000_setup */
+ },
+ { "Formac ProFormance 3", PCI_VENDOR_ID_3DLABS, 0x000a, 0, NULL, /* Formac use 3DLabs ID ?!? */
+- t_FormacProFormance3
++ { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} /* from the 16Mb ProFormance 3 */
+ },
+- { "3DLabs Permedia3 Create!", PCI_VENDOR_ID_3DLABS, 0x0127, 0, NULL, NULL },
++ { "3DLabs Permedia3 Create!", PCI_VENDOR_ID_3DLABS, 0x0127, 0, NULL, PM3FB_UNKNOWN_TIMINGS },
+ { "3DLabs Oxygen VX1 PCI", PCI_VENDOR_ID_3DLABS, 0x0121, 0, NULL,
+- t_3DLabsOxygenVX1
++ { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000 }
+ },
+- { "3DLabs Oxygen VX1 AGP", PCI_VENDOR_ID_3DLABS, 0x0125, 0, NULL, NULL },
+- { "3DLabs Oxygen VX1-16 AGP", PCI_VENDOR_ID_3DLABS, 0x0140, 0, NULL, NULL },
+- { "3DLabs Oxygen VX1-1600SW PCI", PCI_VENDOR_ID_3DLABS, 0x0800, 0, NULL, NULL },
+- { "\0", 0x0, 0x0, 0, NULL, NULL }
++ { "3DLabs Oxygen VX1 AGP", PCI_VENDOR_ID_3DLABS, 0x0125, 0, NULL, PM3FB_UNKNOWN_TIMINGS },
++ { "3DLabs Oxygen VX1-16 AGP", PCI_VENDOR_ID_3DLABS, 0x0140, 0, NULL, PM3FB_UNKNOWN_TIMINGS },
++ { "3DLabs Oxygen VX1-1600SW PCI", PCI_VENDOR_ID_3DLABS, 0x0800, 0, NULL, PM3FB_UNKNOWN_TIMINGS },
++ { "\0", 0x0, 0x0, 0, NULL, PM3FB_UNKNOWN_TIMINGS }
+ };
+
+-/* ********************************** */
+-/* ***** card-specific function ***** */
+-/* ********************************** */
+ static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info)
+ { /* the appian j2000 require more initialization of the second head */
+ /* l_fb_info must point to the _second_ head of the J2000 */
+
+ DTRACE;
+
+- l_fb_info->memt = t_AppianJeronimo2000[0].memt; /* 32 MB, first and only j2000 ? */
++ /* Memory timings for the Appian J2000 board. also in cardbase */
++ l_fb_info->memt.caps = 0x02e311B8;
++ l_fb_info->memt.timings = 0x07424905;
++ l_fb_info->memt.control = 0x0c000003;
++ l_fb_info->memt.refresh = 0x00000061;
++ l_fb_info->memt.powerdown = 0x00000000;
+
+ pm3fb_write_memory_timings(l_fb_info);
+ }
+
+-/* *************************************** */
+-/* ***** permedia3-specific function ***** */
+-/* *************************************** */
+-static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info)
++static int pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info)
+ {
+ l_fb_info->memt.caps = PM3_READ_REG(PM3LocalMemCaps);
+ l_fb_info->memt.timings = PM3_READ_REG(PM3LocalMemTimings);
+@@ -811,35 +770,20 @@
+ (l_fb_info->memt.powerdown == PM3FB_UNKNOWN_TIMING_VALUE))
+ {
+ printk(KERN_ERR "pm3fb: invalid memory timings in permedia3 board #%ld\n", l_fb_info->board_num);
+- return(pm3fb_try_memory_timings(l_fb_info));
+- }
+- return(pm3fb_timing_ok);
+-}
+-
+-static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info)
+-{
+- if (cardbase[l_fb_info->board_type].c_memt)
+- {
+- int i = 0, done = 0;
+- while (!done)
++ if ((cardbase[l_fb_info->board_type].memt.caps != PM3FB_UNKNOWN_TIMING_VALUE) &&
++ (cardbase[l_fb_info->board_type].memt.timings != PM3FB_UNKNOWN_TIMING_VALUE) &&
++ (cardbase[l_fb_info->board_type].memt.control != PM3FB_UNKNOWN_TIMING_VALUE) &&
++ (cardbase[l_fb_info->board_type].memt.refresh != PM3FB_UNKNOWN_TIMING_VALUE) &&
++ (cardbase[l_fb_info->board_type].memt.powerdown != PM3FB_UNKNOWN_TIMING_VALUE))
+ {
+- if ((cardbase[l_fb_info->board_type].c_memt[i].memsize == l_fb_info->fb_size)
+- || !(cardbase[l_fb_info->board_type].c_memt[i].memsize))
+- { /* will use the 0-sized timings by default */
+- done = 1;
+- l_fb_info->memt = cardbase[l_fb_info->board_type].c_memt[i].memt;
+- printk(KERN_WARNING "pm3fb: trying to use predefined memory timings for permedia3 board #%ld (%s, %ld MB)\n",
+- l_fb_info->board_num,
+- cardbase[l_fb_info->board_type].cardname,
+- cardbase[l_fb_info->board_type].c_memt[i].memsize);
++ l_fb_info->memt = cardbase[l_fb_info->board_type].memt;
++ printk(KERN_WARNING "pm3fb: trying to use predefined memory timings for permedia3 board #%ld (%s)\n", l_fb_info->board_num, cardbase[l_fb_info->board_type].cardname);
+ pm3fb_write_memory_timings(l_fb_info);
+- return(pm3fb_timing_retry);
+ }
+- i++;
++ else
++ return(1);
+ }
+- } else
+- return(pm3fb_timing_problem);
+- return(pm3fb_timing_ok);
++ return(0);
+ }
+
+ static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info)
+@@ -874,6 +818,10 @@
+ PM3RD_SClkControl_ENABLE);
+ }
+
++/* *************************************** */
++/* ***** permedia3-specific function ***** */
++/* *************************************** */
++
+ static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info,
+ unsigned long r)
+ {
+@@ -1068,7 +1016,6 @@
+ /* write the mode to registers */
+ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info)
+ {
+- char tempsync = 0x00, tempmisc = 0x00;
+ DTRACE;
+
+ PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, 0xffffffff);
+@@ -1198,25 +1145,21 @@
+ /*
+ PM3_SLOW_WRITE_REG(PM3RD_IndexControl, 0x00);
+ */
+- if ((l_fb_info->current_par->video & PM3VideoControl_HSYNC_MASK) ==
++ {
++ char tempsync = 0x00;
++
++ if ((l_fb_info->current_par->
++ video & PM3VideoControl_HSYNC_MASK) ==
+ PM3VideoControl_HSYNC_ACTIVE_HIGH)
+ tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH;
+- if ((l_fb_info->current_par->video & PM3VideoControl_VSYNC_MASK) ==
++ if ((l_fb_info->current_par->
++ video & PM3VideoControl_VSYNC_MASK) ==
+ PM3VideoControl_VSYNC_ACTIVE_HIGH)
+ tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH;
+
+ PM3_WRITE_DAC_REG(PM3RD_SyncControl, tempsync);
+ DPRINTK(2, "PM3RD_SyncControl: %d\n", tempsync);
+-
+- if (flatpanel[l_fb_info->board_num])
+- {
+- PM3_WRITE_DAC_REG(PM3RD_DACControl, PM3RD_DACControl_BLANK_PEDESTAL_ENABLE);
+- PM3_WAIT(2);
+- PM3_WRITE_REG(PM3VSConfiguration, 0x06);
+- PM3_WRITE_REG(0x5a00, 1 << 14); /* black magic... */
+- tempmisc = PM3RD_MiscControl_VSB_OUTPUT_ENABLE;
+ }
+- else
+ PM3_WRITE_DAC_REG(PM3RD_DACControl, 0x00);
+
+ switch (l_fb_info->current_par->depth) {
+@@ -1226,7 +1169,8 @@
+ PM3_WRITE_DAC_REG(PM3RD_ColorFormat,
+ PM3RD_ColorFormat_CI8_COLOR |
+ PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
+- tempmisc |= PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++ PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++ PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+ break;
+ case 12:
+ PM3_WRITE_DAC_REG(PM3RD_PixelSize,
+@@ -1235,8 +1179,9 @@
+ PM3RD_ColorFormat_4444_COLOR |
+ PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
+ PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
+- tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
+- PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++ PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++ PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
++ PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+ break;
+ case 15:
+ PM3_WRITE_DAC_REG(PM3RD_PixelSize,
+@@ -1245,8 +1190,9 @@
+ PM3RD_ColorFormat_5551_FRONT_COLOR |
+ PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
+ PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
+- tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
+- PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++ PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++ PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
++ PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+ break;
+ case 16:
+ PM3_WRITE_DAC_REG(PM3RD_PixelSize,
+@@ -1255,8 +1201,9 @@
+ PM3RD_ColorFormat_565_FRONT_COLOR |
+ PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
+ PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
+- tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
+- PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++ PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++ PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
++ PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+ break;
+ case 32:
+ PM3_WRITE_DAC_REG(PM3RD_PixelSize,
+@@ -1264,11 +1211,11 @@
+ PM3_WRITE_DAC_REG(PM3RD_ColorFormat,
+ PM3RD_ColorFormat_8888_COLOR |
+ PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
+- tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
+- PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++ PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++ PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
++ PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+ break;
+ }
+- PM3_WRITE_DAC_REG(PM3RD_MiscControl, tempmisc);
+
+ PM3_SHOW_CUR_MODE;
+ }
+@@ -1391,9 +1338,8 @@
+
+ static unsigned long pm3fb_size_memory(struct pm3fb_info *l_fb_info)
+ {
+- unsigned long memsize = 0, tempBypass, i, temp1, temp2;
++ unsigned long memsize, tempBypass, i, temp1, temp2;
+ u16 subvendor, subdevice;
+- pm3fb_timing_result ptr;
+
+ DTRACE;
+
+@@ -1439,7 +1385,7 @@
+
+ /* card-specific setup is done, we preserve the final
+ memory timing for future reference */
+- if ((ptr = pm3fb_preserve_memory_timings(l_fb_info)) == pm3fb_timing_problem) { /* memory timings were wrong ! oops.... */
++ if (pm3fb_preserve_memory_timings(l_fb_info)) { /* memory timings were wrong ! oops.... */
+ return(0);
+ }
+
+@@ -1465,12 +1411,12 @@
+ temp1 = readl((l_fb_info->v_fb + (i * 1048576)));
+ #endif
+ #endif /* KERNEL_2_2 */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ fb_writel(i * 0x00345678,
+ (l_fb_info->v_fb + (i * 1048576)));
+ mb();
+ temp1 = fb_readl((l_fb_info->v_fb + (i * 1048576)));
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+ /* Let's check for wrapover, write will fail at 16MB boundary */
+ if (temp1 == (i * 0x00345678))
+ memsize = i;
+@@ -1513,7 +1459,7 @@
+ ((i - 32) * 1048576)));
+ #endif
+ #endif /* KERNEL_2_2 */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ fb_writel(i * 0x00345678,
+ (l_fb_info->v_fb + (i * 1048576)));
+ mb();
+@@ -1522,7 +1468,7 @@
+ temp2 =
+ fb_readl((l_fb_info->v_fb +
+ ((i - 32) * 1048576)));
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+ if ((temp1 == (i * 0x00345678)) && (temp2 == 0)) /* different value, different RAM... */
+ memsize = i;
+ else
+@@ -1539,21 +1485,8 @@
+
+ DPRINTK(2, "Returning 0x%08lx bytes\n", memsize);
+
+- if (forcesize[l_fb_info->board_num] && ((forcesize[l_fb_info->board_num] * 1048576) != memsize))
+- {
+- printk(KERN_WARNING "pm3fb: mismatch between probed (%ld MB) and specified (%hd MB) memory size, using SPECIFIED !\n", memsize, forcesize[l_fb_info->board_num]);
+- memsize = 1048576 * forcesize[l_fb_info->board_num];
+- }
+-
+ l_fb_info->fb_size = memsize;
+
+- if (ptr == pm3fb_timing_retry)
+- {
+- printk(KERN_WARNING "pm3fb: retrying memory timings check");
+- if (pm3fb_try_memory_timings(l_fb_info) == pm3fb_timing_problem)
+- return(0);
+- }
+-
+ return (memsize);
+ }
+
+@@ -1572,7 +1505,7 @@
+ writel(cc, (l_fb_info->v_fb + (i * sizeof(u32))));
+ #endif
+ #endif
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ fb_writel(cc, (l_fb_info->v_fb + (i * sizeof(u32))));
+ #endif
+ }
+@@ -1601,7 +1534,7 @@
+ disp[l_fb_info->board_num].scrollmode = 0; /* SCROLL_YNOMOVE; *//* 0 means "let fbcon choose" */
+ l_fb_info->gen.parsize = sizeof(struct pm3fb_par);
+ l_fb_info->gen.info.changevar = NULL;
+- l_fb_info->gen.info.node = B_FREE;
++ l_fb_info->gen.info.node = -1;
+ l_fb_info->gen.info.fbops = &pm3fb_ops;
+ l_fb_info->gen.info.disp = &(disp[l_fb_info->board_num]);
+ if (fontn[l_fb_info->board_num][0])
+@@ -1766,7 +1699,6 @@
+ }
+
+ PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xffffffff);
+- PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xffffffff);
+ PM3_SLOW_WRITE_REG(PM3FBWriteMode,
+ PM3FBWriteMode_WriteEnable |
+ PM3FBWriteMode_OpaqueSpan |
+@@ -1788,6 +1720,8 @@
+ else
+ PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, sofb);
+
++ PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xffffffff);
++
+ switch (l_fb_info->current_par->depth) {
+ case 8:
+ PM3_SLOW_WRITE_REG(PM3DitherMode,
+@@ -1843,10 +1777,7 @@
+ height = height * fontheight(p);
+ c = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)];
+
+- /* block fills in 32bpp are hard, but in low res (width <= 1600 :-)
+- we can use 16bpp operations, but not if NoWriteMask is on (SDRAM) */
+- if ((l_fb_info->current_par->width > 1600) ||
+- (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)) {
++ if (l_fb_info->current_par->width > 1600) {
+ PM3_WAIT(4);
+
+ PM3_WRITE_REG(PM3Config2D,
+@@ -1868,7 +1799,7 @@
+ PM3Render2D_SpanOperation |
+ (PM3Render2D_Width(width)) |
+ (PM3Render2D_Height(height)));
+- } else {
++ } else { /* block fills in 32bpp are hard, but in low res (width <= 1600 :-) we can use 16bpp operations */
+ PM3_WAIT(8);
+
+ PM3_WRITE_REG(PM3FBBlockColor, c);
+@@ -1993,9 +1924,6 @@
+
+ PM3_WAIT(4);
+
+- if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+- PM3_WRITE_REG(PM3ForegroundColor, c);
+- else
+ PM3_WRITE_REG(PM3FBBlockColor, c);
+
+ PM3_WRITE_REG(PM3Config2D,
+@@ -2008,15 +1936,6 @@
+ (PM3RectanglePosition_XOffset(sx)) |
+ (PM3RectanglePosition_YOffset(sy)));
+
+- if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+- PM3_WRITE_REG(PM3Render2D,
+- PM3Render2D_XPositive |
+- PM3Render2D_YPositive |
+- PM3Render2D_Operation_Normal |
+- PM3Render2D_SpanOperation |
+- (PM3Render2D_Width(width)) |
+- (PM3Render2D_Height(height)));
+- else
+ PM3_WRITE_REG(PM3Render2D,
+ PM3Render2D_XPositive |
+ PM3Render2D_YPositive |
+@@ -2050,9 +1969,6 @@
+ (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */
+ PM3Config2D_FBWriteEnable);
+
+- if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+- PM3_WRITE_REG(PM3ForegroundColor, c);
+- else
+ PM3_WRITE_REG(PM3FBBlockColor, c);
+
+ PM3_WRITE_REG(PM3RectanglePosition,
+@@ -2061,15 +1977,7 @@
+ sx)) | (PM3RectanglePosition_YOffset(p->
+ var.
+ yoffset)));
+- if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+- PM3_WRITE_REG(PM3Render2D,
+- PM3Render2D_XPositive |
+- PM3Render2D_YPositive |
+- PM3Render2D_Operation_Normal |
+- PM3Render2D_SpanOperation |
+- (PM3Render2D_Width(p->var.xres - sx)) |
+- (PM3Render2D_Height(p->var.yres)));
+- else
++
+ PM3_WRITE_REG(PM3Render2D,
+ PM3Render2D_XPositive |
+ PM3Render2D_YPositive |
+@@ -2087,25 +1995,12 @@
+ (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */
+ PM3Config2D_FBWriteEnable);
+
+- if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+- PM3_WRITE_REG(PM3ForegroundColor, c);
+- else
+ PM3_WRITE_REG(PM3FBBlockColor, c);
+
+-
+ PM3_WRITE_REG(PM3RectanglePosition,
+ (PM3RectanglePosition_XOffset(p->var.xoffset)) |
+ (PM3RectanglePosition_YOffset(p->var.yoffset + sy)));
+
+- if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+- PM3_WRITE_REG(PM3Render2D,
+- PM3Render2D_XPositive |
+- PM3Render2D_YPositive |
+- PM3Render2D_Operation_Normal |
+- PM3Render2D_SpanOperation |
+- (PM3Render2D_Width(p->var.xres)) |
+- (PM3Render2D_Height(p->var.yres - sy)));
+- else
+ PM3_WRITE_REG(PM3Render2D,
+ PM3Render2D_XPositive |
+ PM3Render2D_YPositive |
+@@ -2289,7 +2184,7 @@
+ int c, int yy, int xx)
+ {
+ struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info;
+- u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0;
++ u8 *cdat, asx = 0, asy = 0, o_x, o_y;
+ u32 fgx, bgx, ldat;
+ int sx, sy, i;
+
+@@ -2399,7 +2294,7 @@
+ int xx)
+ {
+ struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info;
+- u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0;
++ u8 *cdat, asx = 0, asy = 0, o_x, o_y;
+ u32 fgx, bgx, ldat;
+ int sx, sy, i, j;
+ u16 sc;
+@@ -2517,12 +2412,7 @@
+ yy = yy * fontheight(p);
+
+ if (l_fb_info->current_par->depth == 8)
+- {
+- if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+- PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0x0F0F0F0F);
+- else
+ PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0x0F0F0F0F);
+- }
+
+ PM3_WAIT(3);
+
+@@ -2548,12 +2438,7 @@
+ pm3fb_wait_pm3(l_fb_info);
+
+ if (l_fb_info->current_par->depth == 8)
+- {
+- if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+- PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xFFFFFFFF);
+- else
+ PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xFFFFFFFF);
+- }
+ }
+
+ #endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB16 || FBCON_HAS_CFB32 */
+@@ -2641,25 +2526,12 @@
+ unsigned long bd = simple_strtoul(bds, (char **) NULL, 10);
+
+ if (!(depth_supported(bd))) {
+- printk(KERN_WARNING "pm3fb: ignoring invalid depth %s for board #%ld\n",
+- bds, board_num);
++ DPRINTK(1, "Invalid depth: %s\n", bds);
+ return;
+ }
+ depth[board_num] = bd;
+ }
+
+-static void pm3fb_forcesize_setup(char *bds, unsigned long board_num)
+-{
+- unsigned long bd = simple_strtoul(bds, (char **) NULL, 10);
+-
+- if (bd > 64) {
+- printk(KERN_WARNING "pm3fb: ignoring invalid memory size %s for board #%ld\n",
+- bds, board_num);
+- return;
+- }
+- forcesize[board_num] = bd;
+-}
+-
+ static char *pm3fb_boardnum_setup(char *options, unsigned long *bn)
+ {
+ char *next;
+@@ -2753,12 +2625,6 @@
+ pm3fb_bootdepth_setup(options, bn);
+ } else if (!strncmp(options, "printtimings", 12)) {
+ printtimings = 1;
+- } else if (!strncmp(options, "flatpanel:", 10)) {
+- options = pm3fb_boardnum_setup(options + 10, &bn);
+- flatpanel[bn] = 1;
+- } else if (!strncmp(options, "forcesize:", 10)) {
+- options = pm3fb_boardnum_setup(options + 10, &bn);
+- pm3fb_forcesize_setup(options, bn);
+ }
+ options = next;
+ }
+@@ -3496,7 +3362,7 @@
+ pci_resource_start(l_fb_info->dev, 1);
+ l_fb_info->v_fb = (unsigned char *) -1;
+
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5) /* full resource management, new in linux-2.4.x */
++#ifdef KERNEL_2_4 /* full resource management, new in linux-2.4.x */
+ if (!request_mem_region
+ ((unsigned long)l_fb_info->p_fb, 64 * 1024 * 1024, /* request full aperture size */
+ "pm3fb")) {
+@@ -3513,9 +3379,7 @@
+ l_fb_info->board_num);
+ continue;
+ }
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
+- if (forcesize[l_fb_info->board_num])
+- l_fb_info->fb_size = forcesize[l_fb_info->board_num];
++#endif /* KERNEL_2_4 */
+
+ l_fb_info->fb_size =
+ pm3fb_size_memory(l_fb_info);
+@@ -3612,7 +3476,7 @@
+ /* ***** standard FB API init functions ***** */
+ /* ****************************************** */
+
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ int __init pm3fb_setup(char *options)
+ #endif
+ #ifdef KERNEL_2_2
+@@ -3628,12 +3492,12 @@
+ PM3_OPTIONS_SIZE) ? PM3_OPTIONS_SIZE : (opsi + 1));
+ g_options[PM3_OPTIONS_SIZE - 1] = 0;
+
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ return (0);
+ #endif
+ }
+
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ int __init pm3fb_init(void)
+ #endif
+ #ifdef KERNEL_2_2
+@@ -3642,7 +3506,7 @@
+ {
+ DTRACE;
+
+- DPRINTK(2, "This is pm3fb.c, CVS version: $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $");
++ DPRINTK(2, "This is pm3fb.c, CVS version: $Header: /home/pm3fb/pm3fb/pm3fb.c,v 1.139 2001/08/28 08:13:54 dolbeau Exp $");
+
+ pm3fb_real_setup(g_options);
+
+@@ -3651,7 +3515,7 @@
+ if (!fb_info[0].dev) { /* not even one board ??? */
+ DPRINTK(1, "No PCI Permedia3 board detected\n");
+ }
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ return (0);
+ #endif
+ }
+@@ -3754,11 +3618,7 @@
+ MODULE_PARM(depth,PM3_MAX_BOARD_MODULE_ARRAY_SHORT);
+ MODULE_PARM_DESC(depth,"boot-time depth");
+ MODULE_PARM(printtimings, "h");
+-MODULE_PARM_DESC(printtimings, "print the memory timings of the card(s)");
+-MODULE_PARM(forcesize, PM3_MAX_BOARD_MODULE_ARRAY_SHORT);
+-MODULE_PARM_DESC(forcesize, "force specified memory size");
+-MODULE_PARM(flatpanel, PM3_MAX_BOARD_MODULE_ARRAY_SHORT);
+-MODULE_PARM_DESC(flatpanel, "flatpanel (LCD) support (preliminary)");
++MODULE_PARM_DESC(printtimings, "print the memory timngs of the card(s)");
+ /*
+ MODULE_SUPPORTED_DEVICE("Permedia3 PCI boards")
+ MODULE_GENERIC_TABLE(gtype,name)
+@@ -3803,11 +3663,6 @@
+ sprintf(ts, ",depth:%d:%d", i, depth[i]);
+ strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options));
+ }
+- if (flatpanel[i])
+- {
+- sprintf(ts, ",flatpanel:%d:", i);
+- strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options));
+- }
+ }
+ g_options[PM3_OPTIONS_SIZE - 1] = '\0';
+ DPRINTK(1, "pm3fb use options: %s\n", g_options);
+@@ -3837,14 +3692,14 @@
+ if (l_fb_info->vIOBase !=
+ (unsigned char *) -1) {
+ pm3fb_unmapIO(l_fb_info);
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ release_mem_region(l_fb_info->p_fb,
+ l_fb_info->
+ fb_size);
+ release_mem_region(l_fb_info->
+ pIOBase,
+ PM3_REGS_SIZE);
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+ }
+ unregister_framebuffer(&l_fb_info->gen.
+ info);
+diff -urN kernel-source-2.4.27-8/drivers/video/pm3fb.h kernel-source-2.4.27-8-arm-1/drivers/video/pm3fb.h
+--- kernel-source-2.4.27-8/drivers/video/pm3fb.h 2002-11-28 23:53:15.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/video/pm3fb.h 2005-02-18 17:48:44.000000000 +0000
+@@ -8,7 +8,7 @@
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+- * $Header: /cvsroot/linux/drivers/video/pm3fb.h,v 1.1 2002/02/25 19:11:06 marcelo Exp $
++ * $Header: /home/pm3fb/pm3fb/pm3fb.h,v 1.30 2001/08/22 09:13:46 dolbeau Exp $
+ *
+ */
+
+@@ -92,7 +92,6 @@
+ #define PM3MemBypassWriteMask 0x1008
+ #define PM3MemScratch 0x1010
+ #define PM3LocalMemCaps 0x1018
+- #define PM3LocalMemCaps_NoWriteMask (1 << 28)
+ #define PM3LocalMemTimings 0x1020
+ #define PM3LocalMemControl 0x1028
+ #define PM3LocalMemRefresh 0x1030
+@@ -1121,10 +1120,6 @@
+
+ /* kernel -specific definitions */
+ /* what kernel is this ? */
+-#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)))
+-#define KERNEL_2_5
+-#endif
+-
+ #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)))
+ #define KERNEL_2_4
+ #endif
+@@ -1138,8 +1133,8 @@
+ #endif
+ #endif
+
+-#if (!defined(KERNEL_2_2)) && (!defined(KERNEL_2_4)) && (!defined(KERNEL_2_5))
+-#error "Only kernel 2.2.x, kernel 2.4.y and kernel 2.5.z might work"
++#if (!defined(KERNEL_2_2)) && (!defined(KERNEL_2_4))
++#error "Only kernel 2.2.x and kernel 2.4.y might work"
+ #endif
+
+ /* not sure if/why it's needed. doesn't work without on my PowerMac... */
+@@ -1147,11 +1142,6 @@
+ #define MUST_BYTESWAP
+ #endif
+
+-/* for compatibility between 2.5, 2.4 and 2.2 */
+-#ifndef B_FREE
+-#define B_FREE -1
+-#endif
+-
+ /* permedia3 -specific definitions */
+ #define PM3_SCALE_TO_CLOCK(pr, fe, po) ((2 * PM3_REF_CLOCK * fe) / (pr * (1 << (po))))
+ #define PICOS2KHZ(a) (1000000000UL/(a))
+@@ -1219,10 +1209,10 @@
+ #define PM3_READ_REG(r) readl((l_fb_info->vIOBase + r))
+ #endif /* MUST_BYTESWAP */
+ #endif /* KERNEL_2_2 */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5) /* native-endian access */
++#ifdef KERNEL_2_4 /* native-endian access */
+ #define PM3_WRITE_REG(r, v) fb_writel(v, (l_fb_info->vIOBase + r))
+ #define PM3_READ_REG(r) fb_readl((l_fb_info->vIOBase + r))
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+
+
+ #define depth2bpp(d) ((d + 7L) & ~7L)
+diff -urN kernel-source-2.4.27-8/drivers/video/s3c2410fb.c kernel-source-2.4.27-8-arm-1/drivers/video/s3c2410fb.c
+--- kernel-source-2.4.27-8/drivers/video/s3c2410fb.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/s3c2410fb.c 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,1348 @@
++/*
++ * linux/drivers/video/s3c2410fb.c
++ *
++ * Modifications for machine bast:
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ *
++ * CopyRight (C) 2002 SAMSUNG ELECTRONICS
++ * SW.LEE <hitchcar at sec.samsung.com>
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.s
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/fb.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/init.h>
++#include <linux/cpufreq.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++#include <asm/debug.h>
++
++#include <video/fbcon.h>
++#include <video/fbcon-mfb.h>
++#include <video/fbcon-cfb4.h>
++#include <video/fbcon-cfb8.h>
++#include <video/fbcon-cfb16.h>
++
++#include <asm/arch-s3c2410/S3C2410-gpio.h>
++#include <asm/arch-s3c2410/S3C2410-lcd.h>
++
++/*
++ * enable this if your panel appears to have broken
++ */
++#undef CHECK_COMPAT
++
++/*
++ * debugging?
++ */
++#define DEBUG 0
++/*
++ * Complain if VAR is out of range.
++ */
++#define DEBUG_VAR 0
++
++
++#include "s3c2410fb.h"
++
++void (*s3c2410fb_blank_helper)(int blank);
++/* EXPORT_SYMBOL(s3c2410fb_blank_helper); */
++
++
++/*
++ * IMHO this looks wrong. In 8BPP, length should be 8.
++ */
++static struct s3c2410fb_rgb rgb_8 = {
++ red: { offset: 0, length: 4, },
++ green: { offset: 0, length: 4, },
++ blue: { offset: 0, length: 4, },
++ transp: { offset: 0, length: 0, },
++};
++
++/* S3C2410
++ * 256 Palette Usage (TFT)
++ * 256 color palette consist of 256(depth) * 16 bit SPSRAM
++ */
++
++static struct s3c2410fb_rgb def_rgb_16 = {
++ red: { offset: 11, length: 5, },
++ green: { offset: 5, length: 6, },
++ blue: { offset: 0, length: 5, },
++ transp: { offset: 0, length: 0, },
++};
++
++#define CONFIG_MODE_VGA640
++
++
++/*******
++ * role : Fill Machine dependant data ,according to STN or TFT
++ *
++ */
++
++
++#if 0
++ { /* 640x480 @ 60Hz, 31.5 kHz hsync */
++ NULL, 60, 640, 480, 39722, 48, 16, 32, 11, 96, 2,
++ 0,
++ FB_VMODE_NONINTERLACED
++ }, { /* 800x600 @ 56Hz, 35.2 kHz hsync */
++ NULL, 56, 800, 600, 27778, 101, 23, 22, 1, 100, 2,
++ 0,
++ FB_VMODE_NONINTERLACED
++ }
++#endif
++
++
++static void __init s3c2410fb_get_machine_info(struct s3c2410fb_info *fbi)
++{
++
++ static struct s3c2410fb_mach_info inf __initdata = {
++#if defined(CONFIG_MODE_VGA640)
++ pixclock: 0, bpp: 8,
++ xres: 640, yres: 480,
++ hsync_len: 96, vsync_len: 2,
++ left_margin: 48, right_margin: 16,
++ lower_margin: 11, upper_margin: 32,
++ sync: FB_ACTIVATE_NOW
++#else
++#if defined(TFT240_320)
++ pixclock: 0, bpp: 16,
++ xres: 240, yres: 320,
++#elif defined(CONFIG_SOMETHING_OHTERS) /* Not Tested .. */
++ pixclock: 0, bpp: 8,
++ xres: 320, yres: 240,
++#endif
++ hsync_len: 0, vsync_len: 0,
++ left_margin: 0, upper_margin: 0,
++ right_margin: 0, lower_margin: 0,
++
++ sync: FB_ACTIVATE_NOW , /* value 0 */
++#endif
++ };
++
++ fbi->max_xres = inf.xres;
++ fbi->fb.var.xres = inf.xres;
++ fbi->fb.var.xres_virtual = inf.xres;
++ fbi->max_yres = inf.yres;
++ fbi->fb.var.yres = inf.yres;
++ fbi->fb.var.yres_virtual = inf.yres;
++ fbi->max_bpp = inf.bpp;
++ fbi->fb.var.bits_per_pixel = inf.bpp;
++ fbi->fb.var.pixclock = inf.pixclock;
++ fbi->fb.var.hsync_len = inf.hsync_len;
++ fbi->fb.var.left_margin = inf.left_margin;
++ fbi->fb.var.right_margin = inf.right_margin;
++ fbi->fb.var.vsync_len = inf.vsync_len;
++ fbi->fb.var.upper_margin = inf.upper_margin;
++ fbi->fb.var.lower_margin = inf.lower_margin;
++ fbi->fb.var.sync = inf.sync;
++ fbi->fb.var.grayscale = inf.cmap_greyscale;
++ fbi->cmap_inverse = inf.cmap_inverse;
++ fbi->cmap_static = inf.cmap_static;
++}
++
++static void s3c2410fb_lcd_port_init(void );
++static int s3c2410fb_activate_var(struct fb_var_screeninfo *var, struct s3c2410fb_info *);
++static void set_ctrlr_state(struct s3c2410fb_info *fbi, u_int state);
++
++
++#define FR_WIDTH 240
++#define FR_HEIGHT 320
++struct FrameBuffer {
++ unsigned short pixel[FR_HEIGHT][FR_WIDTH];
++};
++struct FrameBuffer *FBuf;
++
++#ifdef YOU_WANT_TO_DRAW_TETRAGON
++static void lcd_demo(void)
++{
++ // Test LCD Initialization. by displaying R.G.B and White.
++ int i,j;
++ for(i=0 ;i<FR_HEIGHT/2;i++)
++ {
++ for(j=0;j<FR_WIDTH;j++)
++ {
++ if(j<FR_WIDTH/2)
++ FBuf->pixel[i][j]= 0xffff;
++ else
++ FBuf->pixel[i][j]= 0xf800;
++ }
++ }
++ for(i=FR_HEIGHT/2 ;i<FR_HEIGHT;i++)
++ {
++ for(j=0;j<FR_WIDTH;j++)
++ {
++ if(j<FR_WIDTH/2)
++ FBuf->pixel[i][j]= 0x07e0;
++ else
++ FBuf->pixel[i][j]= 0x001f;
++ }
++ }
++}
++#endif
++
++
++static void s3c2410fb_lcd_port_init(void)
++{
++ int i;
++
++ llprintk("setting gpio lines...\n");
++
++ if (!machine_is_bast() || 1) {
++ /* disable the pull-up resistors on the LCD signal pins,
++ * and ensure they are setup correctly for the case we've
++ * not been through a bootloader, though we do check
++ * to see if they are likely to have been configured,
++ * to keep any bootloader set values.
++ */
++
++ llprintk(__FUNCTION__ ": portc=%08x,%04x portd=%08x,%04x\n",
++ __raw_readl(S3C2410_GPCCON), __raw_readl(S3C2410_GPCUP),
++ __raw_readl(S3C2410_GPDCON), __raw_readl(S3C2410_GPDUP));
++
++ if (__raw_readl(S3C2410_GPDCON) == 0) {
++ __raw_writel(0xffff, S3C2410_GPCUP);
++ __raw_writel(0xffff, S3C2410_GPDUP);
++
++ //Initialize VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
++ __raw_writel(0xaaaaaaaa, S3C2410_GPCCON);
++ __raw_writel(0xaaaaaaaa, S3C2410_GPDCON); //Initialize VD[23:8]
++ }
++ }
++
++
++ /* temporary palette init */
++
++ for (i = 1; i < 256; i++) {
++ unsigned int ptr = S3C2410_VA_LCD + 0x400 + (i*4);
++
++ __raw_writel(0xffffff, ptr);
++ __raw_writel(0xffffff, ptr);
++ __raw_writel(0xffffff, ptr);
++ }
++}
++
++
++static inline void s3c2410fb_schedule_task(struct s3c2410fb_info *fbi, u_int state)
++{
++ unsigned long flags;
++
++ local_irq_save(flags);
++ /*
++ * We need to handle two requests being made at the same time.
++ * There are two important cases:
++ * 1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE)
++ * We must perform the unblanking, which will do our REENABLE for us.
++ * 2. When we are blanking, but immediately unblank before we have
++ * blanked. We do the "REENABLE" thing here as well, just to be sure.
++ */
++ if (fbi->task_state == C_ENABLE && state == C_REENABLE)
++ state = (u_int) -1;
++ if (fbi->task_state == C_DISABLE && state == C_ENABLE)
++ state = C_REENABLE;
++
++ if (state != (u_int)-1) {
++ fbi->task_state = state;
++ schedule_task(&fbi->task);
++ }
++ local_irq_restore(flags);
++}
++
++/*
++ * Get the VAR structure pointer for the specified console
++ */
++static inline struct fb_var_screeninfo *get_con_var(struct fb_info *info, int con)
++{
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ return (con == fbi->currcon || con == -1) ? &fbi->fb.var : &fb_display[con].var;
++}
++
++/*
++ * Get the DISPLAY structure pointer for the specified console
++ *
++ * struct display fb_display[MAX_NR_CONSOLES]; from fbcon.c
++ */
++
++static inline struct display *get_con_display(struct fb_info *info, int con)
++{
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ return (con < 0) ? fbi->fb.disp : &fb_display[con];
++}
++
++/*
++ * Get the CMAP pointer for the specified console
++ *
++ * struct fb_cmap {
++ * __u32 start; First entry
++ * __u32 len; Number of entries
++ * __u16 *red; Red values
++ * __u16 *green;
++ * __u16 *blue;
++ * __u16 *transp; transparency, can be NULL
++ * };
++ *
++ */
++static inline struct fb_cmap *get_con_cmap(struct fb_info *info, int con)
++{
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ return (con == fbi->currcon || con == -1) ? &fbi->fb.cmap : &fb_display[con].cmap;
++}
++
++static inline u_int
++chan_to_field(u_int chan, struct fb_bitfield *bf)
++{
++ chan &= 0xffff;
++ chan >>= 16 - bf->length;
++ return chan << bf->offset;
++}
++
++/*
++ * Convert bits-per-pixel to a hardware palette PBS value.
++ */
++static inline u_int
++palette_pbs(struct fb_var_screeninfo *var)
++{
++ int ret = 0;
++ switch (var->bits_per_pixel) {
++#ifdef FBCON_HAS_CFB4
++ case 4: ret = 0 << 12; break;
++#endif
++#ifdef FBCON_HAS_CFB8
++ case 8: ret = 1 << 12; break;
++#endif
++#ifdef FBCON_HAS_CFB16
++ case 16: ret = 2 << 12; break;
++#endif
++ }
++ return ret;
++}
++
++
++/******
++ * bit mask RGB=565 -> RRRR RGGG GGGB BBBB
++ * 1111 1000 0000 0000 0xf800
++ * 0000 0111 1110 0000 0x07e0
++ * 0000 0000 0001 1111 0x001f
++ *******************************************************/
++static int
++s3c2410fb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
++ u_int trans, struct fb_info *info)
++{
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ unsigned int ptr = S3C2410_VA_LCD + 0x400 + (regno * 4);
++ u_int val, ret = 1;
++
++ if (regno < fbi->palette_size) {
++ val= (red & 0xf800) |
++ ((green & 0xfc00)>>5) |
++ ((blue & 0xf800)>>11);
++
++ __raw_writel(val, ptr);
++ __raw_writel(val, ptr);
++ __raw_writel(val, ptr);
++ ret=0;
++
++ }
++
++#if 0
++ if (regno < fbi->palette_size) {
++ val = ((red >> 5) & 0xf800);
++ val |= ((green >> 11) & 0x07e0);
++ val |= ((blue >> 16) & 0x001f);
++
++ if (regno == 0)
++ val |= palette_pbs(&fbi->fb.var);
++
++ fbi->palette_cpu[regno] = val;
++
++ ret = 0;
++ }
++#endif
++ return ret;
++}
++
++static int
++s3c2410fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++ u_int trans, struct fb_info *info)
++{
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ struct display *disp = get_con_display(info, fbi->currcon);
++ u_int val;
++ int ret = 1;
++
++ /*
++ * If inverse mode was selected, invert all the colours
++ * rather than the register number. The register number
++ * is what you poke into the framebuffer to produce the
++ * colour you requested.
++ */
++
++ if (disp->inverse) {
++ red = 0xffff - red;
++ green = 0xffff - green;
++ blue = 0xffff - blue;
++ }
++
++ /*
++ * If greyscale is true, then we convert the RGB value
++ * to greyscale no mater what visual we are using.
++ */
++ if (fbi->fb.var.grayscale)
++ red = green = blue = (19595 * red + 38470 * green +
++ 7471 * blue) >> 16;
++
++ switch (fbi->fb.disp->visual) {
++ case FB_VISUAL_TRUECOLOR:
++ /*
++ * 12 or 16-bit True Colour. We encode the RGB value
++ * according to the RGB bitfield information.
++ */
++ if (regno < 16) {
++ u16 *pal = fbi->fb.pseudo_palette;
++
++ val = chan_to_field(red, &fbi->fb.var.red);
++ val |= chan_to_field(green, &fbi->fb.var.green);
++ val |= chan_to_field(blue, &fbi->fb.var.blue);
++
++ pal[regno] = val;
++ ret = 0;
++ }
++ break;
++
++ case FB_VISUAL_STATIC_PSEUDOCOLOR:
++ case FB_VISUAL_PSEUDOCOLOR:
++ ret = s3c2410fb_setpalettereg(regno, red, green, blue, trans, info);
++ break;
++ }
++ return ret;
++}
++
++/*
++ * s3c2410fb_decode_var():
++ * Get the video params out of 'var'. If a value doesn't fit, round it up,
++ * if it's too big, return -EINVAL.
++ *
++ * Suggestion: Round up in the following order: bits_per_pixel, xres,
++ * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
++ * bitfields, horizontal timing, vertical timing.
++ */
++static int
++s3c2410fb_validate_var(struct fb_var_screeninfo *var,
++ struct s3c2410fb_info *fbi)
++{
++ int ret = -EINVAL;
++
++ if (var->xres < MIN_XRES)
++ var->xres = MIN_XRES;
++ if (var->yres < MIN_YRES)
++ var->yres = MIN_YRES;
++ if (var->xres > fbi->max_xres)
++ var->xres = fbi->max_xres;
++ if (var->yres > fbi->max_yres)
++ var->yres = fbi->max_yres;
++ var->xres_virtual =
++ var->xres_virtual < var->xres ? var->xres : var->xres_virtual;
++ var->yres_virtual =
++ var->yres_virtual < var->yres ? var->yres : var->yres_virtual;
++
++ switch (var->bits_per_pixel) {
++#ifdef FBCON_HAS_CFB4
++ case 4: ret = 0; break;
++#endif
++#ifdef FBCON_HAS_CFB8
++ case 8: ret = 0; break;
++#endif
++#ifdef FBCON_HAS_CFB16
++ case 16: ret = 0; break;
++#endif
++ default:
++ break;
++ }
++
++ return ret;
++}
++
++static inline void s3c2410fb_set_truecolor(u_int is_true_color)
++{
++ DPRINTK("true_color = %d\n", is_true_color);
++}
++
++static void
++s3c2410fb_hw_set_var(struct fb_var_screeninfo *var, struct s3c2410fb_info *fbi)
++{
++ u_long palette_mem_size;
++
++ fbi->palette_size = 256;
++ palette_mem_size = fbi->palette_size * sizeof(u16);
++ fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
++ fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
++ fb_set_cmap(&fbi->fb.cmap, 1, s3c2410fb_setcolreg, &fbi->fb);
++
++ /* Set board control register to handle new color depth */
++ s3c2410fb_set_truecolor(var->bits_per_pixel >= 16);
++ s3c2410fb_activate_var(var, fbi);
++
++ llprintk("fbi->palette_cpu=%08x, palette_dma=%08x\n",
++ fbi->palette_dma, fbi->palette_cpu);
++
++ fbi->palette_cpu[0] = (fbi->palette_cpu[0] &
++ 0xcfff) | palette_pbs(var);
++}
++
++/*
++ * s3c2410fb_set_var():
++ * Set the user defined part of the display for the specified console
++ */
++static int
++s3c2410fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ struct fb_var_screeninfo *dvar = get_con_var(&fbi->fb, con);
++ struct display *display = get_con_display(&fbi->fb, con);
++ int err, chgvar = 0, rgbidx = 0;
++
++ /*
++ * Decode var contents into a par structure, adjusting any
++ * out of range values.
++ */
++ err = s3c2410fb_validate_var(var, fbi);
++ if (err) return err;
++ if (var->activate & FB_ACTIVATE_TEST)
++ {
++ return 0;
++ }
++
++ if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
++ {
++ return -EINVAL;
++ }
++
++ if (dvar->xres != var->xres)
++ chgvar = 1;
++ if (dvar->yres != var->yres)
++ chgvar = 1;
++ if (dvar->xres_virtual != var->xres_virtual)
++ chgvar = 1;
++ if (dvar->yres_virtual != var->yres_virtual)
++ chgvar = 1;
++ if (dvar->bits_per_pixel != var->bits_per_pixel)
++ chgvar = 1;
++ if (con < 0)
++ chgvar = 0;
++
++ switch (var->bits_per_pixel) {
++ case 16:
++#ifdef CONFIG_FBCON_CFB16
++ display->visual = FB_VISUAL_TRUECOLOR;
++ display->line_length = var->xres * 2;
++ display->dispsw = &fbcon_cfb16;
++ display->dispsw_data = fbi->fb.pseudo_palette;
++ rgbidx = RGB_16;
++#endif
++ break;
++#ifdef CONFIG_FBCON_CFB8
++ case 8:
++ display->visual = FB_VISUAL_PSEUDOCOLOR;
++ display->line_length = var->xres;
++ display->dispsw = &fbcon_cfb8;
++ display->dispsw_data = fbi->fb.pseudo_palette;
++ rgbidx = RGB_16;
++ break;
++#endif
++ default:
++ rgbidx = 0;
++ display->dispsw = &fbcon_dummy;
++ break;
++ }
++
++ display->screen_base = fbi->screen_cpu;
++ display->next_line = display->line_length;
++ display->type = fbi->fb.fix.type;
++ display->type_aux = fbi->fb.fix.type_aux;
++ display->ypanstep = fbi->fb.fix.ypanstep;
++ display->ywrapstep = fbi->fb.fix.ywrapstep;
++ display->can_soft_blank = 1;
++ display->inverse = fbi->cmap_inverse;
++
++ *dvar = *var;
++ dvar->activate &= ~FB_ACTIVATE_ALL;
++
++ /*
++ * Copy the RGB parameters for this display
++ * from the machine specific parameters.
++ */
++ dvar->red = fbi->rgb[rgbidx]->red;
++ dvar->green = fbi->rgb[rgbidx]->green;
++ dvar->blue = fbi->rgb[rgbidx]->blue;
++ dvar->transp = fbi->rgb[rgbidx]->transp;
++
++ /*
++ * Update the old var. The fbcon drivers still use this.
++ * Once they are using fbi->fb.var, this can be dropped.
++ */
++ display->var = *dvar;
++
++ /*
++ * If we are setting all the virtual consoles, also set the
++ * defaults used to create new consoles.
++ */
++ if (var->activate & FB_ACTIVATE_ALL)
++ fbi->fb.disp->var = *dvar;
++
++ /*
++ * If the console has changed and the console has defined
++ * a changevar function, call that function.
++ */
++ if (chgvar && info && fbi->fb.changevar)
++ fbi->fb.changevar(con);
++
++ /* If the current console is selected, activate the new var. */
++ if (con != fbi->currcon)
++ return 0;
++ s3c2410fb_hw_set_var(dvar, fbi);
++
++ return 0;
++}
++
++static int
++__do_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++ struct fb_info *info)
++{
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ struct fb_cmap *dcmap = get_con_cmap(info, con);
++ int err = 0;
++
++ if (con == -1)
++ con = fbi->currcon;
++
++ /* no colormap allocated? (we always have "this" colour map allocated) */
++ if (con >= 0)
++ err = fb_alloc_cmap(&fb_display[con].cmap, fbi->palette_size, 0);
++
++ if (!err && con == fbi->currcon)
++ err = fb_set_cmap(cmap, kspc, s3c2410fb_setcolreg, info);
++
++ if (!err)
++ fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
++
++ return err;
++}
++
++static int
++s3c2410fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++ struct fb_info *info)
++{
++ struct display *disp = get_con_display(info, con);
++
++ if (disp->visual == FB_VISUAL_TRUECOLOR ||
++ disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
++ return -EINVAL;
++
++ return __do_set_cmap(cmap, kspc, con, info);
++}
++
++static int
++s3c2410fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++{
++ struct display *display = get_con_display(info, con);
++
++ *fix = info->fix;
++
++ fix->line_length = display->line_length;
++ fix->visual = display->visual;
++ return 0;
++}
++
++static int
++s3c2410fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++ *var = *get_con_var(info, con);
++ return 0;
++}
++
++static int
++s3c2410fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
++{
++ struct fb_cmap *dcmap = get_con_cmap(info, con);
++ fb_copy_cmap(dcmap, cmap, kspc ? 0 : 2);
++ return 0;
++}
++
++static struct fb_ops s3c2410fb_ops = {
++ owner: THIS_MODULE,
++ fb_get_fix: s3c2410fb_get_fix,
++ fb_get_var: s3c2410fb_get_var,
++ fb_set_var: s3c2410fb_set_var,
++ fb_get_cmap: s3c2410fb_get_cmap,
++ fb_set_cmap: s3c2410fb_set_cmap,
++};
++
++/*
++ * s3c2410fb_switch():
++ * Change to the specified console. Palette and video mode
++ * are changed to the console's stored parameters.
++ *
++ * Uh oh, this can be called from a tasklet (IRQ)
++ */
++static int s3c2410fb_switch(int con, struct fb_info *info)
++{
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ struct display *disp;
++ struct fb_cmap *cmap;
++
++ if (con == fbi->currcon)
++ return 0;
++
++ if (fbi->currcon >= 0) {
++ disp = fb_display + fbi->currcon;
++
++ /*
++ * Save the old colormap and video mode.
++ */
++ disp->var = fbi->fb.var;
++
++ if (disp->cmap.len)
++ fb_copy_cmap(&fbi->fb.cmap, &disp->cmap, 0);
++ }
++
++ fbi->currcon = con;
++ disp = fb_display + con;
++
++ /*
++ * Make sure that our colourmap contains 256 entries.
++ */
++ fb_alloc_cmap(&fbi->fb.cmap, 256, 0);
++
++ if (disp->cmap.len)
++ cmap = &disp->cmap;
++ else
++ cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
++
++ fb_copy_cmap(cmap, &fbi->fb.cmap, 0);
++
++ fbi->fb.var = disp->var;
++ fbi->fb.var.activate = FB_ACTIVATE_NOW;
++
++ s3c2410fb_set_var(&fbi->fb.var, con, info);
++ return 0;
++}
++
++/*
++ * Formal definition of the VESA spec:
++ * On
++ * This refers to the state of the display when it is in full operation
++ * Stand-By
++ * This defines an optional operating state of minimal power reduction with
++ * the shortest recovery time
++ * Suspend
++ * This refers to a level of power management in which substantial power
++ * reduction is achieved by the display. The display can have a longer
++ * recovery time from this state than from the Stand-by state
++ * Off
++ * This indicates that the display is consuming the lowest level of power
++ * and is non-operational. Recovery from this state may optionally require
++ * the user to manually power on the monitor
++ *
++ * Now, the fbdev driver adds an additional state, (blank), where they
++ * turn off the video (maybe by colormap tricks), but don't mess with the
++ * video itself: think of it semantically between on and Stand-By.
++ *
++ * So here's what we should do in our fbdev blank routine:
++ *
++ * VESA_NO_BLANKING (mode 0) Video on, front/back light on
++ * VESA_VSYNC_SUSPEND (mode 1) Video on, front/back light off
++ * VESA_HSYNC_SUSPEND (mode 2) Video on, front/back light off
++ * VESA_POWERDOWN (mode 3) Video off, front/back light off
++ *
++ * This will match the matrox implementation.
++ */
++
++/*
++ * s3c2410fb_blank():
++ * Blank the display by setting all palette values to zero. Note, the
++ * 12 and 16 bpp modes don't really use the palette, so this will not
++ * blank the display in all modes.
++ * blank = 0 unblank ;
++ * blank > 0 , VESA level
++ */
++static void s3c2410fb_blank(int blank, struct fb_info *info)
++{
++
++#ifdef S3C2410_REAL_BLANK
++ struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
++ int i,j;
++ struct FrameBuffer * pBlankFB;
++#endif
++
++#ifdef S3C2410_SUPPORT_PALETTE
++ switch (blank) {
++ case VESA_POWERDOWN:
++ case VESA_VSYNC_SUSPEND:
++ case VESA_HSYNC_SUSPEND:
++ case VESA_NO_BLANKING:
++ if (s3c2410fb_blank_helper)
++ s3c2410fb_blank_helper(blank);
++ if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
++ fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
++ fb_set_cmap(&fbi->fb.cmap, 1, s3c2410fb_setcolreg, info);
++ s3c2410fb_schedule_task(fbi, C_ENABLE);
++ }
++#endif
++
++#ifdef S3C2410_REAL_BLANK
++ pBlankFB = (struct FrameBuffer *) (fbi->screen_cpu);
++
++ for(i=0 ;i<FR_HEIGHT;i++)
++ for(j=0;j<FR_WIDTH;j++)
++ pBlankFB->pixel[i][j]= 0xffff;
++#endif
++}
++
++static int s3c2410fb_updatevar(int con, struct fb_info *info)
++{
++ DPRINTK("entered\n");
++ return 0;
++}
++
++/*
++ * s3c2410fb_activate_var():
++ * Configures LCD Controller based on entries in var parameter. Settings are
++ * only written to the controller if changes were made.
++
++*/
++static int s3c2410fb_activate_var(struct fb_var_screeninfo *var, struct s3c2410fb_info *fbi)
++{
++ struct s3c2410fb_lcd_reg new_regs;
++ u_long flags;
++ u_long line_size = var->xres;
++
++ memset(&new_regs, 0, sizeof(new_regs));
++
++ llprintk("activate: xres=%d, yres=%d, margins=%d,%d,%d,%d\n",
++ var->xres, var->yres, var->left_margin, var->upper_margin,
++ var->right_margin, var->lower_margin);
++#if DEBUG_VAR
++ if (var->xres < 16 || var->xres > 1024)
++ printk(KERN_ERR "%s: invalid xres %d\n",
++ fbi->fb.fix.id, var->xres);
++ if (var->hsync_len < 1 || var->hsync_len > 64)
++ printk(KERN_ERR "%s: invalid hsync_len %d\n",
++ fbi->fb.fix.id, var->hsync_len);
++ if (var->left_margin < 1 || var->left_margin > 255)
++ printk(KERN_ERR "%s: invalid left_margin %d\n",
++ fbi->fb.fix.id, var->left_margin);
++ if (var->right_margin < 1 || var->right_margin > 255)
++ printk(KERN_ERR "%s: invalid right_margin %d\n",
++ fbi->fb.fix.id, var->right_margin);
++ if (var->yres < 1 || var->yres > 1024)
++ printk(KERN_ERR "%s: invalid yres %d\n",
++ fbi->fb.fix.id, var->yres);
++ if (var->vsync_len < 1 || var->vsync_len > 64)
++ printk(KERN_ERR "%s: invalid vsync_len %d\n",
++ fbi->fb.fix.id, var->vsync_len);
++ if (var->upper_margin < 0 || var->upper_margin > 255)
++ printk(KERN_ERR "%s: invalid upper_margin %d\n",
++ fbi->fb.fix.id, var->upper_margin);
++ if (var->lower_margin < 0 || var->lower_margin > 255)
++ printk(KERN_ERR "%s: invalid lower_margin %d\n",
++ fbi->fb.fix.id, var->lower_margin);
++#endif
++
++
++#ifdef TFT240_320
++ new_regs.lcdcon1=(CLKVAL_TFT<<8)|(MVAL_USED<<7)|(3<<5)|(12<<1)|0;
++ /* TFT LCD panel,16bpp TFT,ENVID=off */
++ new_regs.lcdcon2=(VBPD<<24)|((var->yres-1)<<14)|(VFPD<<6)|(VSPW);
++ new_regs.lcdcon3=(HBPD<<19)|((var->xres-1)<<8)|(HFPD);
++ new_regs.lcdcon4=(MVAL<<8)|(HSPW);
++ new_regs.lcdcon5=(1<<11)|(1<<9)|(1<<8)|(1<<3)|1;
++ // new_regs.lcdcon5=(1<<11)|(1<<9)|(1<<8)|(1<<3);
++
++ new_regs.lcdsaddr1=
++ (((unsigned long)fbi->screen_dma>>22)<<21)|M5D((unsigned long)fbi->screen_dma>>1);
++
++ new_regs.lcdsaddr2=
++ M5D(((unsigned long)fbi->screen_dma+(var->xres*var->yres*2))>>1);
++ new_regs.lcdsaddr3=(((var->xres - var->xres)/1)<<11)|(var->xres/1);
++ // new_regs.lcdintmsk = 3;
++ // new_regs.lpcsel = 0x7; /* Enable PCD3600 */
++ // new_regs.tpal = 0x0;
++#else
++ // todo - sort out clkval
++ new_regs.lcdcon1 = S3C2410_LCDCON1_TFT | S3C2410_LCDCON1_CLKVAL(2-1);
++
++ switch (var->bits_per_pixel) {
++ case 1:
++ llprintk(__FUNCTION__ ": selecting 1bpp\n");
++ new_regs.lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
++ line_size /= 8;
++ break;
++ case 2:
++ llprintk(__FUNCTION__ ": selecting 2bpp\n");
++ new_regs.lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
++ line_size /= 4;
++ break;
++ case 4:
++ llprintk(__FUNCTION__ ": selecting 4bpp\n");
++ new_regs.lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
++ line_size /= 2;
++ break;
++ case 8:
++ llprintk(__FUNCTION__ ": selecting 8bpp\n");
++ new_regs.lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
++ break;
++ case 16:
++ llprintk(__FUNCTION__ ": selecting 16bpp\n");
++ new_regs.lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
++ line_size *= 2;
++ break;
++ case 24:
++ if (!machine_is_bast()) {
++ llprintk(__FUNCTION__ ": selecting 24bpp\n");
++ new_regs.lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
++ line_size *= 4;
++ break;
++ }
++
++ default:
++ new_regs.lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
++ printk(KERN_ERR "s3c2410fb: invalid bpp %d\n",
++ var->bits_per_pixel);
++ }
++
++ llprintk("vert: vfpd=%d, vspw=%d, vsync_len=%d\n",
++ var->upper_margin, var->lower_margin,
++ var->vsync_len);
++
++ new_regs.lcdcon2 = S3C2410_LCDCON2_VBPD(var->lower_margin-1) |
++ S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
++ S3C2410_LCDCON2_VFPD(var->upper_margin-1) |
++ S3C2410_LCDCON2_VSPW(var->vsync_len-1);
++
++ llprintk("horiz: hbpd=%d, hozval=%d, hfpd=%d, hsync_len=%d\n",
++ var->left_margin, var->xres, var->right_margin,
++ var->hsync_len);
++
++ new_regs.lcdcon3 = S3C2410_LCDCON3_HBPD(var->left_margin-1) |
++ S3C2410_LCDCON3_HOZVAL(var->xres - 1) |
++ S3C2410_LCDCON3_HFPD(var->right_margin - 1);
++
++ new_regs.lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len-1);
++
++ /* need to decode the sync type */
++
++ new_regs.lcdcon5 = S3C2410_LCDCON5_INVHSYNC | S3C2410_LCDCON5_INVVSYNC;
++ new_regs.lcdcon5 |= S3C2410_LCDCON5_BSWP;
++ new_regs.lcdcon5 |= S3C2410_LCDCON5_FRM565;
++
++ llprintk("screen dma at %08x\n", fbi->screen_dma);
++
++ new_regs.lcdsaddr1 = (unsigned long)(fbi->screen_dma >> 1);
++ new_regs.lcdsaddr2 = new_regs.lcdsaddr1 + ((line_size * var->yres) >> 1);
++ new_regs.lcdsaddr3 = (line_size >> 1);
++
++ llprintk("new_regs: CON=%08x %08x %08x %08x %08x\n"
++ "new_regs: ADRR=%08x %08x %08x\n",
++ new_regs.lcdcon1, new_regs.lcdcon2, new_regs.lcdcon3,
++ new_regs.lcdcon4, new_regs.lcdcon5, new_regs.lcdsaddr1,
++ new_regs.lcdsaddr2, new_regs.lcdsaddr3);
++#endif /* ONLY 240 * 320 */
++
++
++ /* Update shadow copy atomically */
++ local_irq_save(flags);
++ fbi->reg_lcdcon1 = new_regs.lcdcon1;
++ fbi->reg_lcdcon2 = new_regs.lcdcon2;
++ fbi->reg_lcdcon3 = new_regs.lcdcon3;
++ fbi->reg_lcdcon4 = new_regs.lcdcon4;
++ fbi->reg_lcdcon5 = new_regs.lcdcon5;
++ fbi->reg_lcdsaddr1 = new_regs.lcdsaddr1;
++ fbi->reg_lcdsaddr2 = new_regs.lcdsaddr2;
++ fbi->reg_lcdsaddr3 = new_regs.lcdsaddr3;
++ fbi->reg_lpcsel = new_regs.lpcsel;
++ local_irq_restore(flags);
++
++ /*
++ * Only update the registers if the controller is enabled
++ * and something has changed.
++ */
++ if (__raw_readl(S3C2410_LCDCON1) != fbi->reg_lcdcon1 ||
++ __raw_readl(S3C2410_LCDCON2) != fbi->reg_lcdcon2 ||
++ __raw_readl(S3C2410_LCDCON3) != fbi->reg_lcdcon3 ||
++ __raw_readl(S3C2410_LCDCON4) != fbi->reg_lcdcon4 ||
++ __raw_readl(S3C2410_LCDCON5) != fbi->reg_lcdcon5 ||
++ __raw_readl(S3C2410_LCDSADDR1) != fbi->reg_lcdsaddr1 ||
++ __raw_readl(S3C2410_LCDSADDR2) != fbi->reg_lcdsaddr2 ||
++ __raw_readl(S3C2410_LCDSADDR3) != fbi->reg_lcdsaddr3) {
++ s3c2410fb_schedule_task(fbi, C_REENABLE);
++ }
++
++ return 0;
++}
++
++
++static void s3c2410fb_enable_controller(struct s3c2410fb_info *fbi)
++{
++ DPRINTK("Enabling LCD controller\n");
++
++ llprintk(__FUNCTION__ ": writing fbi %p\n", fbi);
++
++ /*
++ * Make sure the mode bits are present in the first palette entry
++ */
++ fbi->palette_cpu[0] &= 0xcfff;
++ fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var);
++
++ fbi->reg_lcdcon1 |= S3C2410_LCDCON1_ENVDI; /* enable the lcd system */
++
++ llprintk(__FUNCTION__ ": current: %08x %08x %08x %08x %08x\n",
++ __raw_readl(S3C2410_LCDCON1), __raw_readl(S3C2410_LCDCON2),
++ __raw_readl(S3C2410_LCDCON3), __raw_readl(S3C2410_LCDCON4),
++ __raw_readl(S3C2410_LCDCON5));
++
++#if 1
++ __raw_writel(fbi->reg_lcdcon1, S3C2410_LCDCON1);
++ __raw_writel(fbi->reg_lcdcon2, S3C2410_LCDCON2);
++ __raw_writel(fbi->reg_lcdcon3, S3C2410_LCDCON3);
++ __raw_writel(fbi->reg_lcdcon4, S3C2410_LCDCON4);
++ __raw_writel(fbi->reg_lcdcon5, S3C2410_LCDCON5);
++#endif
++
++ __raw_writel(fbi->reg_lcdsaddr1, S3C2410_LCDSADDR1);
++ __raw_writel(fbi->reg_lcdsaddr2, S3C2410_LCDSADDR2);
++ __raw_writel(fbi->reg_lcdsaddr3, S3C2410_LCDSADDR3);
++ // rLPCSEL &= (~(fbi->reg_lpcsel));
++ // rLCDINTMSK |= fbi->reg_lcdintmsk;
++ // rLCDINTMSK |= 3;
++
++ __raw_writel(0, S3C2410_TPAL); // temporary palette register, Disabled
++ __raw_writel(0, S3C2410_LPCSEL);
++
++ FBuf = (struct FrameBuffer *)fbi->screen_cpu;
++#ifdef YOU_WANT_TO_DRAW_TETRAGON
++ lcd_demo();
++ {
++ unsigned long i ;
++ for (i = 0; i < 0xc000000;i++);
++ }
++#endif
++
++}
++
++static void s3c2410fb_disable_controller(struct s3c2410fb_info *fbi)
++{
++ DECLARE_WAITQUEUE(wait, current);
++
++ DPRINTK("Disabling LCD controller\n");
++
++ llprintk(__FUNCTION__ ": fbi=%p\n", fbi);
++
++ add_wait_queue(&fbi->ctrlr_wait, &wait);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++
++ fbi->reg_lcdcon1 &= ~S3C2410_LCDCON1_ENVDI;
++
++#if 1
++ __raw_writel(fbi->reg_lcdcon1, S3C2410_LCDCON1);
++ __raw_writel(fbi->reg_lcdcon2, S3C2410_LCDCON2);
++ __raw_writel(fbi->reg_lcdcon3, S3C2410_LCDCON3);
++ __raw_writel(fbi->reg_lcdcon4, S3C2410_LCDCON4);
++ __raw_writel(fbi->reg_lcdcon5, S3C2410_LCDCON5);
++
++ __raw_writel(fbi->reg_lcdsaddr1, S3C2410_LCDSADDR1);
++ __raw_writel(fbi->reg_lcdsaddr2, S3C2410_LCDSADDR2);
++ __raw_writel(fbi->reg_lcdsaddr3, S3C2410_LCDSADDR3);
++ __raw_writel(fbi->reg_lpcsel, S3C2410_LPCSEL);
++#endif
++
++ schedule_timeout(20 * HZ / 1000);
++ current->state = TASK_RUNNING;
++ remove_wait_queue(&fbi->ctrlr_wait, &wait);
++}
++
++
++
++/*
++ * This function must be called from task context only, since it will
++ * sleep when disabling the LCD controller, or if we get two contending
++ * processes trying to alter state.
++ */
++static void set_ctrlr_state(struct s3c2410fb_info *fbi, u_int state)
++{
++ u_int old_state;
++
++ down(&fbi->ctrlr_sem);
++
++ old_state = fbi->state;
++
++ switch (state) {
++ case C_DISABLE_CLKCHANGE:
++ /*
++ * Disable controller for clock change. If the
++ * controller is already disabled, then do nothing.
++ */
++ if (old_state != C_DISABLE) {
++ fbi->state = state;
++ s3c2410fb_disable_controller(fbi);
++ }
++ break;
++
++ case C_DISABLE:
++ /*
++ * Disable controller
++ */
++ if (old_state != C_DISABLE) {
++ fbi->state = state;
++
++ if (old_state != C_DISABLE_CLKCHANGE)
++ s3c2410fb_disable_controller(fbi);
++ }
++ break;
++
++ case C_ENABLE_CLKCHANGE:
++ /*
++ * Enable the controller after clock change. Only
++ * do this if we were disabled for the clock change.
++ */
++ if (old_state == C_DISABLE_CLKCHANGE) {
++ fbi->state = C_ENABLE;
++ s3c2410fb_enable_controller(fbi);
++ }
++ break;
++
++ case C_REENABLE:
++ /*
++ * Re-enable the controller only if it was already
++ * enabled. This is so we reprogram the control
++ * registers.
++ */
++ if (old_state == C_ENABLE) {
++ s3c2410fb_disable_controller(fbi);
++ s3c2410fb_enable_controller(fbi);
++ }
++ break;
++
++ case C_ENABLE:
++ /*
++ * Power up the LCD screen, enable controller, and
++ * turn on the backlight.
++ */
++ if (old_state != C_ENABLE) {
++ fbi->state = C_ENABLE;
++ s3c2410fb_enable_controller(fbi);
++ }
++ break;
++ }
++ up(&fbi->ctrlr_sem);
++}
++
++/*
++ * Our LCD controller task (which is called when we blank or unblank)
++ * via keventd.
++ */
++static void s3c2410fb_task(void *dummy)
++{
++ struct s3c2410fb_info *fbi = dummy;
++ u_int state = xchg(&fbi->task_state, -1);
++ set_ctrlr_state(fbi, state);
++}
++
++
++/*
++ * s3c2410fb_map_video_memory():
++ * Allocates the DRAM memory for the frame buffer. This buffer is
++ * remapped into a non-cached, non-buffered, memory region to
++ * allow palette and pixel writes to occur without flushing the
++ * cache. Once this area is remapped, all virtual memory
++ * access to the video memory should occur at the new region.
++ */
++static int __init s3c2410fb_map_video_memory(struct s3c2410fb_info *fbi)
++{
++ /*
++ * We reserve one page for the palette, plus the size
++ * of the framebuffer.
++ *
++ * fbi->map_dma is bus address (physical)
++ * fbi->screen_dma = fbi->map_dma + PAGE_SIZE
++ * fbi->map_cpu is virtual address
++ * fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE
++ *
++ * S3C2400 Result
++ * map_size is 0x00027000
++ * map_cpu is 0xC2800000
++ * smem_start is 0x0CFC1000
++ * palette_mem_size 0x20
++ */
++
++ fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE );
++ fbi->map_cpu = consistent_alloc(GFP_KERNEL , fbi->map_size,
++ &fbi->map_dma);
++ if (fbi->map_cpu) {
++ fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE ;
++ fbi->screen_dma = fbi->map_dma + PAGE_SIZE ;
++ fbi->fb.fix.smem_start = fbi->screen_dma;
++ }
++
++ return fbi->map_cpu ? 0 : -ENOMEM;
++}
++
++/* Fake monspecs to fill in fbinfo structure */
++static struct fb_monspecs monspecs __initdata = {
++ 30000, 70000, 50, 65, 0 /* Generic */
++};
++
++
++static struct s3c2410fb_info * __init s3c2410fb_init_fbinfo(void)
++{
++ struct s3c2410fb_info *fbi;
++
++ fbi = kmalloc(sizeof(struct s3c2410fb_info) + sizeof(struct display) +
++ sizeof(u16) * 16, GFP_KERNEL);
++ if (!fbi)
++ return NULL;
++
++ memset(fbi, 0, sizeof(struct s3c2410fb_info) + sizeof(struct display));
++
++ fbi->currcon = -1;
++
++ strcpy(fbi->fb.fix.id, S3C2410_NAME);
++ fbi->fb.node = -1; /* What is this ? */
++ fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS;
++ fbi->fb.fix.type_aux = 0;
++ fbi->fb.fix.xpanstep = 0;
++ fbi->fb.fix.ypanstep = 0;
++ fbi->fb.fix.ywrapstep = 0;
++ fbi->fb.fix.accel = FB_ACCEL_NONE; /* No hardware Accelerator */
++
++ fbi->fb.var.nonstd = 0;
++ fbi->fb.var.activate = FB_ACTIVATE_NOW;
++ fbi->fb.var.height = -1;
++ fbi->fb.var.width = -1;
++ fbi->fb.var.accel_flags = 0;
++ fbi->fb.var.vmode = FB_VMODE_NONINTERLACED;
++
++ strcpy(fbi->fb.modename, S3C2410_NAME);
++ strcpy(fbi->fb.fontname, "Acorn8x8");
++
++ fbi->fb.fbops = &s3c2410fb_ops;
++ fbi->fb.changevar = NULL;
++ fbi->fb.switch_con = s3c2410fb_switch;
++ fbi->fb.updatevar = s3c2410fb_updatevar;
++ fbi->fb.blank = s3c2410fb_blank;
++ fbi->fb.flags = FBINFO_FLAG_DEFAULT;
++ fbi->fb.node = -1;
++ fbi->fb.monspecs = monspecs;
++ fbi->fb.disp = (struct display *)(fbi + 1); /* golbal display */
++ fbi->fb.pseudo_palette = (void *)(fbi->fb.disp + 1);
++
++ fbi->rgb[RGB_8] = &rgb_8;
++ fbi->rgb[RGB_16] = &def_rgb_16;
++
++ s3c2410fb_get_machine_info(fbi);
++
++ fbi->state = C_DISABLE;
++ fbi->task_state = (u_char)-1;
++ fbi->fb.fix.smem_len = (fbi->max_xres * fbi->max_yres * fbi->max_bpp) / 8;
++
++ init_waitqueue_head(&fbi->ctrlr_wait);
++ INIT_TQUEUE(&fbi->task, s3c2410fb_task, fbi);
++ init_MUTEX(&fbi->ctrlr_sem);
++
++ return fbi;
++}
++
++static int fifo_errcount = 0;
++
++static void
++s3c2410fb_irq_fifo(int irq, void *pw)
++{
++ fifo_errcount++;
++
++#if 0
++ printk(KERN_DEBUG "s3c2410fb: fifo error, count=%d (jiffies=%ld)\n",
++ fifo_errcount, jiffies);
++#endif
++}
++
++int __init s3c2410fb_init(void)
++{
++ struct s3c2410fb_info *fbi;
++ int ret;
++
++ printk("S3C2410 LCD/VGA Console support\n");
++
++ if (machine_is_vr1000()) {
++ printk("S3C2410: VR1000 has no video hardware, skipping\n");
++ return 1;
++ }
++
++ llprintk(__FUNCTION__ ":%d: initialising\n", __LINE__);
++
++ s3c2410fb_lcd_port_init();
++ fbi = s3c2410fb_init_fbinfo();
++ ret = -ENOMEM;
++ if (!fbi)
++ goto failed;
++
++ llprintk(__FUNCTION__ ":%d: initialising\n", __LINE__);
++
++ /* Initialize video memory */
++ ret = s3c2410fb_map_video_memory(fbi);
++ if (ret)
++ goto failed;
++
++ llprintk(__FUNCTION__ ":%d: initialising\n", __LINE__);
++
++ s3c2410fb_set_var(&fbi->fb.var, -1, &fbi->fb);
++ ret = register_framebuffer(&fbi->fb);
++ if (ret < 0)
++ goto failed;
++
++ llprintk(__FUNCTION__ ":%d: initialising\n", __LINE__);
++
++ /*
++ * Ok, now enable the LCD controller
++ */
++ set_ctrlr_state(fbi, C_ENABLE);
++
++#if 0
++ /* enable this code to count the LCD FIFO overruns */
++
++ ret = request_irq(IRQ_LCD_FIFO, s3c2410fb_irq_fifo, SA_INTERRUPT,
++ "LCD FIFO Overrun", NULL);
++#endif
++
++ if (ret != 0) {
++ printk(KERN_ERR "s3c2410fb: LCD FIFO IRQ cannot be claimed (%d)\n", ret);
++ }
++
++ /* This driver cannot be unloaded at the moment */
++ MOD_INC_USE_COUNT;
++ return 0;
++
++failed:
++ if (fbi) kfree(fbi);
++ return ret;
++}
++
++int __init s3c2410fb_setup(char *options)
++{
++ return 0;
++}
++
++MODULE_DESCRIPTION("S3C2410/SAMSUNG framebuffer driver");
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/drivers/video/s3c2410fb.h kernel-source-2.4.27-8-arm-1/drivers/video/s3c2410fb.h
+--- kernel-source-2.4.27-8/drivers/video/s3c2410fb.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/s3c2410fb.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,156 @@
++/*
++ * linux/drivers/video/s3c2410fb.h
++ * -- StrongARM 1100 LCD Controller Frame Buffer Device
++ *
++ * Copyright (C) 1999 Eric A. Thomas
++ * Based on acornfb.c Copyright (C) Russell King.
++ * 2001 Sangwook Lee (hitchcar at sec.samsung.com) S3C2400
++ * 2002 Sangwook Lee (hitchcar at sec.samsung.com) S3C2410
++ *
++ */
++
++/*
++ * struct fb_bitfield {
++ * __u32 offset; beginning of bitfield
++ * __u32 length; length of bitfield
++ * __u32 msb_right; != 0 : Most significant bit is
++ * right
++ * };
++ * These are the bitfields for each
++ * display depth that we support.
++ */
++struct s3c2410fb_rgb {
++ struct fb_bitfield red;
++ struct fb_bitfield green;
++ struct fb_bitfield blue;
++ struct fb_bitfield transp;
++};
++
++/*
++ * This structure describes the machine which we are running on.
++ */
++struct s3c2410fb_mach_info {
++ u_long pixclock;
++
++ u_short xres;
++ u_short yres;
++
++ u_char bpp;
++ u_char hsync_len;
++ u_char left_margin;
++ u_char right_margin;
++
++ u_char vsync_len;
++ u_char upper_margin;
++ u_char lower_margin;
++ u_char sync;
++
++ u_int cmap_greyscale:1,
++ cmap_inverse:1,
++ cmap_static:1,
++ unused:29;
++};
++
++/* Shadows for LCD controller registers */
++struct s3c2410fb_lcd_reg {
++ unsigned long lcdcon1;
++ unsigned long lcdcon2;
++ unsigned long lcdcon3;
++ unsigned long lcdcon4;
++ unsigned long lcdcon5;
++ unsigned long lcdsaddr1;
++ unsigned long lcdsaddr2;
++ unsigned long lcdsaddr3;
++ unsigned long lpcsel;
++ unsigned long lcdintmsk;
++ unsigned long tpal;
++};
++
++#define RGB_8 (0)
++#define RGB_16 (1)
++#define NR_RGB 2
++
++struct s3c2410fb_info {
++ struct fb_info fb;
++ signed int currcon;
++
++ struct s3c2410fb_rgb *rgb[NR_RGB];
++
++ u_int max_bpp;
++ u_int max_xres;
++ u_int max_yres;
++
++ /*
++ * These are the addresses we mapped
++ * the framebuffer memory region to.
++ */
++ dma_addr_t map_dma;
++ u_char * map_cpu;
++ u_int map_size;
++
++ u_char * screen_cpu;
++ dma_addr_t screen_dma;
++ u16 * palette_cpu;
++ dma_addr_t palette_dma;
++ u_int palette_size;
++
++ u_int cmap_inverse:1,
++ cmap_static:1,
++ unused:30;
++
++ u_int reg_lcdcon1;
++ u_int reg_lcdcon2;
++ u_int reg_lcdcon3;
++ u_int reg_lcdcon4;
++ u_int reg_lcdcon5;
++ u_int reg_lcdsaddr1;
++ u_int reg_lcdsaddr2;
++ u_int reg_lcdsaddr3;
++ u_int reg_lpcsel;
++ u_int reg_lcdintmsk;
++
++ volatile u_char state;
++ volatile u_char task_state;
++ struct semaphore ctrlr_sem;
++ wait_queue_head_t ctrlr_wait;
++ struct tq_struct task;
++
++#ifdef CONFIG_PM
++ struct pm_dev *pm;
++#endif
++#ifdef CONFIG_CPU_FREQ
++ struct notifier_block clockchg;
++#endif
++};
++
++#define __type_entry(ptr,type,member) ((type *)((char *)(ptr)-offsetof(type,member)))
++
++#define TO_INF(ptr,member) __type_entry(ptr,struct s3c2410fb_info,member)
++
++#define S3C2410_PALETTE_MODE_VAL(bpp) (((bpp) & 0x018) << 9)
++
++/*
++ * These are the actions for set_ctrlr_state
++ */
++#define C_DISABLE (0)
++#define C_ENABLE (1)
++#define C_DISABLE_CLKCHANGE (2)
++#define C_ENABLE_CLKCHANGE (3)
++#define C_REENABLE (4)
++
++#define S3C2410_NAME "S3C2410"
++
++/*
++ * Debug macros
++ */
++#if DEBUG
++# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
++#else
++# define DPRINTK(fmt, args...)
++#endif
++
++/*
++ * Minimum X and Y resolutions
++ */
++#define MIN_XRES 64
++#define MIN_YRES 64
+diff -urN kernel-source-2.4.27-8/drivers/video/sa1100fb.c kernel-source-2.4.27-8-arm-1/drivers/video/sa1100fb.c
+--- kernel-source-2.4.27-8/drivers/video/sa1100fb.c 2001-11-14 22:52:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/video/sa1100fb.c 2005-02-18 17:48:44.000000000 +0000
+@@ -41,6 +41,17 @@
+ * David Neuer. It's around 8 lines of C code, plus another 4 to
+ * detect if we are using grayscale.
+ *
++ * - The following must never be specified in a panel definition:
++ * LCCR0_LtlEnd, LCCR3_PixClkDiv, LCCR3_VrtSnchL, LCCR3_HorSnchL
++ *
++ * - The following should be specified:
++ * either LCCR0_Color or LCCR0_Mono
++ * either LCCR0_Sngl or LCCR0_Dual
++ * either LCCR0_Act or LCCR0_Pas
++ * either LCCR3_OutEnH or LCCD3_OutEnL
++ * either LCCR3_PixRsEdg or LCCR3_PixFlEdg
++ * either LCCR3_ACBsDiv or LCCR3_ACBsCntOff
++ *
+ * Code Status:
+ * 1999/04/01:
+ * - Driver appears to be working for Brutus 320x200x8bpp mode. Other
+@@ -147,6 +158,10 @@
+ *
+ * 2001/10/12: <rmk at arm.linux.org.uk>
+ * - Add patch 681/1 and clean up stork definitions.
++ *
++ * 2002/02/21: <abraham at 2d3d.co.za>
++ * - Added support for ICP LCD-Kit01 on Frodo.
++ * - Added support for backlight via CPLDs on Frodo.
+ */
+
+ #include <linux/config.h>
+@@ -169,6 +184,7 @@
+ #include <asm/mach-types.h>
+ #include <asm/uaccess.h>
+ #include <asm/arch/assabet.h>
++#include <asm/arch/shannon.h>
+
+ #include <video/fbcon.h>
+ #include <video/fbcon-mfb.h>
+@@ -177,11 +193,6 @@
+ #include <video/fbcon-cfb16.h>
+
+ /*
+- * enable this if your panel appears to have broken
+- */
+-#undef CHECK_COMPAT
+-
+-/*
+ * debugging?
+ */
+ #define DEBUG 0
+@@ -197,243 +208,6 @@
+ void (*sa1100fb_blank_helper)(int blank);
+ EXPORT_SYMBOL(sa1100fb_blank_helper);
+
+-
+-#ifdef CHECK_COMPAT
+-static void
+-sa1100fb_check_shadow(struct sa1100fb_lcd_reg *new_regs,
+- struct fb_var_screeninfo *var, u_int pcd)
+-{
+- struct sa1100fb_lcd_reg shadow;
+- int different = 0;
+-
+- /*
+- * These machines are good machines!
+- */
+- if (machine_is_assabet() || machine_is_h3600())
+- return;
+-
+- /*
+- * The following ones are bad, bad, bad.
+- * Please make yours good!
+- */
+- if (machine_is_pangolin()) {
+- DPRINTK("Configuring Pangolin LCD\n");
+- shadow.lccr0 =
+- LCCR0_LEN + LCCR0_Color + LCCR0_LDM +
+- LCCR0_BAM + LCCR0_ERM + LCCR0_Act +
+- LCCR0_LtlEnd + LCCR0_DMADel(0);
+- shadow.lccr1 =
+- LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(64) +
+- LCCR1_BegLnDel(160) + LCCR1_EndLnDel(24);
+- shadow.lccr2 =
+- LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(7) +
+- LCCR2_BegFrmDel(7) + LCCR2_EndFrmDel(1);
+- shadow.lccr3 =
+- LCCR3_PixClkDiv(pcd) + LCCR3_HorSnchH +
+- LCCR3_VrtSnchH + LCCR3_PixFlEdg + LCCR3_OutEnH;
+-
+- DPRINTK("pcd = %x, PixCldDiv(pcd)=%x\n",
+- pcd, LCCR3_PixClkDiv(pcd));
+- }
+- if (machine_is_freebird()) {
+- DPRINTK("Configuring Freebird LCD\n");
+-#if 1
+- shadow.lccr0 = 0x00000038;
+- shadow.lccr1 = 0x010108e0;
+- shadow.lccr2 = 0x0000053f;
+- shadow.lccr3 = 0x00000c20;
+-#else
+- shadow.lccr0 =
+- LCCR0_LEN + LCCR0_Color + LCCR0_Sngl +
+- LCCR0_LDM + LCCR0_BAM + LCCR0_ERM + LCCR0_Pas +
+- LCCR0_LtlEnd + LCCR0_DMADel(0);
+- /* Check ,Chester */
+- shadow.lccr1 =
+- LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(5) +
+- LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
+- /* Check ,Chester */
+- shadow.lccr2 =
+- LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
+- LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
+- /* Check ,Chester */
+- shadow.lccr3 =
+- LCCR3_OutEnH + LCCR3_PixFlEdg + LCCR3_VrtSnchH +
+- LCCR3_HorSnchH + LCCR3_ACBsCntOff +
+- LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(pcd);
+-#endif
+- }
+- if (machine_is_brutus()) {
+- DPRINTK("Configuring Brutus LCD\n");
+- shadow.lccr0 =
+- LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Pas +
+- LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+- LCCR0_DMADel(0);
+- shadow.lccr1 =
+- LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(3) +
+- LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
+- shadow.lccr2 =
+- LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
+- LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
+- shadow.lccr3 =
+- LCCR3_OutEnH + LCCR3_PixRsEdg + LCCR3_VrtSnchH +
+- LCCR3_HorSnchH + LCCR3_ACBsCntOff +
+- LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(44);
+- }
+- if (machine_is_huw_webpanel()) {
+- DPRINTK("Configuring HuW LCD\n");
+- shadow.lccr0 = LCCR0_LEN + LCCR0_Dual + LCCR0_LDM;
+- shadow.lccr1 = LCCR1_DisWdth(var->xres) +
+- LCCR1_HorSnchWdth(3) +
+- LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
+- shadow.lccr2 = 239 + LCCR2_VrtSnchWdth(1);
+- shadow.lccr3 = 8 + LCCR3_OutEnH +
+- LCCR3_PixRsEdg + LCCR3_VrtSnchH +
+- LCCR3_HorSnchH + LCCR3_ACBsCntOff + LCCR3_ACBsDiv(2);
+- }
+- if (machine_is_lart()) {
+- DPRINTK("Configuring LART LCD\n");
+-#if defined LART_GREY_LCD
+- shadow.lccr0 =
+- LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
+- LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+- LCCR0_DMADel(0);
+- shadow.lccr1 =
+- LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(1) +
+- LCCR1_BegLnDel(4) + LCCR1_EndLnDel(2);
+- shadow.lccr2 =
+- LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
+- LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
+- shadow.lccr3 =
+- LCCR3_PixClkDiv(34) + LCCR3_ACBsDiv(512) +
+- LCCR3_ACBsCntOff + LCCR3_HorSnchH + LCCR3_VrtSnchH;
+-#endif
+-#if defined LART_COLOR_LCD
+- shadow.lccr0 =
+- LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
+- LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+- LCCR0_DMADel(0);
+- shadow.lccr1 =
+- LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(2) +
+- LCCR1_BegLnDel(69) + LCCR1_EndLnDel(8);
+- shadow.lccr2 =
+- LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(3) +
+- LCCR2_BegFrmDel(14) + LCCR2_EndFrmDel(4);
+- shadow.lccr3 =
+- LCCR3_PixClkDiv(34) + LCCR3_ACBsDiv(512) +
+- LCCR3_ACBsCntOff + LCCR3_HorSnchL + LCCR3_VrtSnchL +
+- LCCR3_PixFlEdg;
+-#endif
+-#if defined LART_VIDEO_OUT
+- shadow.lccr0 =
+- LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
+- LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+- LCCR0_DMADel(0);
+- shadow.lccr1 =
+- LCCR1_DisWdth(640) + LCCR1_HorSnchWdth(95) +
+- LCCR1_BegLnDel(40) + LCCR1_EndLnDel(24);
+- shadow.lccr2 =
+- LCCR2_DisHght(480) + LCCR2_VrtSnchWdth(2) +
+- LCCR2_BegFrmDel(32) + LCCR2_EndFrmDel(11);
+- shadow.lccr3 =
+- LCCR3_PixClkDiv(8) + LCCR3_ACBsDiv(512) +
+- LCCR3_ACBsCntOff + LCCR3_HorSnchH + LCCR3_VrtSnchH +
+- LCCR3_PixFlEdg + LCCR3_OutEnL;
+-#endif
+- }
+- if (machine_is_graphicsclient()) {
+- DPRINTK("Configuring GraphicsClient LCD\n");
+- shadow.lccr0 =
+- LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act;
+- shadow.lccr1 =
+- LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(9) +
+- LCCR1_EndLnDel(54) + LCCR1_BegLnDel(54);
+- shadow.lccr2 =
+- LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(9) +
+- LCCR2_EndFrmDel(32) + LCCR2_BegFrmDel(24);
+- shadow.lccr3 =
+- LCCR3_PixClkDiv(10) + LCCR3_ACBsDiv(2) +
+- LCCR3_ACBsCntOff + LCCR3_HorSnchL + LCCR3_VrtSnchL;
+- }
+- if (machine_is_omnimeter()) {
+- DPRINTK("Configuring OMNI LCD\n");
+- shadow.lccr0 = LCCR0_LEN | LCCR0_CMS | LCCR0_DPD;
+- shadow.lccr1 =
+- LCCR1_BegLnDel(10) + LCCR1_EndLnDel(10) +
+- LCCR1_HorSnchWdth(1) + LCCR1_DisWdth(var->xres);
+- shadow.lccr2 = LCCR2_DisHght(var->yres);
+- shadow.lccr3 =
+- LCCR3_ACBsDiv(0xFF) + LCCR3_PixClkDiv(44);
+-//jca (GetPCD(25) << LCD3_V_PCD);
+- }
+- if (machine_is_xp860()) {
+- DPRINTK("Configuring XP860 LCD\n");
+- shadow.lccr0 =
+- LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
+- LCCR0_LtlEnd + LCCR0_LDM + LCCR0_ERM + LCCR0_DMADel(0);
+- shadow.lccr1 =
+- LCCR1_DisWdth(var->xres) +
+- LCCR1_HorSnchWdth(var->hsync_len) +
+- LCCR1_BegLnDel(var->left_margin) +
+- LCCR1_EndLnDel(var->right_margin);
+- shadow.lccr2 =
+- LCCR2_DisHght(var->yres) +
+- LCCR2_VrtSnchWdth(var->vsync_len) +
+- LCCR2_BegFrmDel(var->upper_margin) +
+- LCCR2_EndFrmDel(var->lower_margin);
+- shadow.lccr3 =
+- LCCR3_PixClkDiv(6) + LCCR3_HorSnchL + LCCR3_VrtSnchL;
+- }
+-
+- /*
+- * Ok, since we're calculating these values, we want to know
+- * if the calculation is correct. If you see any of these
+- * messages _PLEASE_ report the incident to me for diagnosis,
+- * including details about what was happening when the
+- * messages appeared. --rmk, 30 March 2001
+- */
+- if (shadow.lccr0 != new_regs->lccr0) {
+- printk(KERN_ERR "LCCR1 mismatch: 0x%08x != 0x%08x\n",
+- shadow.lccr1, new_regs->lccr1);
+- different = 1;
+- }
+- if (shadow.lccr1 != new_regs->lccr1) {
+- printk(KERN_ERR "LCCR1 mismatch: 0x%08x != 0x%08x\n",
+- shadow.lccr1, new_regs->lccr1);
+- different = 1;
+- }
+- if (shadow.lccr2 != new_regs->lccr2) {
+- printk(KERN_ERR "LCCR2 mismatch: 0x%08x != 0x%08x\n",
+- shadow.lccr2, new_regs->lccr2);
+- different = 1;
+- }
+- if (shadow.lccr3 != new_regs->lccr3) {
+- printk(KERN_ERR "LCCR3 mismatch: 0x%08x != 0x%08x\n",
+- shadow.lccr3, new_regs->lccr3);
+- different = 1;
+- }
+- if (different) {
+- printk(KERN_ERR "var: xres=%d hslen=%d lm=%d rm=%d\n",
+- var->xres, var->hsync_len,
+- var->left_margin, var->right_margin);
+- printk(KERN_ERR "var: yres=%d vslen=%d um=%d bm=%d\n",
+- var->yres, var->vsync_len,
+- var->upper_margin, var->lower_margin);
+-
+- printk(KERN_ERR "Please report this to Russell King "
+- "<rmk at arm.linux.org.uk>\n");
+- }
+-
+- DPRINTK("olccr0 = 0x%08x\n", shadow.lccr0);
+- DPRINTK("olccr1 = 0x%08x\n", shadow.lccr1);
+- DPRINTK("olccr2 = 0x%08x\n", shadow.lccr2);
+- DPRINTK("olccr3 = 0x%08x\n", shadow.lccr3);
+-}
+-#else
+-#define sa1100fb_check_shadow(regs,var,pcd)
+-#endif
+-
+-
+-
+ /*
+ * IMHO this looks wrong. In 8BPP, length should be 8.
+ */
+@@ -488,23 +262,22 @@
+ #endif
+ #endif
+
+-#ifdef CONFIG_SA1100_H3600
+-static struct sa1100fb_mach_info h3600_info __initdata = {
+-#ifdef CONFIG_IPAQ_H3100
+- pixclock: 407766, bpp: 4,
++#ifdef CONFIG_SA1100_H3XXX
++static struct sa1100fb_mach_info h3800_info __initdata = {
++ pixclock: 174757, bpp: 16,
+ xres: 320, yres: 240,
+
+- hsync_len: 26, vsync_len: 41,
+- left_margin: 4, upper_margin: 0,
+- right_margin: 4, lower_margin: 0,
++ hsync_len: 3, vsync_len: 3,
++ left_margin: 12, upper_margin: 10,
++ right_margin: 17, lower_margin: 1,
+
+- sync: FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+- cmap_greyscale: 1, cmap_static: 1,
+- cmap_inverse: 1,
++ sync: 0, cmap_static: 1,
+
+- lccr0: LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
+- lccr3: LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
+-#else
++ lccr0: LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++ lccr3: LCCR3_ACBsCntOff | LCCR3_PixFlEdg | LCCR3_OutEnH,
++};
++
++static struct sa1100fb_mach_info h3600_info __initdata = {
+ pixclock: 174757, bpp: 16,
+ xres: 320, yres: 240,
+
+@@ -512,11 +285,10 @@
+ left_margin: 12, upper_margin: 10,
+ right_margin: 17, lower_margin: 1,
+
+- sync: 0,
++ sync: 0, cmap_static: 1,
+
+ lccr0: LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+- lccr3: LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
+-#endif
++ lccr3: LCCR3_ACBsCntOff | LCCR3_OutEnH | LCCR3_PixFlEdg,
+ };
+
+ static struct sa1100fb_rgb h3600_rgb_16 = {
+@@ -525,6 +297,22 @@
+ blue: { offset: 1, length: 4, },
+ transp: { offset: 0, length: 0, },
+ };
++
++static struct sa1100fb_mach_info h3100_info __initdata = {
++ pixclock: 406977, bpp: 4,
++ xres: 320, yres: 240,
++
++ hsync_len: 26, vsync_len: 41,
++ left_margin: 4, upper_margin: 0,
++ right_margin: 4, lower_margin: 0,
++
++ sync: FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++ cmap_greyscale: 1,
++ cmap_inverse: 1,
++
++ lccr0: LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
++ lccr3: LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
++};
+ #endif
+
+ #ifdef CONFIG_SA1100_BRUTUS
+@@ -618,6 +406,58 @@
+
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+ static struct sa1100fb_mach_info graphicsclient_info __initdata = {
++// for LQ64D343
++ pixclock: 53500, bpp: 8,
++ xres: 640, yres: 480,
++
++ hsync_len: 9, vsync_len: 9,
++ left_margin: 54, upper_margin: 24,
++ right_margin: 54, lower_margin: 32,
++
++ sync: 0,
++
++ lccr0: LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++ lccr3: LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
++};
++#endif
++
++#ifdef CONFIG_SA1100_GRAPHICSMASTER
++static struct sa1100fb_mach_info graphicsmaster_info __initdata = {
++// for LQ64D343
++ pixclock: 53500, bpp: 8,
++ xres: 640, yres: 480,
++
++ hsync_len: 9, vsync_len: 9,
++ left_margin: 54, upper_margin: 24,
++ right_margin: 54, lower_margin: 32,
++
++ sync: 0,
++
++ lccr0: LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++ lccr3: LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
++};
++#endif
++
++#ifdef CONFIG_SA1100_ADSBITSY
++static struct sa1100fb_mach_info adsbitsy_info __initdata = {
++// for LQ64D343
++ pixclock: 53500, bpp: 8,
++ xres: 640, yres: 480,
++
++ hsync_len: 9, vsync_len: 9,
++ left_margin: 54, upper_margin: 24,
++ right_margin: 54, lower_margin: 32,
++
++ sync: 0,
++
++ lccr0: LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++ lccr3: LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
++};
++#endif
++
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++static struct sa1100fb_mach_info adsbitsyplus_info __initdata = {
++// for LQ64D343
+ pixclock: 53500, bpp: 8,
+ xres: 640, yres: 480,
+
+@@ -717,6 +557,40 @@
+ };
+ #endif
+
++#ifdef CONFIG_SA1100_FRODO
++static struct sa1100fb_mach_info frodo_kit01_info __initdata =
++{
++ /* best would be 41731 (25.8mhz), but we can only do 14.743mhz at 191.7mhz clock speed */
++ pixclock: 73030, bpp: 16,
++ xres: 640, yres: 480,
++
++ hsync_len: 32, vsync_len: 19,
++ left_margin: 120, upper_margin: 33,
++ right_margin: 17, lower_margin: 12,
++
++ sync: 0,
++
++ lccr0: LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++ lccr3: LCCR3_OutEnH | LCCR3_PixFlEdg
++};
++#endif
++
++#ifdef CONFIG_SA1100_SHANNON
++static struct sa1100fb_mach_info shannon_info __initdata = {
++ pixclock: 152500, bpp: 8,
++ xres: 640, yres: 480,
++
++ hsync_len: 4, vsync_len: 3,
++ left_margin: 2, upper_margin: 0,
++ right_margin: 1, lower_margin: 0,
++
++ sync: FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++
++ lccr0: LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
++ lccr3: LCCR3_ACBsDiv(512),
++};
++#endif
++
+ #ifdef CONFIG_SA1100_OMNIMETER
+ static struct sa1100fb_mach_info omnimeter_info __initdata = {
+ pixclock: 0, bpp: 4,
+@@ -752,7 +626,24 @@
+ sync: FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+
+ lccr0: LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+- lccr3: LCCR3_OutEnH | LCCR3_PixFlEdg,
++ lccr3: LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsCntOff,
++};
++#endif
++
++#ifdef CONFIG_SA1100_SIMPUTER
++static struct sa1100fb_mach_info simputer_info __initdata = {
++ pixclock: 70000, bpp: 4,
++ xres: 320, yres: 240,
++
++ hsync_len: 9, vsync_len: 2,
++ left_margin: 9, upper_margin: 0,
++ right_margin: 2, lower_margin: 0,
++
++ cmap_greyscale: 1,
++ sync: FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT ,
++
++ lccr0: LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono,
++ lccr3: LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(202),
+ };
+ #endif
+
+@@ -849,11 +740,17 @@
+ #endif
+ }
+ #endif
+-#ifdef CONFIG_SA1100_H3600
++#ifdef CONFIG_SA1100_H3XXX
+ if (machine_is_h3600()) {
+ inf = &h3600_info;
+ fbi->rgb[RGB_16] = &h3600_rgb_16;
+ }
++ if (machine_is_h3100()) {
++ inf = &h3100_info;
++ }
++ if (machine_is_h3800()) {
++ inf = &h3800_info;
++ }
+ #endif
+ #ifdef CONFIG_SA1100_BRUTUS
+ if (machine_is_brutus()) {
+@@ -876,6 +773,22 @@
+ inf = &graphicsclient_info;
+ }
+ #endif
++#ifdef CONFIG_SA1100_GRAPHICSMASTER
++ if (machine_is_graphicsmaster()) {
++ inf = &graphicsmaster_info;
++ }
++#endif
++#ifdef CONFIG_SA1100_ADSBITSY
++ if (machine_is_adsbitsy()) {
++ inf = &adsbitsy_info;
++ }
++#endif
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++ if (machine_is_adsbitsyplus()) {
++ inf = &adsbitsyplus_info;
++ }
++ }
++#endif
+ #ifdef CONFIG_SA1100_HUW_WEBPANEL
+ if (machine_is_huw_webpanel()) {
+ inf = &huw_webpanel_info;
+@@ -897,6 +810,21 @@
+ #endif
+ }
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++ if (machine_is_frodo()) {
++ inf = &frodo_kit01_info;
++ }
++#endif
++#ifdef CONFIG_SA1100_SHANNON
++ if (machine_is_shannon()) {
++ inf = &shannon_info;
++ }
++#endif
++#ifdef CONFIG_SA1100_SIMPUTER
++ if (machine_is_simputer()) {
++ inf = &simputer_info;
++ }
++#endif
+ #ifdef CONFIG_SA1100_OMNIMETER
+ if (machine_is_omnimeter()) {
+ inf = &omnimeter_info;
+@@ -1556,7 +1484,8 @@
+ unsigned int pcd;
+
+ if (pixclock) {
+- pcd = get_cclk_frequency() * pixclock;
++ pcd = cpufreq_get(0) / 100;
++ pcd *= pixclock;
+ pcd /= 10000000;
+ pcd += 1; /* make up for integer math truncations */
+ } else {
+@@ -1659,8 +1588,6 @@
+ if (pcd)
+ new_regs.lccr3 |= LCCR3_PixClkDiv(pcd);
+
+- sa1100fb_check_shadow(&new_regs, var, pcd);
+-
+ DPRINTK("nlccr0 = 0x%08x\n", new_regs.lccr0);
+ DPRINTK("nlccr1 = 0x%08x\n", new_regs.lccr1);
+ DPRINTK("nlccr2 = 0x%08x\n", new_regs.lccr2);
+@@ -1733,6 +1660,10 @@
+ if (machine_is_omnimeter())
+ LEDBacklightOn();
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++ if (machine_is_frodo())
++ frodo_cpld_set (FRODO_CPLD_GENERAL,FRODO_LCD_BACKLIGHT);
++#endif
+ }
+
+ /*
+@@ -1755,6 +1686,10 @@
+ if (machine_is_omnimeter())
+ LEDBacklightOff();
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++ if (machine_is_frodo())
++ frodo_cpld_clear (FRODO_CPLD_GENERAL,FRODO_LCD_BACKLIGHT);
++#endif
+ }
+
+ static void sa1100fb_power_up_lcd(struct sa1100fb_info *fbi)
+@@ -1773,20 +1708,25 @@
+ if (machine_is_omnimeter())
+ LCDPowerOn();
+ #endif
+-#ifdef CONFIG_SA1100_H3600
+- if (machine_is_h3600()) {
+- set_h3600_egpio(EGPIO_H3600_LCD_ON |
+- EGPIO_H3600_LCD_PCI |
+- EGPIO_H3600_LCD_5V_ON |
+- EGPIO_H3600_LVDD_ON);
+- }
+-#endif
++ if (machine_is_h3xxx())
++ set_h3600_egpio( IPAQ_EGPIO_LCD_ON ); /* Turn on power to the LCD */
+ #ifdef CONFIG_SA1100_STORK
+ if (machine_is_stork()) {
+ storkSetLCDCPLD(0, 1);
+ storkSetLatchA(STORK_LCD_BACKLIGHT_INVERTER_ON);
+ }
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++ if (machine_is_frodo())
++ sa1100fb_backlight_on(fbi);
++#endif
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++ if (machine_is_adsbitsyplus()) {
++ ADS_CPLD_PCON &= ~ADS_PCON_PANEL_ON;
++ ADS_CPLD_SUPPC |= ADS_SUPPC_VEE_ON;
++ }
++#endif
++
+ }
+
+ static void sa1100fb_power_down_lcd(struct sa1100fb_info *fbi)
+@@ -1802,20 +1742,24 @@
+ if (machine_is_huw_webpanel())
+ BCR_set(BCR_TFT_NPWR);
+ #endif
+-#ifdef CONFIG_SA1100_H3600
+- if (machine_is_h3600()) {
+- clr_h3600_egpio(EGPIO_H3600_LCD_ON |
+- EGPIO_H3600_LCD_PCI |
+- EGPIO_H3600_LCD_5V_ON |
+- EGPIO_H3600_LVDD_ON);
+- }
+-#endif
++ if (machine_is_h3xxx())
++ clr_h3600_egpio( IPAQ_EGPIO_LCD_ON );
+ #ifdef CONFIG_SA1100_STORK
+ if (machine_is_stork()) {
+ storkSetLCDCPLD(0, 0);
+ storkClearLatchA(STORK_LCD_BACKLIGHT_INVERTER_ON);
+ }
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++ if (machine_is_frodo())
++ sa1100fb_backlight_off(fbi);
++#endif
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++ if (machine_is_adsbitsyplus()) {
++ ADS_CPLD_PCON |= ADS_PCON_PANEL_ON;
++ ADS_CPLD_SUPPC &= ~ADS_SUPPC_VEE_ON;
++ }
++#endif
+ }
+
+ static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
+@@ -1911,15 +1855,29 @@
+ LCCR0 |= LCCR0_LEN;
+
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+-#error Where is GPIO24 set as an output? Can we fit this in somewhere else?
+ if (machine_is_graphicsclient()) {
+ // From ADS doc again...same as disable
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(20 * HZ / 1000);
+- GPSR |= GPIO_GPIO24;
++ GPDR |= GPIO_GPIO24;
++ GPSR = GPIO_GPIO24;
++ }
++#endif
++#ifdef CONFIG_SA1100_GRAPHICSMASTER
++ if (machine_is_graphicsmaster()) {
++ // From ADS doc again...same as disable
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout(20 * HZ / 1000);
++ GPDR |= GPIO_GPIO24;
++ GPSR = GPIO_GPIO24;
+ }
+ #endif
+
++ if (machine_is_shannon()) {
++ GPDR |= SHANNON_GPIO_DISP_EN;
++ GPSR |= SHANNON_GPIO_DISP_EN;
++ }
++
+ DPRINTK("DBAR1 = %p\n", DBAR1);
+ DPRINTK("DBAR2 = %p\n", DBAR2);
+ DPRINTK("LCCR0 = 0x%08x\n", LCCR0);
+@@ -1935,7 +1893,6 @@
+ DPRINTK("Disabling LCD controller\n");
+
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+-#error Where is GPIO24 set as an output? Can we fit this in somewhere else?
+ if (machine_is_graphicsclient()) {
+ /*
+ * From ADS internal document:
+@@ -1944,6 +1901,22 @@
+ *
+ * We'll wait 20msec.
+ */
++ GPDR |= GPIO_GPIO24;
++ GPCR |= GPIO_GPIO24;
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout(20 * HZ / 1000);
++ }
++#endif
++#ifdef CONFIG_SA1100_GRAPHICSMASTER
++ if (machine_is_graphicsmaster()) {
++ /*
++ * From ADS internal document:
++ * GPIO24 should be LOW at least 10msec prior to disabling
++ * the LCD interface.
++ *
++ * We'll wait 20msec.
++ */
++ GPDR |= GPIO_GPIO24;
+ GPCR |= GPIO_GPIO24;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(20 * HZ / 1000);
+@@ -1958,12 +1931,15 @@
+ }
+ #endif
+
++ if (machine_is_shannon()) {
++ GPCR |= SHANNON_GPIO_DISP_EN;
++ }
++
+ add_wait_queue(&fbi->ctrlr_wait, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ LCSR = 0xffffffff; /* Clear LCD Status Register */
+ LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */
+- enable_irq(IRQ_LCD); /* Enable LCD IRQ */
+ LCCR0 &= ~LCCR0_LEN; /* Disable LCD Controller */
+
+ schedule_timeout(20 * HZ / 1000);
+@@ -2006,12 +1982,13 @@
+ * Disable controller for clock change. If the
+ * controller is already disabled, then do nothing.
+ */
+- if (old_state != C_DISABLE) {
++ if (old_state != C_DISABLE && old_state != C_DISABLE_PM) {
+ fbi->state = state;
+ sa1100fb_disable_controller(fbi);
+ }
+ break;
+
++ case C_DISABLE_PM:
+ case C_DISABLE:
+ /*
+ * Disable controller
+@@ -2050,6 +2027,16 @@
+ }
+ break;
+
++ case C_ENABLE_PM:
++ /*
++ * Re-enable the controller after PM. This is not
++ * perfect - think about the case where we were doing
++ * a clock change, and we suspended half-way through.
++ */
++ if (old_state != C_DISABLE_PM)
++ break;
++ /* fall through */
++
+ case C_ENABLE:
+ /*
+ * Power up the LCD screen, enable controller, and
+@@ -2162,10 +2149,10 @@
+
+ if (state == 0) {
+ /* Enter D0. */
+- set_ctrlr_state(fbi, C_ENABLE);
++ set_ctrlr_state(fbi, C_ENABLE_PM);
+ } else {
+ /* Enter D1-D3. Disable the LCD controller. */
+- set_ctrlr_state(fbi, C_DISABLE);
++ set_ctrlr_state(fbi, C_DISABLE_PM);
+ }
+ }
+ DPRINTK("done\n");
+@@ -2304,7 +2291,7 @@
+ goto failed;
+
+ ret = request_irq(IRQ_LCD, sa1100fb_handle_irq, SA_INTERRUPT,
+- fbi->fb.fix.id, fbi);
++ "LCD", fbi);
+ if (ret) {
+ printk(KERN_ERR "sa1100fb: request_irq failed: %d\n", ret);
+ goto failed;
+diff -urN kernel-source-2.4.27-8/drivers/video/sa1100fb.h kernel-source-2.4.27-8-arm-1/drivers/video/sa1100fb.h
+--- kernel-source-2.4.27-8/drivers/video/sa1100fb.h 2001-10-25 21:53:52.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/drivers/video/sa1100fb.h 2005-02-18 17:48:44.000000000 +0000
+@@ -127,6 +127,8 @@
+ #define C_DISABLE_CLKCHANGE (2)
+ #define C_ENABLE_CLKCHANGE (3)
+ #define C_REENABLE (4)
++#define C_DISABLE_PM (5)
++#define C_ENABLE_PM (6)
+
+ #define SA1100_NAME "SA1100"
+
+diff -urN kernel-source-2.4.27-8/drivers/video/vga16fb.c kernel-source-2.4.27-8-arm-1/drivers/video/vga16fb.c
+--- kernel-source-2.4.27-8/drivers/video/vga16fb.c 2001-11-14 22:52:20.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/drivers/video/vga16fb.c 2005-02-18 17:48:44.000000000 +0000
+@@ -142,7 +142,7 @@
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id,"VGA16 VGA");
+
+- fix->smem_start = VGA_FB_PHYS;
++ fix->smem_start = VGA_MAP_MEM(VGA_FB_PHYS);
+ fix->smem_len = VGA_FB_PHYS_LEN;
+ fix->type = FB_TYPE_VGA_PLANES;
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+@@ -896,7 +896,7 @@
+
+ /* XXX share VGA_FB_PHYS region with vgacon */
+
+- vga16fb.video_vbase = ioremap(VGA_FB_PHYS, VGA_FB_PHYS_LEN);
++ vga16fb.video_vbase = ioremap(VGA_MAP_MEM(VGA_FB_PHYS), VGA_FB_PHYS_LEN);
+ if (!vga16fb.video_vbase) {
+ printk(KERN_ERR "vga16fb: unable to map device\n");
+ return -ENOMEM;
+diff -urN kernel-source-2.4.27-8/fs/adfs/dir.c kernel-source-2.4.27-8-arm-1/fs/adfs/dir.c
+--- kernel-source-2.4.27-8/fs/adfs/dir.c 2000-09-18 23:14:06.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/adfs/dir.c 2005-02-18 17:48:44.000000000 +0000
+@@ -23,7 +23,7 @@
+ /*
+ * For future. This should probably be per-directory.
+ */
+-static rwlock_t adfs_dir_lock;
++static rwlock_t adfs_dir_lock = RW_LOCK_UNLOCKED;
+
+ static int
+ adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+diff -urN kernel-source-2.4.27-8/fs/adfs/map.c kernel-source-2.4.27-8-arm-1/fs/adfs/map.c
+--- kernel-source-2.4.27-8/fs/adfs/map.c 2001-10-25 21:53:53.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/adfs/map.c 2005-02-18 17:48:44.000000000 +0000
+@@ -13,22 +13,25 @@
+ #include <linux/adfs_fs.h>
+ #include <linux/spinlock.h>
+
++#include <asm/unaligned.h>
++
+ #include "adfs.h"
+
+ /*
+ * For the future...
+ */
+-static rwlock_t adfs_map_lock;
++static rwlock_t adfs_map_lock = RW_LOCK_UNLOCKED;
+
++/*
++ * This is fun. We need to load up to 19 bits from the map at an
++ * arbitary bit alignment. (We're limited to 19 bits by F+ version
++ * 2).
++ */
+ #define GET_FRAG_ID(_map,_start,_idmask) \
+ ({ \
+- unsigned long _v2, _frag; \
+- unsigned int _tmp; \
+- _tmp = _start >> 5; \
+- _frag = le32_to_cpu(_map[_tmp]); \
+- _v2 = le32_to_cpu(_map[_tmp + 1]); \
+- _tmp = start & 31; \
+- _frag = (_frag >> _tmp) | (_v2 << (32 - _tmp)); \
++ unsigned char *_m = _map + (_start >> 3); \
++ u32 _frag = get_unaligned((u32 *)_m); \
++ _frag >>= (_start & 7); \
+ _frag & _idmask; \
+ })
+
+@@ -44,14 +47,13 @@
+ const unsigned int frag_id, unsigned int *offset)
+ {
+ const unsigned int mapsize = dm->dm_endbit;
+- const unsigned int idmask = (1 << idlen) - 1;
+- unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1;
++ const u32 idmask = (1 << idlen) - 1;
++ unsigned char *map = dm->dm_bh->b_data + 4;
+ unsigned int start = dm->dm_startbit;
+ unsigned int mapptr;
++ u32 frag;
+
+ do {
+- unsigned long frag;
+-
+ frag = GET_FRAG_ID(map, start, idmask);
+ mapptr = start + idlen;
+
+@@ -59,15 +61,17 @@
+ * find end of fragment
+ */
+ {
+- unsigned long v2;
++ u32 v, *_map = (u32 *)map;
+
+- while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
++ v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
++ while (v == 0) {
+ mapptr = (mapptr & ~31) + 32;
+ if (mapptr >= mapsize)
+ goto error;
++ v = le32_to_cpu(_map[mapptr >> 5]);
+ }
+
+- mapptr += 1 + ffz(~v2);
++ mapptr += 1 + ffz(~v);
+ }
+
+ if (frag == frag_id)
+@@ -75,8 +79,11 @@
+ again:
+ start = mapptr;
+ } while (mapptr < mapsize);
++ return -1;
+
+ error:
++ printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
++ frag, start, mapptr);
+ return -1;
+
+ found:
+@@ -102,10 +109,10 @@
+ const unsigned int mapsize = dm->dm_endbit + 32;
+ const unsigned int idlen = asb->s_idlen;
+ const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
+- const unsigned int idmask = (1 << frag_idlen) - 1;
+- unsigned long *map = (unsigned long *)dm->dm_bh->b_data;
++ const u32 idmask = (1 << frag_idlen) - 1;
++ unsigned char *map = dm->dm_bh->b_data;
+ unsigned int start = 8, mapptr;
+- unsigned long frag;
++ u32 frag;
+ unsigned long total = 0;
+
+ /*
+@@ -133,15 +140,17 @@
+ * find end of fragment
+ */
+ {
+- unsigned long v2;
++ u32 v, *_map = (u32 *)map;
+
+- while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
++ v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
++ while (v == 0) {
+ mapptr = (mapptr & ~31) + 32;
+ if (mapptr >= mapsize)
+ goto error;
++ v = le32_to_cpu(_map[mapptr >> 5]);
+ }
+
+- mapptr += 1 + ffz(~v2);
++ mapptr += 1 + ffz(~v);
+ }
+
+ total += mapptr - start;
+diff -urN kernel-source-2.4.27-8/fs/adfs/super.c kernel-source-2.4.27-8-arm-1/fs/adfs/super.c
+--- kernel-source-2.4.27-8/fs/adfs/super.c 2002-02-25 19:38:07.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/fs/adfs/super.c 2005-02-18 17:48:44.000000000 +0000
+@@ -387,12 +387,22 @@
+ sb->u.adfs_sb.s_version = dr->format_version;
+ sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize;
+
++ printk(KERN_DEBUG "ADFS: idlen %d map bit size %d sector size %d\n",
++ dr->idlen, 1 << dr->log2bpmb, 1 << dr->log2secsize);
++ printk(KERN_DEBUG "ADFS: map size %d map2blk %d version %d share size %d\n",
++ sb->u.adfs_sb.s_map_size,
++ sb->u.adfs_sb.s_map2blk,
++ sb->u.adfs_sb.s_version,
++ 1 << sb->u.adfs_sb.s_log2sharesize);
++
+ sb->u.adfs_sb.s_map = adfs_read_map(sb, dr);
+ if (!sb->u.adfs_sb.s_map)
+ goto error_free_bh;
+
+ brelse(bh);
+
++ printk(KERN_DEBUG "ADFS: ids per zone %d\n", sb->u.adfs_sb.s_ids_per_zone);
++
+ /*
+ * set up enough so that we can read an inode
+ */
+diff -urN kernel-source-2.4.27-8/fs/binfmt_aout.c kernel-source-2.4.27-8-arm-1/fs/binfmt_aout.c
+--- kernel-source-2.4.27-8/fs/binfmt_aout.c 2005-01-19 09:57:58.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/fs/binfmt_aout.c 2005-02-18 17:48:44.000000000 +0000
+@@ -431,7 +431,11 @@
+ start_thread(regs, ex.a_entry, current->mm->start_stack);
+ if (current->ptrace & PT_PTRACED)
+ send_sig(SIGTRAP, current, 0);
++#ifndef __arm__
+ return 0;
++#else
++ return regs->ARM_r0;
++#endif
+ }
+
+ static int load_aout_library(struct file *file)
+@@ -461,8 +465,11 @@
+
+ /* For QMAGIC, the starting address is 0x20 into the page. We mask
+ this off to get the starting address for the page */
+-
++#ifndef __arm__
+ start_addr = ex.a_entry & 0xfffff000;
++#else
++ start_addr = ex.a_entry & 0xffff8000;
++#endif
+
+ if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
+ static unsigned long error_time;
+diff -urN kernel-source-2.4.27-8/fs/binfmt_aout.c.orig kernel-source-2.4.27-8-arm-1/fs/binfmt_aout.c.orig
+--- kernel-source-2.4.27-8/fs/binfmt_aout.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/binfmt_aout.c.orig 2005-01-19 09:57:58.000000000 +0000
+@@ -0,0 +1,527 @@
++/*
++ * linux/fs/binfmt_aout.c
++ *
++ * Copyright (C) 1991, 1992, 1996 Linus Torvalds
++ */
++
++#include <linux/module.h>
++
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/mm.h>
++#include <linux/mman.h>
++#include <linux/a.out.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/string.h>
++#include <linux/fs.h>
++#include <linux/file.h>
++#include <linux/stat.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/user.h>
++#include <linux/slab.h>
++#include <linux/binfmts.h>
++#include <linux/personality.h>
++#include <linux/init.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/pgalloc.h>
++
++static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
++static int load_aout_library(struct file*);
++static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file);
++
++extern void dump_thread(struct pt_regs *, struct user *);
++
++static struct linux_binfmt aout_format = {
++ NULL, THIS_MODULE, load_aout_binary, load_aout_library, aout_core_dump, PAGE_SIZE
++};
++
++#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE)
++
++static int set_brk(unsigned long start, unsigned long end)
++{
++ start = PAGE_ALIGN(start);
++ end = PAGE_ALIGN(end);
++ if (end > start) {
++ unsigned long addr = do_brk_locked(start, end - start);
++ if (BAD_ADDR(addr))
++ return addr;
++ }
++ return 0;
++}
++
++/*
++ * These are the only things you should do on a core-file: use only these
++ * macros to write out all the necessary info.
++ */
++
++static int dump_write(struct file *file, const void *addr, int nr)
++{
++ return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
++}
++
++#define DUMP_WRITE(addr, nr) \
++ if (!dump_write(file, (void *)(addr), (nr))) \
++ goto end_coredump;
++
++#define DUMP_SEEK(offset) \
++if (file->f_op->llseek) { \
++ if (file->f_op->llseek(file,(offset),0) != (offset)) \
++ goto end_coredump; \
++} else file->f_pos = (offset)
++
++/*
++ * Routine writes a core dump image in the current directory.
++ * Currently only a stub-function.
++ *
++ * Note that setuid/setgid files won't make a core-dump if the uid/gid
++ * changed due to the set[u|g]id. It's enforced by the "current->mm->dumpable"
++ * field, which also makes sure the core-dumps won't be recursive if the
++ * dumping of the process results in another error..
++ */
++
++static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
++{
++ mm_segment_t fs;
++ int has_dumped = 0;
++ unsigned long dump_start, dump_size;
++ struct user dump;
++#if defined(__alpha__)
++# define START_DATA(u) (u.start_data)
++#elif defined(__arm__)
++# define START_DATA(u) ((u.u_tsize << PAGE_SHIFT) + u.start_code)
++#elif defined(__sparc__)
++# define START_DATA(u) (u.u_tsize)
++#elif defined(__i386__) || defined(__mc68000__) || defined(__arch_um__)
++# define START_DATA(u) (u.u_tsize << PAGE_SHIFT)
++#endif
++#ifdef __sparc__
++# define START_STACK(u) ((regs->u_regs[UREG_FP]) & ~(PAGE_SIZE - 1))
++#else
++# define START_STACK(u) (u.start_stack)
++#endif
++
++ fs = get_fs();
++ set_fs(KERNEL_DS);
++ has_dumped = 1;
++ current->flags |= PF_DUMPCORE;
++ strncpy(dump.u_comm, current->comm, sizeof(current->comm));
++#ifndef __sparc__
++ dump.u_ar0 = (void *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump)));
++#endif
++ dump.signal = signr;
++ dump_thread(regs, &dump);
++
++/* If the size of the dump file exceeds the rlimit, then see what would happen
++ if we wrote the stack, but not the data area. */
++#ifdef __sparc__
++ if ((dump.u_dsize+dump.u_ssize) >
++ current->rlim[RLIMIT_CORE].rlim_cur)
++ dump.u_dsize = 0;
++#else
++ if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
++ current->rlim[RLIMIT_CORE].rlim_cur)
++ dump.u_dsize = 0;
++#endif
++
++/* Make sure we have enough room to write the stack and data areas. */
++#ifdef __sparc__
++ if ((dump.u_ssize) >
++ current->rlim[RLIMIT_CORE].rlim_cur)
++ dump.u_ssize = 0;
++#else
++ if ((dump.u_ssize+1) * PAGE_SIZE >
++ current->rlim[RLIMIT_CORE].rlim_cur)
++ dump.u_ssize = 0;
++#endif
++
++/* make sure we actually have a data and stack area to dump */
++ set_fs(USER_DS);
++#ifdef __sparc__
++ if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize))
++ dump.u_dsize = 0;
++ if (verify_area(VERIFY_READ, (void *) START_STACK(dump), dump.u_ssize))
++ dump.u_ssize = 0;
++#else
++ if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize << PAGE_SHIFT))
++ dump.u_dsize = 0;
++ if (verify_area(VERIFY_READ, (void *) START_STACK(dump), dump.u_ssize << PAGE_SHIFT))
++ dump.u_ssize = 0;
++#endif
++
++ set_fs(KERNEL_DS);
++/* struct user */
++ DUMP_WRITE(&dump,sizeof(dump));
++/* Now dump all of the user data. Include malloced stuff as well */
++#ifndef __sparc__
++ DUMP_SEEK(PAGE_SIZE);
++#endif
++/* now we start writing out the user space info */
++ set_fs(USER_DS);
++/* Dump the data area */
++ if (dump.u_dsize != 0) {
++ dump_start = START_DATA(dump);
++#ifdef __sparc__
++ dump_size = dump.u_dsize;
++#else
++ dump_size = dump.u_dsize << PAGE_SHIFT;
++#endif
++ DUMP_WRITE(dump_start,dump_size);
++ }
++/* Now prepare to dump the stack area */
++ if (dump.u_ssize != 0) {
++ dump_start = START_STACK(dump);
++#ifdef __sparc__
++ dump_size = dump.u_ssize;
++#else
++ dump_size = dump.u_ssize << PAGE_SHIFT;
++#endif
++ DUMP_WRITE(dump_start,dump_size);
++ }
++/* Finally dump the task struct. Not be used by gdb, but could be useful */
++ set_fs(KERNEL_DS);
++ DUMP_WRITE(current,sizeof(*current));
++end_coredump:
++ set_fs(fs);
++ return has_dumped;
++}
++
++/*
++ * create_aout_tables() parses the env- and arg-strings in new user
++ * memory and creates the pointer tables from them, and puts their
++ * addresses on the "stack", returning the new stack pointer value.
++ */
++static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm)
++{
++ char **argv, **envp;
++ unsigned long * sp;
++ int argc = bprm->argc;
++ int envc = bprm->envc;
++
++ sp = (unsigned long *) ((-(unsigned long)sizeof(char *)) & (unsigned long) p);
++#ifdef __sparc__
++ /* This imposes the proper stack alignment for a new process. */
++ sp = (unsigned long *) (((unsigned long) sp) & ~7);
++ if ((envc+argc+3)&1) --sp;
++#endif
++#ifdef __alpha__
++/* whee.. test-programs are so much fun. */
++ put_user(0, --sp);
++ put_user(0, --sp);
++ if (bprm->loader) {
++ put_user(0, --sp);
++ put_user(0x3eb, --sp);
++ put_user(bprm->loader, --sp);
++ put_user(0x3ea, --sp);
++ }
++ put_user(bprm->exec, --sp);
++ put_user(0x3e9, --sp);
++#endif
++ sp -= envc+1;
++ envp = (char **) sp;
++ sp -= argc+1;
++ argv = (char **) sp;
++#if defined(__i386__) || defined(__mc68000__) || defined(__arm__) || defined(__arch_um__)
++ put_user((unsigned long) envp,--sp);
++ put_user((unsigned long) argv,--sp);
++#endif
++ put_user(argc,--sp);
++ current->mm->arg_start = (unsigned long) p;
++ while (argc-->0) {
++ char c;
++ put_user(p,argv++);
++ do {
++ get_user(c,p++);
++ } while (c);
++ }
++ put_user(NULL,argv);
++ current->mm->arg_end = current->mm->env_start = (unsigned long) p;
++ while (envc-->0) {
++ char c;
++ put_user(p,envp++);
++ do {
++ get_user(c,p++);
++ } while (c);
++ }
++ put_user(NULL,envp);
++ current->mm->env_end = (unsigned long) p;
++ return sp;
++}
++
++/*
++ * These are the functions used to load a.out style executables and shared
++ * libraries. There is no binary dependent code anywhere else.
++ */
++
++static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
++{
++ struct exec ex;
++ unsigned long error;
++ unsigned long fd_offset;
++ unsigned long rlim;
++ int retval;
++
++ ex = *((struct exec *) bprm->buf); /* exec-header */
++ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
++ N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
++ N_TRSIZE(ex) || N_DRSIZE(ex) ||
++ bprm->file->f_dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
++ return -ENOEXEC;
++ }
++
++ fd_offset = N_TXTOFF(ex);
++
++ /* Check initial limits. This avoids letting people circumvent
++ * size limits imposed on them by creating programs with large
++ * arrays in the data or bss.
++ */
++ rlim = current->rlim[RLIMIT_DATA].rlim_cur;
++ if (rlim >= RLIM_INFINITY)
++ rlim = ~0;
++ if (ex.a_data + ex.a_bss > rlim)
++ return -ENOMEM;
++
++ /* Flush all traces of the currently running executable */
++ retval = flush_old_exec(bprm);
++ if (retval)
++ return retval;
++
++ /* OK, This is the point of no return */
++#if defined(__alpha__)
++ SET_AOUT_PERSONALITY(bprm, ex);
++#elif defined(__sparc__)
++ set_personality(PER_SUNOS);
++#if !defined(__sparc_v9__)
++ memcpy(¤t->thread.core_exec, &ex, sizeof(struct exec));
++#endif
++#else
++ set_personality(PER_LINUX);
++#endif
++
++ current->mm->end_code = ex.a_text +
++ (current->mm->start_code = N_TXTADDR(ex));
++ current->mm->end_data = ex.a_data +
++ (current->mm->start_data = N_DATADDR(ex));
++ current->mm->brk = ex.a_bss +
++ (current->mm->start_brk = N_BSSADDR(ex));
++
++ current->mm->rss = 0;
++ current->mm->mmap = NULL;
++ compute_creds(bprm);
++ current->flags &= ~PF_FORKNOEXEC;
++#ifdef __sparc__
++ if (N_MAGIC(ex) == NMAGIC) {
++ loff_t pos = fd_offset;
++ /* Fuck me plenty... */
++ /* <AOL></AOL> */
++ error = do_brk_locked(N_TXTADDR(ex), ex.a_text);
++ bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
++ ex.a_text, &pos);
++ error = do_brk_locked(N_DATADDR(ex), ex.a_data);
++ bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
++ ex.a_data, &pos);
++ goto beyond_if;
++ }
++#endif
++
++ if (N_MAGIC(ex) == OMAGIC) {
++ unsigned long text_addr, map_size;
++ loff_t pos;
++
++ text_addr = N_TXTADDR(ex);
++
++#if defined(__alpha__) || defined(__sparc__)
++ pos = fd_offset;
++ map_size = ex.a_text+ex.a_data + PAGE_SIZE - 1;
++#else
++ pos = 32;
++ map_size = ex.a_text+ex.a_data;
++#endif
++
++ error = do_brk_locked(text_addr & PAGE_MASK, map_size);
++ if (error != (text_addr & PAGE_MASK)) {
++ send_sig(SIGKILL, current, 0);
++ return error;
++ }
++
++ error = bprm->file->f_op->read(bprm->file, (char *)text_addr,
++ ex.a_text+ex.a_data, &pos);
++ if ((signed long)error < 0) {
++ send_sig(SIGKILL, current, 0);
++ return error;
++ }
++
++ flush_icache_range(text_addr, text_addr+ex.a_text+ex.a_data);
++ } else {
++ static unsigned long error_time, error_time2;
++ if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
++ (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ)
++ {
++ printk(KERN_NOTICE "executable not page aligned\n");
++ error_time2 = jiffies;
++ }
++
++ if ((fd_offset & ~PAGE_MASK) != 0 &&
++ (jiffies-error_time) > 5*HZ)
++ {
++ printk(KERN_WARNING
++ "fd_offset is not page aligned. Please convert program: %s\n",
++ bprm->file->f_dentry->d_name.name);
++ error_time = jiffies;
++ }
++
++ if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
++ loff_t pos = fd_offset;
++ do_brk_locked(N_TXTADDR(ex), ex.a_text+ex.a_data);
++ bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
++ ex.a_text+ex.a_data, &pos);
++ flush_icache_range((unsigned long) N_TXTADDR(ex),
++ (unsigned long) N_TXTADDR(ex) +
++ ex.a_text+ex.a_data);
++ goto beyond_if;
++ }
++
++ down_write(¤t->mm->mmap_sem);
++ error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
++ PROT_READ | PROT_EXEC,
++ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
++ fd_offset);
++ up_write(¤t->mm->mmap_sem);
++
++ if (error != N_TXTADDR(ex)) {
++ send_sig(SIGKILL, current, 0);
++ return error;
++ }
++
++ down_write(¤t->mm->mmap_sem);
++ error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
++ PROT_READ | PROT_WRITE | PROT_EXEC,
++ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
++ fd_offset + ex.a_text);
++ up_write(¤t->mm->mmap_sem);
++ if (error != N_DATADDR(ex)) {
++ send_sig(SIGKILL, current, 0);
++ return error;
++ }
++ }
++beyond_if:
++ set_binfmt(&aout_format);
++
++ retval = set_brk(current->mm->start_brk, current->mm->brk);
++ if (retval < 0) {
++ send_sig(SIGKILL, current, 0);
++ return retval;
++ }
++
++ retval = setup_arg_pages(bprm);
++ if (retval < 0) {
++ /* Someone check-me: is this error path enough? */
++ send_sig(SIGKILL, current, 0);
++ return retval;
++ }
++
++ current->mm->start_stack =
++ (unsigned long) create_aout_tables((char *) bprm->p, bprm);
++#ifdef __alpha__
++ regs->gp = ex.a_gpvalue;
++#endif
++ start_thread(regs, ex.a_entry, current->mm->start_stack);
++ if (current->ptrace & PT_PTRACED)
++ send_sig(SIGTRAP, current, 0);
++ return 0;
++}
++
++static int load_aout_library(struct file *file)
++{
++ struct inode * inode;
++ unsigned long bss, start_addr, len;
++ unsigned long error;
++ int retval;
++ struct exec ex;
++
++ inode = file->f_dentry->d_inode;
++
++ retval = -ENOEXEC;
++ error = kernel_read(file, 0, (char *) &ex, sizeof(ex));
++ if (error != sizeof(ex))
++ goto out;
++
++ /* We come in here for the regular a.out style of shared libraries */
++ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
++ N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
++ inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
++ goto out;
++ }
++
++ if (N_FLAGS(ex))
++ goto out;
++
++ /* For QMAGIC, the starting address is 0x20 into the page. We mask
++ this off to get the starting address for the page */
++
++ start_addr = ex.a_entry & 0xfffff000;
++
++ if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
++ static unsigned long error_time;
++ loff_t pos = N_TXTOFF(ex);
++
++ if ((jiffies-error_time) > 5*HZ)
++ {
++ printk(KERN_WARNING
++ "N_TXTOFF is not page aligned. Please convert library: %s\n",
++ file->f_dentry->d_name.name);
++ error_time = jiffies;
++ }
++
++ do_brk_locked(start_addr, ex.a_text + ex.a_data + ex.a_bss);
++
++ file->f_op->read(file, (char *)start_addr,
++ ex.a_text + ex.a_data, &pos);
++ flush_icache_range((unsigned long) start_addr,
++ (unsigned long) start_addr + ex.a_text + ex.a_data);
++
++ retval = 0;
++ goto out;
++ }
++ /* Now use mmap to map the library into memory. */
++ down_write(¤t->mm->mmap_sem);
++ error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
++ PROT_READ | PROT_WRITE | PROT_EXEC,
++ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
++ N_TXTOFF(ex));
++ up_write(¤t->mm->mmap_sem);
++ retval = error;
++ if (error != start_addr)
++ goto out;
++
++ len = PAGE_ALIGN(ex.a_text + ex.a_data);
++ bss = ex.a_text + ex.a_data + ex.a_bss;
++ if (bss > len) {
++ error = do_brk_locked(start_addr + len, bss - len);
++ retval = error;
++ if (error != start_addr + len)
++ goto out;
++ }
++ retval = 0;
++out:
++ return retval;
++}
++
++static int __init init_aout_binfmt(void)
++{
++ return register_binfmt(&aout_format);
++}
++
++static void __exit exit_aout_binfmt(void)
++{
++ unregister_binfmt(&aout_format);
++}
++
++EXPORT_NO_SYMBOLS;
++
++module_init(init_aout_binfmt);
++module_exit(exit_aout_binfmt);
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-8/fs/exec.c kernel-source-2.4.27-8-arm-1/fs/exec.c
+--- kernel-source-2.4.27-8/fs/exec.c 2005-01-19 09:57:53.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/fs/exec.c 2005-02-18 17:48:44.000000000 +0000
+@@ -315,6 +315,7 @@
+ spin_unlock(&tsk->mm->page_table_lock);
+
+ /* no need for flush_tlb */
++ memc_update_addr(tsk->mm, *pte, address);
+ return;
+ out:
+ spin_unlock(&tsk->mm->page_table_lock);
+diff -urN kernel-source-2.4.27-8/fs/jffs/inode-v23.c kernel-source-2.4.27-8-arm-1/fs/jffs/inode-v23.c
+--- kernel-source-2.4.27-8/fs/jffs/inode-v23.c 2001-10-04 23:14:35.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/jffs/inode-v23.c 2005-02-18 17:48:44.000000000 +0000
+@@ -10,7 +10,7 @@
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+- * $Id: inode-v23.c,v 1.70 2001/10/02 09:16:02 dwmw2 Exp $
++ * $Id: inode-v23.c,v 1.72 2002/01/31 11:42:57 cdavies Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex at cendio.se), Cendio Systems AB
+@@ -48,6 +48,7 @@
+ #include <linux/stat.h>
+ #include <linux/blkdev.h>
+ #include <linux/quotaops.h>
++#include <linux/compatmac.h>
+ #include <asm/semaphore.h>
+ #include <asm/byteorder.h>
+ #include <asm/uaccess.h>
+@@ -58,6 +59,11 @@
+ #include "jffs_proc.h"
+ #endif
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2)
++#define minor(x) MINOR(x)
++#define major(x) MAJOR(x)
++#endif
++
+ static int jffs_remove(struct inode *dir, struct dentry *dentry, int type);
+
+ static struct super_operations jffs_ops;
+@@ -81,7 +87,7 @@
+ D1(printk(KERN_NOTICE "JFFS: Trying to mount device %s.\n",
+ kdevname(dev)));
+
+- if (MAJOR(dev) != MTD_BLOCK_MAJOR) {
++ if (major(dev) != MTD_BLOCK_MAJOR) {
+ printk(KERN_WARNING "JFFS: Trying to mount a "
+ "non-mtd device.\n");
+ return 0;
+@@ -358,7 +364,7 @@
+ inode->i_nlink = raw_inode->nlink;
+ inode->i_uid = raw_inode->uid;
+ inode->i_gid = raw_inode->gid;
+- inode->i_rdev = 0;
++ inode->i_rdev = NODEV;
+ inode->i_size = raw_inode->dsize;
+ inode->i_atime = raw_inode->atime;
+ inode->i_mtime = raw_inode->mtime;
+diff -urN kernel-source-2.4.27-8/fs/jffs/intrep.c kernel-source-2.4.27-8-arm-1/fs/jffs/intrep.c
+--- kernel-source-2.4.27-8/fs/jffs/intrep.c 2003-06-13 15:51:37.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/jffs/intrep.c 2005-02-18 17:48:44.000000000 +0000
+@@ -10,7 +10,7 @@
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+- * $Id: intrep.c,v 1.102 2001/09/23 23:28:36 dwmw2 Exp $
++ * $Id: intrep.c,v 1.104 2002/03/05 14:07:39 dwmw2 Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex at cendio.se), Cendio Systems AB
+@@ -772,6 +772,9 @@
+ __u32 free_chunk_size1;
+ __u32 free_chunk_size2;
+
++ __u32 largest_hole = 0;
++ __u32 hole_end_offset = 0;
++ __u32 head_offset;
+
+ #define NUMFREEALLOWED 2 /* 2 chunks of at least erase size space allowed */
+ int num_free_space = 0; /* Flag err if more than TWO
+@@ -884,6 +887,21 @@
+ (unsigned int) start,
+ (unsigned int)(test_start - start)));
+
++ D1(printk("Reducing start to 0x%x from 0x%x\n",
++ test_start, start));
++ if (largest_hole < test_start - start){
++
++ D3(printk("was hole = %x end_offset = %x\n",
++ largest_hole, hole_end_offset));
++ if (fmc->head) {
++ largest_hole = test_start - start;
++ hole_end_offset = test_start;
++ }
++ }
++
++ D3(printk("now = %x end_offset = %x\n",
++ largest_hole, hole_end_offset));
++
+ /* below, space from "start" to "pos" will be marked dirty. */
+ start = test_start;
+
+@@ -956,6 +974,19 @@
+ num_free_space++;
+ D1(printk("Free space accepted: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start, (unsigned int) (pos - start)));
++
++ if (largest_hole < pos - start) {
++
++ D3(printk("was hole = %x end_offset = %x\n",
++ largest_hole, hole_end_offset));
++ if (fmc->head){
++ largest_hole = pos - start;
++ hole_end_offset = pos;
++ }
++
++ D3(printk("now = %x end_offset = %x\n",
++ largest_hole, hole_end_offset));
++ }
+ }else{
+ num_free_spc_not_accp++;
+ D1(printk("Free space (#%i) found but *Not* accepted: Starting "
+@@ -1002,9 +1033,11 @@
+ to scan for the magic pattern. */
+ D1(printk("*************** Dirty flash memory or "
+ "bad inode: "
+- "hexdump(pos = 0x%lx, len = 128):\n",
+- (long)pos));
+- D1(jffs_hexdump(fmc->mtd, pos, 128));
++ "hexdump(pos = 0x%lx, len = %d):\n",
++ (long)pos,
++ end - pos > 128 ? 128 : end - pos));
++ D1(jffs_hexdump(fmc->mtd, pos,
++ end - pos > 128 ? 128 : end - pos));
+
+ for (pos += 4; pos < end; pos += 4) {
+ switch (flash_read_u32(fmc->mtd, pos)) {
+@@ -1197,12 +1230,6 @@
+
+ return -ENOMEM;
+ }
+- if ((err = jffs_insert_node(c, 0, &raw_inode,
+- name, node)) < 0) {
+- printk("JFFS: Failed to handle raw inode. "
+- "(err = %d)\n", err);
+- break;
+- }
+ if (raw_inode.rename) {
+ struct jffs_delete_list *dl
+ = (struct jffs_delete_list *)
+@@ -1226,6 +1253,12 @@
+ c->delete_list = dl;
+ node->data_size = 0;
+ }
++ if ((err = jffs_insert_node(c, 0, &raw_inode,
++ name, node)) < 0) {
++ printk("JFFS: Failed to handle raw inode. "
++ "(err = %d)\n", err);
++ break;
++ }
+ D3(jffs_print_node(node));
+ node = 0; /* Don't free the node! */
+ }
+@@ -1242,7 +1275,19 @@
+ jffs_free_node(node);
+ DJM(no_jffs_node--);
+ }
+- jffs_build_end(fmc);
++ if (fmc->head && fmc->tail_extra &&
++ fmc->head->offset + fmc->flash_size -
++ fmc->tail_extra->offset - fmc->tail_extra->size > largest_hole) {
++ head_offset = fmc->head->offset;
++ }
++ else {
++ head_offset = hole_end_offset;
++ }
++
++ if (jffs_build_end(fmc, head_offset) < 0) {
++ D(printk("jffs_build_end() failed\n"));
++ return -ENOMEM;
++ }
+
+ /* Free read buffer */
+ kfree (read_buf);
+diff -urN kernel-source-2.4.27-8/fs/jffs/jffs_fm.c kernel-source-2.4.27-8-arm-1/fs/jffs/jffs_fm.c
+--- kernel-source-2.4.27-8/fs/jffs/jffs_fm.c 2001-10-04 23:13:18.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/jffs/jffs_fm.c 2005-02-18 17:48:44.000000000 +0000
+@@ -10,7 +10,7 @@
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+- * $Id: jffs_fm.c,v 1.27 2001/09/20 12:29:47 dwmw2 Exp $
++ * $Id: jffs_fm.c,v 1.29 2002/01/22 09:48:16 cdavies Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex at cendio.se), Cendio Systems AB
+@@ -20,8 +20,14 @@
+ #include <linux/slab.h>
+ #include <linux/blkdev.h>
+ #include <linux/jffs.h>
++#include <linux/compatmac.h>
+ #include "jffs_fm.h"
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2)
++#define minor(x) MINOR(x)
++#define major(x) MAJOR(x)
++#endif
++
+ #if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE
+ static int jffs_mark_obsolete(struct jffs_fmcontrol *fmc, __u32 fm_offset);
+ #endif
+@@ -46,7 +52,7 @@
+ }
+ DJM(no_jffs_fmcontrol++);
+
+- mtd = get_mtd_device(NULL, MINOR(dev));
++ mtd = get_mtd_device(NULL, minor(dev));
+
+ if (!mtd) {
+ kfree(fmc);
+@@ -89,8 +95,8 @@
+
+ /* When the flash memory scan has completed, this function should be called
+ before use of the control structure. */
+-void
+-jffs_build_end(struct jffs_fmcontrol *fmc)
++int
++jffs_build_end(struct jffs_fmcontrol *fmc, __u32 head_offset)
+ {
+ D3(printk("jffs_build_end()\n"));
+
+@@ -99,13 +105,100 @@
+ fmc->tail = fmc->tail_extra;
+ }
+ else if (fmc->head_extra) {
++ struct jffs_fm *fm, *cur;
++
++ if (head_offset == fmc->head->offset){
++ fmc->tail->next = fmc->head_extra;
++ fmc->head_extra->prev = fmc->tail;
++ fmc->tail = fmc->tail_extra;
++ }
++ else {
+ fmc->tail_extra->next = fmc->head;
+ fmc->head->prev = fmc->tail_extra;
+ fmc->head = fmc->head_extra;
++ while (fmc->head->offset != head_offset){
++ fmc->tail->next = fmc->head;
++ fmc->head = fmc->head->next;
++ fmc->head->prev = 0;
++ fmc->tail->next->prev = fmc->tail;
++ fmc->tail = fmc->tail->next;
++ fmc->tail->next = 0;
++ }
++ }
++ /* Make sure the only free space we have is between tail and head.
++ */
++ for (cur = fmc->head; cur && cur != fmc->tail;) {
++ if (cur->offset + cur->size < cur->next->offset) {
++ if (!(fm = kmalloc(sizeof(struct jffs_fm), GFP_KERNEL))) {
++ D(printk("jffs_buid_end(): kmalloc failed!\n"));
++ return -ENOMEM;
++ }
++ DJM(no_jffs_fm++);
++ fm->size = cur->next->offset - cur->offset - cur->size;
++ fm->offset = cur->offset + cur->size;
++ fm->nodes = 0;
++ fm->next = cur->next;
++ fm->prev = cur;
++ cur->next->prev = fm;
++ cur->next = fm;
++ cur = fm->next;
++ fmc->free_size -= fm->size;
++ fmc->dirty_size += fm->size;
++ }
++ else if (cur->offset > cur->next->offset) {
++ if (cur->offset + cur->size < fmc->flash_size){
++ if (!(fm = kmalloc(sizeof(struct jffs_fm), GFP_KERNEL))){
++
++ D(printk("jffs_buid_end(): kmalloc failed!\n"));
++ return -ENOMEM;
++ }
++ DJM(no_jffs_fm++);
++ fm->size = fmc->flash_size -
++ cur->offset - cur->size;
++ fm->nodes = 0;
++ fm->offset = cur->offset + cur->size;
++ fm->next = cur->next;
++ fm->prev = cur;
++ cur->next->prev = fm;
++ cur->next = fm;
++ cur = fm->next;
++ fmc->free_size -= fm->size;
++ fmc->dirty_size += fm->size;
++ }
++ else {
++ cur = cur->next;
++ }
++ if (cur->offset > 0) {
++
++ if (!(fm = kmalloc(sizeof(struct jffs_fm), GFP_KERNEL))) {
++ D(printk("jffs_buid_end(): kmalloc failed!\n"));
++ return -ENOMEM;
++ }
++ DJM(no_jffs_fm++);
++ fm->size = cur->offset;
++ fm->nodes = 0;
++ fm->offset = 0;
++ fm->next = cur;
++ fm->prev = cur->prev;
++ cur->prev->next = fm;
++ cur->prev = fm;
++ fmc->free_size -= fm->size;
++ fmc->dirty_size += fm->size;
++ }
++ }
++ else if (cur->offset + cur->size != cur->next->offset) {
++ printk("jffs_build_end(): Internal error.\n");
++ return -EINVAL;
++ }
++ else {
++ cur = cur->next;
++ }
++ }
+ }
+ fmc->head_extra = 0; /* These two instructions should be omitted. */
+ fmc->tail_extra = 0;
+ D3(jffs_print_fmcontrol(fmc));
++ return 0;
+ }
+
+
+diff -urN kernel-source-2.4.27-8/fs/jffs/jffs_fm.h kernel-source-2.4.27-8-arm-1/fs/jffs/jffs_fm.h
+--- kernel-source-2.4.27-8/fs/jffs/jffs_fm.h 2001-10-04 23:13:18.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/jffs/jffs_fm.h 2005-02-18 17:48:44.000000000 +0000
+@@ -10,7 +10,7 @@
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+- * $Id: jffs_fm.h,v 1.13 2001/01/11 12:03:25 dwmw2 Exp $
++ * $Id: jffs_fm.h,v 1.14 2001/12/10 17:37:12 asanochkin Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex at cendio.se), Cendio Systems AB
+@@ -123,7 +123,7 @@
+
+
+ struct jffs_fmcontrol *jffs_build_begin(struct jffs_control *c, kdev_t dev);
+-void jffs_build_end(struct jffs_fmcontrol *fmc);
++int jffs_build_end(struct jffs_fmcontrol *fmc, __u32 head_offset);
+ void jffs_cleanup_fmcontrol(struct jffs_fmcontrol *fmc);
+
+ int jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size,
+diff -urN kernel-source-2.4.27-8/fs/partitions/Config.in kernel-source-2.4.27-8-arm-1/fs/partitions/Config.in
+--- kernel-source-2.4.27-8/fs/partitions/Config.in 2002-11-28 23:53:15.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/fs/partitions/Config.in 2005-02-18 17:48:44.000000000 +0000
+@@ -6,6 +6,7 @@
+ bool ' Acorn partition support' CONFIG_ACORN_PARTITION
+ if [ "$CONFIG_ACORN_PARTITION" != "n" ]; then
+ # bool ' Cumana partition support' CONFIG_ACORN_PARTITION_CUMANA
++ bool ' EESOX partition support' CONFIG_ACORN_PARTITION_EESOX
+ bool ' ICS partition support' CONFIG_ACORN_PARTITION_ICS
+ bool ' Native filecore partition support' CONFIG_ACORN_PARTITION_ADFS
+ bool ' PowerTec partition support' CONFIG_ACORN_PARTITION_POWERTEC
+@@ -52,6 +53,7 @@
+ define_bool CONFIG_ACORN_PARTITION y
+ define_bool CONFIG_ACORN_PARTITION_ADFS y
+ # define_bool CONFIG_ACORN_PARTITION_CUMANA y
++ define_bool CONFIG_ACORN_PARTITION_EESOX y
+ define_bool CONFIG_ACORN_PARTITION_ICS y
+ define_bool CONFIG_ACORN_PARTITION_POWERTEC y
+ define_bool CONFIG_ACORN_PARTITION_RISCIX y
+diff -urN kernel-source-2.4.27-8/fs/partitions/acorn.c kernel-source-2.4.27-8-arm-1/fs/partitions/acorn.c
+--- kernel-source-2.4.27-8/fs/partitions/acorn.c 2002-08-03 01:39:45.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/partitions/acorn.c 2005-02-18 17:48:44.000000000 +0000
+@@ -7,7 +7,10 @@
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+- * Scan ADFS partitions on hard disk drives.
++ * Scan ADFS partitions on hard disk drives. Unfortunately, there
++ * isn't a standard for partitioning drives on Acorn machines, so
++ * every single manufacturer of SCSI and IDE cards created their own
++ * method.
+ */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+@@ -18,10 +21,18 @@
+ #include <linux/genhd.h>
+ #include <linux/fs.h>
+ #include <linux/pagemap.h>
++#include <linux/adfs_fs.h>
+
+ #include "check.h"
+ #include "acorn.h"
+
++/*
++ * Partition types. (Oh for reusability)
++ */
++#define PARTITION_RISCIX_MFM 1
++#define PARTITION_RISCIX_SCSI 2
++#define PARTITION_LINUX 9
++
+ static void
+ adfspart_setgeometry(kdev_t dev, unsigned int secspertrack, unsigned int heads)
+ {
+@@ -61,6 +72,21 @@
+ }
+
+ #ifdef CONFIG_ACORN_PARTITION_RISCIX
++
++struct riscix_part {
++ __u32 start;
++ __u32 length;
++ __u32 one;
++ char name[16];
++};
++
++struct riscix_record {
++ __u32 magic;
++#define RISCIX_MAGIC (0x4a657320)
++ __u32 date;
++ struct riscix_part part[8];
++};
++
+ static int
+ riscix_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sect, int minor, unsigned long nr_sects)
+@@ -102,6 +128,15 @@
+ }
+ #endif
+
++#define LINUX_NATIVE_MAGIC 0xdeafa1de
++#define LINUX_SWAP_MAGIC 0xdeafab1e
++
++struct linux_part {
++ __u32 magic;
++ __u32 start_sect;
++ __u32 nr_sects;
++};
++
+ static int
+ linux_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sect, int minor, unsigned long nr_sects)
+@@ -136,7 +171,7 @@
+ }
+
+ #ifdef CONFIG_ACORN_PARTITION_CUMANA
+-static int
++int
+ adfspart_check_CUMANA(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+ {
+@@ -147,7 +182,7 @@
+ int first = 1;
+
+ /*
+- * Try Cumana style partitions - sector 3 contains ADFS boot block
++ * Try Cumana style partitions - sector 6 contains ADFS boot block
+ * with pointer to next 'drive'.
+ *
+ * There are unknowns in this code - is the 'cylinder number' of the
+@@ -163,13 +198,13 @@
+ struct adfs_discrecord *dr;
+ unsigned int nr_sects;
+
+- if (!(minor & mask))
+- break;
+-
+ data = read_dev_sector(bdev, start_blk * 2 + 6, §);
+ if (!data)
+ return -1;
+
++ if (!(minor & mask))
++ break;
++
+ dr = adfs_partition(hd, name, data, first_sector, minor++);
+ if (!dr)
+ break;
+@@ -229,7 +264,7 @@
+ * hda1 = ADFS partition on first drive.
+ * hda2 = non-ADFS partition.
+ */
+-static int
++int
+ adfspart_check_ADFS(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+ {
+@@ -282,11 +317,18 @@
+ break;
+ }
+ }
++ printk("\n");
+ return 1;
+ }
+ #endif
+
+ #ifdef CONFIG_ACORN_PARTITION_ICS
++
++struct ics_part {
++ __u32 start;
++ __s32 size;
++};
++
+ static int adfspart_check_ICSLinux(struct block_device *bdev, unsigned long block)
+ {
+ Sector sect;
+@@ -303,6 +345,22 @@
+ }
+
+ /*
++ * Check for a valid ICS partition using the checksum.
++ */
++static inline int valid_ics_sector(const unsigned char *data)
++{
++ unsigned long sum;
++ int i;
++
++ for (i = 0, sum = 0x50617274; i < 508; i++)
++ sum += data[i];
++
++ sum -= le32_to_cpu(*(__u32 *)(&data[508]));
++
++ return sum == 0;
++}
++
++/*
+ * Purpose: allocate ICS partitions.
+ * Params : hd - pointer to gendisk structure to store partition info.
+ * dev - device number to access.
+@@ -314,15 +372,14 @@
+ * hda2 = ADFS partition 1 on first drive.
+ * ..etc..
+ */
+-static int
++int
+ adfspart_check_ICS(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+ {
++ const unsigned char *data;
++ const struct ics_part *p;
++ unsigned int mask = (1 << hd->minor_shift) - 1;
+ Sector sect;
+- unsigned char *data;
+- unsigned long sum;
+- unsigned int i, mask = (1 << hd->minor_shift) - 1;
+- struct ics_part *p;
+
+ /*
+ * Try ICS style partitions - sector 0 contains partition info.
+@@ -331,21 +388,14 @@
+ if (!data)
+ return -1;
+
+- /*
+- * check for a valid checksum
+- */
+- for (i = 0, sum = 0x50617274; i < 508; i++)
+- sum += data[i];
+-
+- sum -= le32_to_cpu(*(__u32 *)(&data[508]));
+- if (sum) {
++ if (!valid_ics_sector(data)) {
+ put_dev_sector(sect);
+- return 0; /* not ICS partition table */
++ return 0;
+ }
+
+ printk(" [ICS]");
+
+- for (p = (struct ics_part *)data; p->size; p++) {
++ for (p = (const struct ics_part *)data; p->size; p++) {
+ unsigned long start;
+ long size;
+
+@@ -355,12 +405,19 @@
+ start = le32_to_cpu(p->start);
+ size = le32_to_cpu(p->size);
+
++ /*
++ * Negative sizes tell the RISC OS ICS driver to ignore
++ * this partition - in effect it says that this does not
++ * contain an ADFS filesystem.
++ */
+ if (size < 0) {
+ size = -size;
+
+ /*
+- * We use the first sector to identify what type
+- * this partition is...
++ * Our own extension - We use the first sector
++ * of the partition to identify what type this
++ * partition is. We must not make this visible
++ * to the filesystem.
+ */
+ if (size > 1 && adfspart_check_ICSLinux(bdev, start)) {
+ start += 1;
+@@ -375,10 +432,32 @@
+ }
+
+ put_dev_sector(sect);
++ printk("\n");
+ return 1;
+ }
+ #endif
+
++#ifdef CONFIG_ACORN_PARTITION_POWERTEC
++struct ptec_part {
++ __u32 unused1;
++ __u32 unused2;
++ __u32 start;
++ __u32 size;
++ __u32 unused5;
++ char type[8];
++};
++
++static inline int valid_ptec_sector(const unsigned char *data)
++{
++ unsigned char checksum = 0x2a;
++ int i;
++
++ for (i = 0; i < 511; i++)
++ checksum += data[i];
++
++ return checksum == data[511];
++}
++
+ /*
+ * Purpose: allocate ICS partitions.
+ * Params : hd - pointer to gendisk structure to store partition info.
+@@ -391,32 +470,27 @@
+ * hda2 = ADFS partition 1 on first drive.
+ * ..etc..
+ */
+-#ifdef CONFIG_ACORN_PARTITION_POWERTEC
+-static int
++int
+ adfspart_check_POWERTEC(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+ {
+ Sector sect;
+- unsigned char *data;
+- struct ptec_partition *p;
+- unsigned char checksum;
++ const unsigned char *data;
++ const struct ptec_part *p;
+ int i;
+
+ data = read_dev_sector(bdev, 0, §);
+ if (!data)
+ return -1;
+
+- for (checksum = 0x2a, i = 0; i < 511; i++)
+- checksum += data[i];
+-
+- if (checksum != data[511]) {
++ if (!valid_ptec_sector(data)) {
+ put_dev_sector(sect);
+ return 0;
+ }
+
+ printk(" [POWERTEC]");
+
+- for (i = 0, p = (struct ptec_partition *)data; i < 12; i++, p++) {
++ for (i = 0, p = (const struct ptec_part *)data; i < 12; i++, p++) {
+ unsigned long start;
+ unsigned long size;
+
+@@ -430,49 +504,82 @@
+ }
+
+ put_dev_sector(sect);
++ printk("\n");
+ return 1;
+ }
+ #endif
+
+-static int (*partfn[])(struct gendisk *, struct block_device *, unsigned long, int) = {
+-#ifdef CONFIG_ACORN_PARTITION_ICS
+- adfspart_check_ICS,
+-#endif
+-#ifdef CONFIG_ACORN_PARTITION_POWERTEC
+- adfspart_check_POWERTEC,
+-#endif
+-#ifdef CONFIG_ACORN_PARTITION_CUMANA
+- adfspart_check_CUMANA,
+-#endif
+-#ifdef CONFIG_ACORN_PARTITION_ADFS
+- adfspart_check_ADFS,
+-#endif
+- NULL
++#ifdef CONFIG_ACORN_PARTITION_EESOX
++struct eesox_part {
++ char magic[6];
++ char name[10];
++ u32 start;
++ u32 unused6;
++ u32 unused7;
++ u32 unused8;
+ };
++
++/*
++ * Guess who created this format?
++ */
++static const char eesox_name[] = {
++ 'N', 'e', 'i', 'l', ' ',
++ 'C', 'r', 'i', 't', 'c', 'h', 'e', 'l', 'l', ' ', ' '
++};
++
+ /*
+- * Purpose: initialise all the partitions on an ADFS drive.
+- * These may be other ADFS partitions or a Linux/RiscBSD/RISCiX
+- * partition.
++ * EESOX SCSI partition format.
+ *
+- * Params : hd - pointer to gendisk structure
+- * dev - device number to access
+- * first_sect - first available sector on the disk.
+- * first_minor - first available minor on this device.
++ * This is a goddamned awful partition format. We don't seem to store
++ * the size of the partition in this table, only the start addresses.
+ *
+- * Returns: -1 on error, 0 if not ADFS format, 1 if ok.
++ * There are two possibilities where the size comes from:
++ * 1. The individual ADFS boot block entries that are placed on the disk.
++ * 2. The start address of the next entry.
+ */
+-int acorn_partition(struct gendisk *hd, struct block_device *bdev,
+- unsigned long first_sect, int first_minor)
++int
++adfspart_check_EESOX(struct gendisk *hd, struct block_device *bdev,
++ unsigned long first_sector, int minor)
+ {
++ Sector sect;
++ const unsigned char *data;
++ unsigned char buffer[256];
++ struct eesox_part *p;
++ u32 start = first_sector;
+ int i;
+
+- for (i = 0; partfn[i]; i++) {
+- int r = partfn[i](hd, bdev, first_sect, first_minor);
+- if (r) {
+- if (r > 0)
+- printk("\n");
+- return r;
++ data = read_dev_sector(bdev, 7, §);
++ if (!data)
++ return -1;
++
++ /*
++ * "Decrypt" the partition table. God knows why...
++ */
++ for (i = 0; i < 256; i++)
++ buffer[i] = data[i] ^ eesox_name[i & 15];
++
++ put_dev_sector(sect);
++
++ for (i = 0, p = (struct eesox_part *)buffer; i < 8; i++, p++) {
++ u32 next;
++
++ if (memcmp(p->magic, "Eesox", 6))
++ break;
++
++ next = le32_to_cpu(p->start) + first_sector;
++ if (i)
++ add_gd_partition(hd, minor++, start, next - start);
++ start = next;
+ }
++
++ if (i != 0) {
++ unsigned long size;
++
++ size = hd->part[MINOR(to_kdev_t(bdev->bd_dev))].nr_sects;
++ add_gd_partition(hd, minor++, start, size - start);
++ printk("\n");
+ }
+- return 0;
++
++ return i ? 1 : 0;
+ }
++#endif
+diff -urN kernel-source-2.4.27-8/fs/partitions/acorn.h kernel-source-2.4.27-8-arm-1/fs/partitions/acorn.h
+--- kernel-source-2.4.27-8/fs/partitions/acorn.h 2001-11-22 19:48:07.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/fs/partitions/acorn.h 2005-02-18 17:48:44.000000000 +0000
+@@ -1,55 +1,28 @@
+ /*
+- * fs/partitions/acorn.h
++ * linux/fs/partitions/acorn.h
+ *
+- * Copyright (C) 1996-1998 Russell King
+- */
+-#include <linux/adfs_fs.h>
+-
+-/*
+- * Partition types. (Oh for reusability)
++ * Copyright (C) 1996-2001 Russell King.
++ *
++ * I _hate_ this partitioning mess - why can't we have one defined
++ * format, and everyone stick to it?
+ */
+-#define PARTITION_RISCIX_MFM 1
+-#define PARTITION_RISCIX_SCSI 2
+-#define PARTITION_LINUX 9
+-
+-struct riscix_part {
+- __u32 start;
+- __u32 length;
+- __u32 one;
+- char name[16];
+-};
+-
+-struct riscix_record {
+- __u32 magic;
+-#define RISCIX_MAGIC (0x4a657320)
+- __u32 date;
+- struct riscix_part part[8];
+-};
+-
+-#define LINUX_NATIVE_MAGIC 0xdeafa1de
+-#define LINUX_SWAP_MAGIC 0xdeafab1e
+-
+-struct linux_part {
+- __u32 magic;
+- __u32 start_sect;
+- __u32 nr_sects;
+-};
+-
+-struct ics_part {
+- __u32 start;
+- __s32 size;
+-};
+-
+-struct ptec_partition {
+- __u32 unused1;
+- __u32 unused2;
+- __u32 start;
+- __u32 size;
+- __u32 unused5;
+- char type[8];
+-};
+-
+-
+-int acorn_partition(struct gendisk *hd, struct block_device *bdev,
+- unsigned long first_sect, int first_minor);
+
++int
++adfspart_check_CUMANA(struct gendisk *hd, struct block_device *bdev,
++ unsigned long first_sector, int minor);
++
++int
++adfspart_check_ADFS(struct gendisk *hd, struct block_device *bdev,
++ unsigned long first_sector, int minor);
++
++int
++adfspart_check_ICS(struct gendisk *hd, struct block_device *bdev,
++ unsigned long first_sector, int minor);
++
++int
++adfspart_check_POWERTEC(struct gendisk *hd, struct block_device *bdev,
++ unsigned long first_sector, int minor);
++
++int
++adfspart_check_EESOX(struct gendisk *hd, struct block_device *bdev,
++ unsigned long first_sector, int minor);
+diff -urN kernel-source-2.4.27-8/fs/partitions/check.c kernel-source-2.4.27-8-arm-1/fs/partitions/check.c
+--- kernel-source-2.4.27-8/fs/partitions/check.c 2004-02-18 13:36:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/fs/partitions/check.c 2005-02-18 17:48:44.000000000 +0000
+@@ -40,8 +40,30 @@
+ int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
+
+ static int (*check_part[])(struct gendisk *hd, struct block_device *bdev, unsigned long first_sect, int first_minor) = {
+-#ifdef CONFIG_ACORN_PARTITION
+- acorn_partition,
++ /*
++ * Probe partition formats with tables at disk address 0
++ * that also have an ADFS boot block at 0xdc0.
++ */
++#ifdef CONFIG_ACORN_PARTITION_ICS
++ adfspart_check_ICS,
++#endif
++#ifdef CONFIG_ACORN_PARTITION_POWERTEC
++ adfspart_check_POWERTEC,
++#endif
++#ifdef CONFIG_ACORN_PARTITION_EESOX
++ adfspart_check_EESOX,
++#endif
++
++ /*
++ * Now move on to formats that only have partition info at
++ * disk address 0xdc0. These should come before MSDOS
++ * partition tables.
++ */
++#ifdef CONFIG_ACORN_PARTITION_CUMANA
++ adfspart_check_CUMANA,
++#endif
++#ifdef CONFIG_ACORN_PARTITION_ADFS
++ adfspart_check_ADFS,
+ #endif
+ #ifdef CONFIG_SGI_PARTITION
+ sgi_partition,
+@@ -79,6 +101,18 @@
+ NULL
+ };
+
++static char *raid_name (struct gendisk *hd, unsigned int unit, unsigned int part,
++ int major_base, char *buf)
++{
++ int ctlr = hd->major - major_base;
++ if (part == 0)
++ sprintf(buf, "%s/c%dd%d", hd->major_name, ctlr, unit);
++ else
++ sprintf(buf, "%s/c%dd%dp%d", hd->major_name, ctlr, unit,
++ part);
++ return buf;
++}
++
+ /*
+ * This is ucking fugly but its probably the best thing for 2.4.x
+ * Take it as a clear reminder that: 1) we should put the device name
+@@ -104,10 +138,11 @@
+ char *disk_name (struct gendisk *hd, int minor, char *buf)
+ {
+ const char *maj = hd->major_name;
+- unsigned int unit = (minor >> hd->minor_shift);
+- unsigned int part = (minor & ((1 << hd->minor_shift) -1 ));
++ unsigned int unit = minor >> hd->minor_shift;
++ unsigned int part = minor & (( 1 << hd->minor_shift) - 1);
++ char *p;
+
+- if ((unit < hd->nr_real) && hd->part[minor].de) {
++ if (unit < hd->nr_real && hd->part[minor].de) {
+ int pos;
+
+ pos = devfs_generate_path (hd->part[minor].de, buf, 64);
+@@ -153,37 +188,15 @@
+ }
+ if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) {
+ unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16;
+- if (unit+'a' > 'z') {
+- unit -= 26;
+- sprintf(buf, "sd%c%c", 'a' + unit / 26, 'a' + unit % 26);
+- if (part)
+- sprintf(buf + 4, "%d", part);
+- return buf;
+- }
+ }
+ if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) {
+- int ctlr = hd->major - COMPAQ_SMART2_MAJOR;
+- if (part == 0)
+- sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
+- else
+- sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
+- return buf;
++ return raid_name(hd, unit, part, COMPAQ_SMART2_MAJOR, buf);
+ }
+ if (hd->major >= COMPAQ_CISS_MAJOR && hd->major <= COMPAQ_CISS_MAJOR+7) {
+- int ctlr = hd->major - COMPAQ_CISS_MAJOR;
+- if (part == 0)
+- sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
+- else
+- sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
+- return buf;
++ return raid_name(hd, unit, part, COMPAQ_CISS_MAJOR, buf);
+ }
+ if (hd->major >= DAC960_MAJOR && hd->major <= DAC960_MAJOR+7) {
+- int ctlr = hd->major - DAC960_MAJOR;
+- if (part == 0)
+- sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
+- else
+- sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
+- return buf;
++ return raid_name(hd, unit, part, DAC960_MAJOR, buf);
+ }
+ if (hd->major == ATARAID_MAJOR) {
+ int disk = minor >> hd->minor_shift;
+@@ -194,10 +207,13 @@
+ sprintf(buf, "%s/d%dp%d", maj, disk, part);
+ return buf;
+ }
+- if (part)
+- sprintf(buf, "%s%c%d", maj, unit+'a', part);
++ p = buf;
++ if (unit <= 26)
++ p += sprintf(buf, "%s%c", maj, 'a' + unit);
+ else
+- sprintf(buf, "%s%c", maj, unit+'a');
++ p += sprintf(buf, "%s%c%c", maj, 'a' + unit / 26, 'a' + unit % 26);
++ if (part)
++ sprintf(p, "%d", part);
+ return buf;
+ }
+
+@@ -207,7 +223,7 @@
+ void add_gd_partition(struct gendisk *hd, int minor, int start, int size)
+ {
+ #ifndef CONFIG_DEVFS_FS
+- char buf[40];
++ char buf[MAX_DISKNAME_LEN];
+ #endif
+
+ hd->part[minor].start_sect = start;
+@@ -229,7 +245,7 @@
+ static int first_time = 1;
+ unsigned long first_sector;
+ struct block_device *bdev;
+- char buf[64];
++ char buf[MAX_DISKNAME_LEN];
+ int i;
+
+ if (first_time)
+diff -urN kernel-source-2.4.27-8/fs/proc/array.c kernel-source-2.4.27-8-arm-1/fs/proc/array.c
+--- kernel-source-2.4.27-8/fs/proc/array.c 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/fs/proc/array.c 2005-02-18 17:48:44.000000000 +0000
+@@ -362,15 +362,15 @@
+ task->cmin_flt,
+ task->maj_flt,
+ task->cmaj_flt,
+- task->times.tms_utime,
+- task->times.tms_stime,
+- task->times.tms_cutime,
+- task->times.tms_cstime,
++ hz_to_std(task->times.tms_utime),
++ hz_to_std(task->times.tms_stime),
++ hz_to_std(task->times.tms_cutime),
++ hz_to_std(task->times.tms_cstime),
+ priority,
+ nice,
+ 0UL /* removed */,
+ task->it_real_value,
+- task->start_time,
++ hz_to_std(task->start_time),
+ vsize,
+ mm ? mm->rss : 0, /* you might want to shift this left 3 */
+ task->rlim[RLIMIT_RSS].rlim_cur,
+@@ -417,6 +417,7 @@
+ end = PMD_SIZE;
+ do {
+ pte_t page = *pte;
++ unsigned long pfn;
+ struct page *ptpage;
+
+ address += PAGE_SIZE;
+@@ -426,8 +427,11 @@
+ ++*total;
+ if (!pte_present(page))
+ continue;
+- ptpage = pte_page(page);
+- if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
++ pfn = pte_pfn(page);
++ if (!pfn_valid(pfn))
++ continue;
++ ptpage = pfn_to_page(pfn);
++ if (PageReserved(ptpage))
+ continue;
+ ++*pages;
+ if (pte_dirty(page))
+@@ -609,14 +613,14 @@
+
+ len = sprintf(buffer,
+ "cpu %lu %lu\n",
+- task->times.tms_utime,
+- task->times.tms_stime);
++ hz_to_std(task->times.tms_utime),
++ hz_to_std(task->times.tms_stime));
+
+ for (i = 0 ; i < smp_num_cpus; i++)
+ len += sprintf(buffer + len, "cpu%d %lu %lu\n",
+ i,
+- task->per_cpu_utime[cpu_logical_map(i)],
+- task->per_cpu_stime[cpu_logical_map(i)]);
++ hz_to_std(task->per_cpu_utime[cpu_logical_map(i)]),
++ hz_to_std(task->per_cpu_stime[cpu_logical_map(i)]));
+
+ return len;
+ }
+diff -urN kernel-source-2.4.27-8/fs/proc/proc_misc.c kernel-source-2.4.27-8-arm-1/fs/proc/proc_misc.c
+--- kernel-source-2.4.27-8/fs/proc/proc_misc.c 2005-01-19 09:57:41.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/fs/proc/proc_misc.c 2005-02-18 17:48:44.000000000 +0000
+@@ -310,16 +310,16 @@
+ {
+ int i, len = 0;
+ extern unsigned long total_forks;
+- unsigned long jif = jiffies;
++ unsigned long jif = hz_to_std(jiffies);
+ unsigned int sum = 0, user = 0, nice = 0, system = 0;
+ int major, disk;
+
+ for (i = 0 ; i < smp_num_cpus; i++) {
+ int cpu = cpu_logical_map(i), j;
+
+- user += kstat.per_cpu_user[cpu];
+- nice += kstat.per_cpu_nice[cpu];
+- system += kstat.per_cpu_system[cpu];
++ user += hz_to_std(kstat.per_cpu_user[cpu]);
++ nice += hz_to_std(kstat.per_cpu_nice[cpu]);
++ system += hz_to_std(kstat.per_cpu_system[cpu]);
+ #if !defined(CONFIG_ARCH_S390)
+ for (j = 0 ; j < NR_IRQS ; j++)
+ sum += kstat.irqs[cpu][j];
+@@ -333,10 +333,10 @@
+ proc_sprintf(page, &off, &len,
+ "cpu%d %u %u %u %lu\n",
+ i,
+- kstat.per_cpu_user[cpu_logical_map(i)],
+- kstat.per_cpu_nice[cpu_logical_map(i)],
+- kstat.per_cpu_system[cpu_logical_map(i)],
+- jif - ( kstat.per_cpu_user[cpu_logical_map(i)] \
++ hz_to_std(kstat.per_cpu_user[cpu_logical_map(i)]),
++ hz_to_std(kstat.per_cpu_nice[cpu_logical_map(i)]),
++ hz_to_std(kstat.per_cpu_system[cpu_logical_map(i)]),
++ jif - hz_to_std( kstat.per_cpu_user[cpu_logical_map(i)] \
+ + kstat.per_cpu_nice[cpu_logical_map(i)] \
+ + kstat.per_cpu_system[cpu_logical_map(i)]));
+ proc_sprintf(page, &off, &len,
+@@ -442,12 +442,14 @@
+ return proc_calc_metrics(page, start, off, count, eof, len);
+ }
+
++#ifdef CONFIG_GENERIC_ISA_DMA
+ static int dma_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+ {
+ int len = get_dma_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+ }
++#endif
+
+ static int cmdline_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+@@ -613,7 +615,9 @@
+ {"interrupts", interrupts_read_proc},
+ #endif
+ {"filesystems", filesystems_read_proc},
++#ifdef CONFIG_GENERIC_ISA_DMA
+ {"dma", dma_read_proc},
++#endif
+ {"cmdline", cmdline_read_proc},
+ #ifdef CONFIG_SGI_DS1286
+ {"rtc", ds1286_read_proc},
+diff -urN kernel-source-2.4.27-8/fs/stat.c kernel-source-2.4.27-8-arm-1/fs/stat.c
+--- kernel-source-2.4.27-8/fs/stat.c 2004-08-08 00:26:06.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/fs/stat.c 2005-02-18 17:48:44.000000000 +0000
+@@ -26,7 +26,9 @@
+ }
+
+
+-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && \
++ !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__arm__) && \
++ !defined(__x86_64__) && !defined(__mips__)
+
+ /*
+ * For backward compatibility? Maybe this should be moved
+@@ -133,7 +135,9 @@
+ }
+
+
+-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && \
++ !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__arm__) && \
++ !defined(__x86_64__) && !defined(__mips__)
+ /*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+@@ -169,7 +173,9 @@
+ return error;
+ }
+
+-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && \
++ !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__arm__) && \
++ !defined(__x86_64__) && !defined(__mips__)
+
+ /*
+ * For backward compatibility? Maybe this should be moved
+@@ -207,7 +213,9 @@
+ return error;
+ }
+
+-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && \
++ !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__arm__) && \
++ !defined(__x86_64__) && !defined(__mips__)
+
+ /*
+ * For backward compatibility? Maybe this should be moved
+diff -urN kernel-source-2.4.27-8/include/asm-alpha/ide.h kernel-source-2.4.27-8-arm-1/include/asm-alpha/ide.h
+--- kernel-source-2.4.27-8/include/asm-alpha/ide.h 2003-06-13 15:51:38.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-alpha/ide.h 2005-02-18 17:48:44.000000000 +0000
+@@ -19,35 +19,15 @@
+ #define MAX_HWIFS 4
+ #endif
+
+-static __inline__ int ide_default_irq(ide_ioreg_t base)
+-{
+- switch (base) {
+- case 0x1f0: return 14;
+- case 0x170: return 15;
+- case 0x1e8: return 11;
+- case 0x168: return 10;
+- default:
+- return 0;
+- }
+-}
+-
+-static __inline__ ide_ioreg_t ide_default_io_base(int index)
+-{
+- switch (index) {
+- case 0: return 0x1f0;
+- case 1: return 0x170;
+- case 2: return 0x1e8;
+- case 3: return 0x168;
+- default:
+- return 0;
+- }
+-}
++#define ide_default_io_base(i) ((ide_ioreg_t)0)
++#define ide_default_irq(b) (0)
+
+ static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
+ {
+ ide_ioreg_t reg = data_port;
+ int i;
+
++ memset(hw, 0, sizeof(*hw));
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hw->io_ports[i] = reg;
+ reg += 1;
+@@ -55,7 +35,7 @@
+ if (ctrl_port) {
+ hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+ } else {
+- hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206;
++ hw->io_ports[IDE_CONTROL_OFFSET] = data_port + 0x206;
+ }
+ if (irq != NULL)
+ *irq = 0;
+@@ -70,13 +50,19 @@
+ {
+ #ifndef CONFIG_BLK_DEV_IDEPCI
+ hw_regs_t hw;
+- int index;
+
+- for (index = 0; index < MAX_HWIFS; index++) {
+- ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL);
+- hw.irq = ide_default_irq(ide_default_io_base(index));
++ ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL);
++ hw.irq = 14;
++ ide_register_hw(&hw, NULL);
++ ide_init_hwif_ports(&hw, 0x170, 0x376, NULL);
++ hw.irq = 15;
++ ide_register_hw(&hw, NULL);
++ ide_init_hwif_ports(&hw, 0x1e8, 0x3ee, NULL);
++ hw.irq = 11;
++ ide_register_hw(&hw, NULL);
++ ide_init_hwif_ports(&hw, 0x168, 0x36e, NULL);
++ hw.irq = 10;
+ ide_register_hw(&hw, NULL);
+- }
+ #endif /* CONFIG_BLK_DEV_IDEPCI */
+ }
+
+diff -urN kernel-source-2.4.27-8/include/asm-alpha/param.h kernel-source-2.4.27-8-arm-1/include/asm-alpha/param.h
+--- kernel-source-2.4.27-8/include/asm-alpha/param.h 2000-11-08 07:37:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-alpha/param.h 2005-02-18 17:48:44.000000000 +0000
+@@ -13,6 +13,9 @@
+ # else
+ # define HZ 1200
+ # endif
++#ifdef __KERNEL__
++# define hz_to_std(a) (a)
++#endif
+ #endif
+
+ #define EXEC_PAGESIZE 8192
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/AT91RM9200.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/AT91RM9200.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/AT91RM9200.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/AT91RM9200.h 2005-02-18 17:48:44.000000000 +0000
+@@ -171,7 +171,7 @@
+ #define AT91C_PA23_TXD2 (AT91C_PIO_PA23) // USART 2 Transmit Data
+ #define AT91C_PA23_IRQ3 (AT91C_PIO_PA23) // Interrupt input 3
+ #define AT91C_PIO_PA24 (1 << 24)
+-#define AT91C_PA24_SCK2 (AT91C_PIO_PA24) // USART2 Serial Clock
++#define AT91C_PA24_SCK2 (AT91C_PIO_PA24) // USART 2 Serial Clock
+ #define AT91C_PA24_PCK1 (AT91C_PIO_PA24) // PMC Programmable Clock Output 1
+ #define AT91C_PIO_PA25 (1 << 25)
+ #define AT91C_PA25_TWD (AT91C_PIO_PA25) // TWI Two-wire Serial Data
+@@ -260,7 +260,7 @@
+ #define AT91C_PIO_PB21 (1 << 21)
+ #define AT91C_PB21_RXD1 (AT91C_PIO_PB21) // USART 1 Receive Data
+ #define AT91C_PIO_PB22 (1 << 22)
+-#define AT91C_PB22_SCK1 (AT91C_PIO_PB22) // USART1 Serial Clock
++#define AT91C_PB22_SCK1 (AT91C_PIO_PB22) // USART 1 Serial Clock
+ #define AT91C_PIO_PB23 (1 << 23)
+ #define AT91C_PB23_DCD1 (AT91C_PIO_PB23) // USART 1 Data Carrier Detect
+ #define AT91C_PIO_PB24 (1 << 24)
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/AT91RM9200_MCI.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/AT91RM9200_MCI.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/AT91RM9200_MCI.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/AT91RM9200_MCI.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,127 @@
++// ----------------------------------------------------------------------------
++// ATMEL Microcontroller Software Support - ROUSSET -
++// ----------------------------------------------------------------------------
++// The software is delivered "AS IS" without warranty or condition of any
++// kind, either express, implied or statutory. This includes without
++// limitation any warranty or condition with respect to merchantability or
++// fitness for any particular purpose, or against the infringements of
++// intellectual property rights of others.
++// ----------------------------------------------------------------------------
++// File Name : AT91RM9200.h
++// Object : AT91RM9200 / MCI definitions
++// Generated : AT91 SW Application Group 12/03/2002 (10:48:02)
++//
++// ----------------------------------------------------------------------------
++
++#ifndef AT91RM9200_MCI_H
++#define AT91RM9200_MCI_H
++
++// *****************************************************************************
++// SOFTWARE API DEFINITION FOR Multimedia Card Interface
++// *****************************************************************************
++#ifndef __ASSEMBLY__
++
++typedef struct _AT91S_MCI {
++ AT91_REG MCI_CR; // MCI Control Register
++ AT91_REG MCI_MR; // MCI Mode Register
++ AT91_REG MCI_DTOR; // MCI Data Timeout Register
++ AT91_REG MCI_SDCR; // MCI SD Card Register
++ AT91_REG MCI_ARGR; // MCI Argument Register
++ AT91_REG MCI_CMDR; // MCI Command Register
++ AT91_REG Reserved0[2]; //
++ AT91_REG MCI_RSPR[4]; // MCI Response Register
++ AT91_REG MCI_RDR; // MCI Receive Data Register
++ AT91_REG MCI_TDR; // MCI Transmit Data Register
++ AT91_REG Reserved1[2]; //
++ AT91_REG MCI_SR; // MCI Status Register
++ AT91_REG MCI_IER; // MCI Interrupt Enable Register
++ AT91_REG MCI_IDR; // MCI Interrupt Disable Register
++ AT91_REG MCI_IMR; // MCI Interrupt Mask Register
++ AT91_REG Reserved2[44]; //
++ AT91_REG MCI_RPR; // Receive Pointer Register
++ AT91_REG MCI_RCR; // Receive Counter Register
++ AT91_REG MCI_TPR; // Transmit Pointer Register
++ AT91_REG MCI_TCR; // Transmit Counter Register
++ AT91_REG MCI_RNPR; // Receive Next Pointer Register
++ AT91_REG MCI_RNCR; // Receive Next Counter Register
++ AT91_REG MCI_TNPR; // Transmit Next Pointer Register
++ AT91_REG MCI_TNCR; // Transmit Next Counter Register
++ AT91_REG MCI_PTCR; // PDC Transfer Control Register
++ AT91_REG MCI_PTSR; // PDC Transfer Status Register
++} AT91S_MCI, *AT91PS_MCI;
++
++#endif
++
++// -------- MCI_CR : (MCI Offset: 0x0) MCI Control Register --------
++#define AT91C_MCI_MCIEN ((unsigned int) 0x1 << 0) // (MCI) Multimedia Interface Enable
++#define AT91C_MCI_MCIDIS ((unsigned int) 0x1 << 1) // (MCI) Multimedia Interface Disable
++#define AT91C_MCI_PWSEN ((unsigned int) 0x1 << 2) // (MCI) Power Save Mode Enable
++#define AT91C_MCI_PWSDIS ((unsigned int) 0x1 << 3) // (MCI) Power Save Mode Disable
++// -------- MCI_MR : (MCI Offset: 0x4) MCI Mode Register --------
++#define AT91C_MCI_CLKDIV ((unsigned int) 0x1 << 0) // (MCI) Clock Divider
++#define AT91C_MCI_PWSDIV ((unsigned int) 0x1 << 8) // (MCI) Power Saving Divider
++#define AT91C_MCI_PDCPADV ((unsigned int) 0x1 << 14) // (MCI) PDC Padding Value
++#define AT91C_MCI_PDCMODE ((unsigned int) 0x1 << 15) // (MCI) PDC Oriented Mode
++#define AT91C_MCI_BLKLEN ((unsigned int) 0x1 << 18) // (MCI) Data Block Length
++// -------- MCI_DTOR : (MCI Offset: 0x8) MCI Data Timeout Register --------
++#define AT91C_MCI_DTOCYC ((unsigned int) 0x1 << 0) // (MCI) Data Timeout Cycle Number
++#define AT91C_MCI_DTOMUL ((unsigned int) 0x7 << 4) // (MCI) Data Timeout Multiplier
++#define AT91C_MCI_DTOMUL_1 ((unsigned int) 0x0 << 4) // (MCI) DTOCYC x 1
++#define AT91C_MCI_DTOMUL_16 ((unsigned int) 0x1 << 4) // (MCI) DTOCYC x 16
++#define AT91C_MCI_DTOMUL_128 ((unsigned int) 0x2 << 4) // (MCI) DTOCYC x 128
++#define AT91C_MCI_DTOMUL_256 ((unsigned int) 0x3 << 4) // (MCI) DTOCYC x 256
++#define AT91C_MCI_DTOMUL_1024 ((unsigned int) 0x4 << 4) // (MCI) DTOCYC x 1024
++#define AT91C_MCI_DTOMUL_4096 ((unsigned int) 0x5 << 4) // (MCI) DTOCYC x 4096
++#define AT91C_MCI_DTOMUL_65536 ((unsigned int) 0x6 << 4) // (MCI) DTOCYC x 65536
++#define AT91C_MCI_DTOMUL_1048576 ((unsigned int) 0x7 << 4) // (MCI) DTOCYC x 1048576
++// -------- MCI_SDCR : (MCI Offset: 0xc) MCI SD Card Register --------
++#define AT91C_MCI_SCDSEL ((unsigned int) 0x1 << 0) // (MCI) SD Card Selector
++#define AT91C_MCI_SCDBUS ((unsigned int) 0x1 << 7) // (MCI) SD Card Bus Width
++// -------- MCI_CMDR : (MCI Offset: 0x14) MCI Command Register --------
++#define AT91C_MCI_CMDNB ((unsigned int) 0x1F << 0) // (MCI) Command Number
++#define AT91C_MCI_RSPTYP ((unsigned int) 0x3 << 6) // (MCI) Response Type
++#define AT91C_MCI_RSPTYP_NO ((unsigned int) 0x0 << 6) // (MCI) No response
++#define AT91C_MCI_RSPTYP_48 ((unsigned int) 0x1 << 6) // (MCI) 48-bit response
++#define AT91C_MCI_RSPTYP_136 ((unsigned int) 0x2 << 6) // (MCI) 136-bit response
++#define AT91C_MCI_SPCMD ((unsigned int) 0x7 << 8) // (MCI) Special CMD
++#define AT91C_MCI_SPCMD_NONE ((unsigned int) 0x0 << 8) // (MCI) Not a special CMD
++#define AT91C_MCI_SPCMD_INIT ((unsigned int) 0x1 << 8) // (MCI) Initialization CMD
++#define AT91C_MCI_SPCMD_SYNC ((unsigned int) 0x2 << 8) // (MCI) Synchronized CMD
++#define AT91C_MCI_SPCMD_IT_CMD ((unsigned int) 0x4 << 8) // (MCI) Interrupt command
++#define AT91C_MCI_SPCMD_IT_REP ((unsigned int) 0x5 << 8) // (MCI) Interrupt response
++#define AT91C_MCI_OPDCMD ((unsigned int) 0x1 << 11) // (MCI) Open Drain Command
++#define AT91C_MCI_MAXLAT ((unsigned int) 0x1 << 12) // (MCI) Maximum Latency for Command to respond
++#define AT91C_MCI_TRCMD ((unsigned int) 0x3 << 16) // (MCI) Transfer CMD
++#define AT91C_MCI_TRCMD_NO ((unsigned int) 0x0 << 16) // (MCI) No transfer
++#define AT91C_MCI_TRCMD_START ((unsigned int) 0x1 << 16) // (MCI) Start transfer
++#define AT91C_MCI_TRCMD_STOP ((unsigned int) 0x2 << 16) // (MCI) Stop transfer
++#define AT91C_MCI_TRDIR ((unsigned int) 0x1 << 18) // (MCI) Transfer Direction
++#define AT91C_MCI_TRTYP ((unsigned int) 0x3 << 19) // (MCI) Transfer Type
++#define AT91C_MCI_TRTYP_BLOCK ((unsigned int) 0x0 << 19) // (MCI) Block Transfer type
++#define AT91C_MCI_TRTYP_MULTIPLE ((unsigned int) 0x1 << 19) // (MCI) Multiple Block transfer type
++#define AT91C_MCI_TRTYP_STREAM ((unsigned int) 0x2 << 19) // (MCI) Stream transfer type
++// -------- MCI_SR : (MCI Offset: 0x40) MCI Status Register --------
++#define AT91C_MCI_CMDRDY ((unsigned int) 0x1 << 0) // (MCI) Command Ready flag
++#define AT91C_MCI_RXRDY ((unsigned int) 0x1 << 1) // (MCI) RX Ready flag
++#define AT91C_MCI_TXRDY ((unsigned int) 0x1 << 2) // (MCI) TX Ready flag
++#define AT91C_MCI_BLKE ((unsigned int) 0x1 << 3) // (MCI) Data Block Transfer Ended flag
++#define AT91C_MCI_DTIP ((unsigned int) 0x1 << 4) // (MCI) Data Transfer in Progress flag
++#define AT91C_MCI_NOTBUSY ((unsigned int) 0x1 << 5) // (MCI) Data Line Not Busy flag
++#define AT91C_MCI_ENDRX ((unsigned int) 0x1 << 6) // (MCI) End of RX Buffer flag
++#define AT91C_MCI_ENDTX ((unsigned int) 0x1 << 7) // (MCI) End of TX Buffer flag
++#define AT91C_MCI_RXBUFF ((unsigned int) 0x1 << 14) // (MCI) RX Buffer Full flag
++#define AT91C_MCI_TXBUFE ((unsigned int) 0x1 << 15) // (MCI) TX Buffer Empty flag
++#define AT91C_MCI_RINDE ((unsigned int) 0x1 << 16) // (MCI) Response Index Error flag
++#define AT91C_MCI_RDIRE ((unsigned int) 0x1 << 17) // (MCI) Response Direction Error flag
++#define AT91C_MCI_RCRCE ((unsigned int) 0x1 << 18) // (MCI) Response CRC Error flag
++#define AT91C_MCI_RENDE ((unsigned int) 0x1 << 19) // (MCI) Response End Bit Error flag
++#define AT91C_MCI_RTOE ((unsigned int) 0x1 << 20) // (MCI) Response Time-out Error flag
++#define AT91C_MCI_DCRCE ((unsigned int) 0x1 << 21) // (MCI) data CRC Error flag
++#define AT91C_MCI_DTOE ((unsigned int) 0x1 << 22) // (MCI) Data timeout Error flag
++#define AT91C_MCI_OVRE ((unsigned int) 0x1 << 30) // (MCI) Overrun flag
++#define AT91C_MCI_UNRE ((unsigned int) 0x1 << 31) // (MCI) Underrun flag
++// -------- MCI_IER : (MCI Offset: 0x44) MCI Interrupt Enable Register --------
++// -------- MCI_IDR : (MCI Offset: 0x48) MCI Interrupt Disable Register --------
++// -------- MCI_IMR : (MCI Offset: 0x4c) MCI Interrupt Mask Register --------
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/AT91RM9200_SSC.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/AT91RM9200_SSC.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/AT91RM9200_SSC.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/AT91RM9200_SSC.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,129 @@
++// ----------------------------------------------------------------------------
++// ATMEL Microcontroller Software Support - ROUSSET -
++// ----------------------------------------------------------------------------
++// The software is delivered "AS IS" without warranty or condition of any
++// kind, either express, implied or statutory. This includes without
++// limitation any warranty or condition with respect to merchantability or
++// fitness for any particular purpose, or against the infringements of
++// intellectual property rights of others.
++// ----------------------------------------------------------------------------
++// File Name : AT91RM9200.h
++// Object : AT91RM9200 / SSC definitions
++// Generated : AT91 SW Application Group 12/03/2002 (10:48:02)
++//
++// ----------------------------------------------------------------------------
++
++#ifndef AT91RM9200_SSC_H
++#define AT91RM9200_SSC_H
++
++// *****************************************************************************
++// SOFTWARE API DEFINITION FOR Synchronous Serial Controller Interface
++// *****************************************************************************
++#ifndef __ASSEMBLY__
++
++typedef struct _AT91S_SSC {
++ AT91_REG SSC_CR; // Control Register
++ AT91_REG SSC_CMR; // Clock Mode Register
++ AT91_REG Reserved0[2]; //
++ AT91_REG SSC_RCMR; // Receive Clock ModeRegister
++ AT91_REG SSC_RFMR; // Receive Frame Mode Register
++ AT91_REG SSC_TCMR; // Transmit Clock Mode Register
++ AT91_REG SSC_TFMR; // Transmit Frame Mode Register
++ AT91_REG SSC_RHR; // Receive Holding Register
++ AT91_REG SSC_THR; // Transmit Holding Register
++ AT91_REG Reserved1[2]; //
++ AT91_REG SSC_RSHR; // Receive Sync Holding Register
++ AT91_REG SSC_TSHR; // Transmit Sync Holding Register
++ AT91_REG SSC_RC0R; // Receive Compare 0 Register
++ AT91_REG SSC_RC1R; // Receive Compare 1 Register
++ AT91_REG SSC_SR; // Status Register
++ AT91_REG SSC_IER; // Interrupt Enable Register
++ AT91_REG SSC_IDR; // Interrupt Disable Register
++ AT91_REG SSC_IMR; // Interrupt Mask Register
++ AT91_REG Reserved2[44]; //
++ AT91_REG SSC_RPR; // Receive Pointer Register
++ AT91_REG SSC_RCR; // Receive Counter Register
++ AT91_REG SSC_TPR; // Transmit Pointer Register
++ AT91_REG SSC_TCR; // Transmit Counter Register
++ AT91_REG SSC_RNPR; // Receive Next Pointer Register
++ AT91_REG SSC_RNCR; // Receive Next Counter Register
++ AT91_REG SSC_TNPR; // Transmit Next Pointer Register
++ AT91_REG SSC_TNCR; // Transmit Next Counter Register
++ AT91_REG SSC_PTCR; // PDC Transfer Control Register
++ AT91_REG SSC_PTSR; // PDC Transfer Status Register
++} AT91S_SSC, *AT91PS_SSC;
++
++#endif
++
++// -------- SSC_CR : (SSC Offset: 0x0) SSC Control Register --------
++#define AT91C_SSC_RXEN ( 0x1 << 0) // (SSC) Receive Enable
++#define AT91C_SSC_RXDIS ( 0x1 << 1) // (SSC) Receive Disable
++#define AT91C_SSC_TXEN ( 0x1 << 8) // (SSC) Transmit Enable
++#define AT91C_SSC_TXDIS ( 0x1 << 9) // (SSC) Transmit Disable
++#define AT91C_SSC_SWRST ( 0x1 << 15) // (SSC) Software Reset
++// -------- SSC_RCMR : (SSC Offset: 0x10) SSC Receive Clock Mode Register --------
++#define AT91C_SSC_CKS ( 0x3 << 0) // (SSC) Receive/Transmit Clock Selection
++#define AT91C_SSC_CKS_DIV ( 0x0) // (SSC) Divided Clock
++#define AT91C_SSC_CKS_TK ( 0x1) // (SSC) TK Clock signal
++#define AT91C_SSC_CKS_RK ( 0x2) // (SSC) RK pin
++#define AT91C_SSC_CKO ( 0x7 << 2) // (SSC) Receive/Transmit Clock Output Mode Selection
++#define AT91C_SSC_CKO_NONE ( 0x0 << 2) // (SSC) Receive/Transmit Clock Output Mode: None RK pin: Input-only
++#define AT91C_SSC_CKO_CONTINOUS ( 0x1 << 2) // (SSC) Continuous Receive/Transmit Clock RK pin: Output
++#define AT91C_SSC_CKO_DATA_TX ( 0x2 << 2) // (SSC) Receive/Transmit Clock only during data transfers RK pin: Output
++#define AT91C_SSC_CKI ( 0x1 << 5) // (SSC) Receive/Transmit Clock Inversion
++#define AT91C_SSC_CKG ( 0x3 << 6) // (SSC) Receive/Transmit Clock Gating Selection
++#define AT91C_SSC_CKG_NONE ( 0x0 << 6) // (SSC) Receive/Transmit Clock Gating: None, continuous clock
++#define AT91C_SSC_CKG_LOW ( 0x1 << 6) // (SSC) Receive/Transmit Clock enabled only if RF Low
++#define AT91C_SSC_CKG_HIGH ( 0x2 << 6) // (SSC) Receive/Transmit Clock enabled only if RF High
++#define AT91C_SSC_START ( 0xF << 8) // (SSC) Receive/Transmit Start Selection
++#define AT91C_SSC_START_CONTINOUS ( 0x0 << 8) // (SSC) Continuous, as soon as the receiver is enabled, and immediately after the end of transfer of the previous data.
++#define AT91C_SSC_START_TX ( 0x1 << 8) // (SSC) Transmit/Receive start
++#define AT91C_SSC_START_LOW_RF ( 0x2 << 8) // (SSC) Detection of a low level on RF input
++#define AT91C_SSC_START_HIGH_RF ( 0x3 << 8) // (SSC) Detection of a high level on RF input
++#define AT91C_SSC_START_FALL_RF ( 0x4 << 8) // (SSC) Detection of a falling edge on RF input
++#define AT91C_SSC_START_RISE_RF ( 0x5 << 8) // (SSC) Detection of a rising edge on RF input
++#define AT91C_SSC_START_LEVEL_RF ( 0x6 << 8) // (SSC) Detection of any level change on RF input
++#define AT91C_SSC_START_EDGE_RF ( 0x7 << 8) // (SSC) Detection of any edge on RF input
++#define AT91C_SSC_START_0 ( 0x8 << 8) // (SSC) Compare 0
++#define AT91C_SSC_STOP ( 0x1 << 12) // (SSC) Receive Stop Selection
++#define AT91C_SSC_STTOUT ( 0x1 << 15) // (SSC) Receive/Transmit Start Output Selection
++#define AT91C_SSC_STTDLY ( 0xFF << 16) // (SSC) Receive/Transmit Start Delay
++#define AT91C_SSC_PERIOD ( 0xFF << 24) // (SSC) Receive/Transmit Period Divider Selection
++// -------- SSC_RFMR : (SSC Offset: 0x14) SSC Receive Frame Mode Register --------
++#define AT91C_SSC_DATLEN ( 0x1F << 0) // (SSC) Data Length
++#define AT91C_SSC_LOOP ( 0x1 << 5) // (SSC) Loop Mode
++#define AT91C_SSC_MSBF ( 0x1 << 7) // (SSC) Most Significant Bit First
++#define AT91C_SSC_DATNB ( 0xF << 8) // (SSC) Data Number per Frame
++#define AT91C_SSC_FSLEN ( 0xF << 16) // (SSC) Receive/Transmit Frame Sync length
++#define AT91C_SSC_FSOS ( 0x7 << 20) // (SSC) Receive/Transmit Frame Sync Output Selection
++#define AT91C_SSC_FSOS_NONE ( 0x0 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: None RK pin Input-only
++#define AT91C_SSC_FSOS_NEGATIVE ( 0x1 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Negative Pulse
++#define AT91C_SSC_FSOS_POSITIVE ( 0x2 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Positive Pulse
++#define AT91C_SSC_FSOS_LOW ( 0x3 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Driver Low during data transfer
++#define AT91C_SSC_FSOS_HIGH ( 0x4 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Driver High during data transfer
++#define AT91C_SSC_FSOS_TOGGLE ( 0x5 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Toggling at each start of data transfer
++#define AT91C_SSC_FSEDGE ( 0x1 << 24) // (SSC) Frame Sync Edge Detection
++// -------- SSC_TCMR : (SSC Offset: 0x18) SSC Transmit Clock Mode Register --------
++// -------- SSC_TFMR : (SSC Offset: 0x1c) SSC Transmit Frame Mode Register --------
++#define AT91C_SSC_DATDEF ( 0x1 << 5) // (SSC) Data Default Value
++#define AT91C_SSC_FSDEN ( 0x1 << 23) // (SSC) Frame Sync Data Enable
++// -------- SSC_SR : (SSC Offset: 0x40) SSC Status Register --------
++#define AT91C_SSC_TXRDY ( 0x1 << 0) // (SSC) Transmit Ready
++#define AT91C_SSC_TXEMPTY ( 0x1 << 1) // (SSC) Transmit Empty
++#define AT91C_SSC_ENDTX ( 0x1 << 2) // (SSC) End Of Transmission
++#define AT91C_SSC_TXBUFE ( 0x1 << 3) // (SSC) Transmit Buffer Empty
++#define AT91C_SSC_RXRDY ( 0x1 << 4) // (SSC) Receive Ready
++#define AT91C_SSC_OVRUN ( 0x1 << 5) // (SSC) Receive Overrun
++#define AT91C_SSC_ENDRX ( 0x1 << 6) // (SSC) End of Reception
++#define AT91C_SSC_RXBUFF ( 0x1 << 7) // (SSC) Receive Buffer Full
++#define AT91C_SSC_CP0 ( 0x1 << 8) // (SSC) Compare 0
++#define AT91C_SSC_CP1 ( 0x1 << 9) // (SSC) Compare 1
++#define AT91C_SSC_TXSYN ( 0x1 << 10) // (SSC) Transmit Sync
++#define AT91C_SSC_RXSYN ( 0x1 << 11) // (SSC) Receive Sync
++#define AT91C_SSC_TXENA ( 0x1 << 16) // (SSC) Transmit Enable
++#define AT91C_SSC_RXENA ( 0x1 << 17) // (SSC) Receive Enable
++// -------- SSC_IER : (SSC Offset: 0x44) SSC Interrupt Enable Register --------
++// -------- SSC_IDR : (SSC Offset: 0x48) SSC Interrupt Disable Register --------
++// -------- SSC_IMR : (SSC Offset: 0x4c) SSC Interrupt Mask Register --------
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/AT91RM9200_TC.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/AT91RM9200_TC.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/AT91RM9200_TC.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/AT91RM9200_TC.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,165 @@
++// ----------------------------------------------------------------------------
++// ATMEL Microcontroller Software Support - ROUSSET -
++// ----------------------------------------------------------------------------
++// The software is delivered "AS IS" without warranty or condition of any
++// kind, either express, implied or statutory. This includes without
++// limitation any warranty or condition with respect to merchantability or
++// fitness for any particular purpose, or against the infringements of
++// intellectual property rights of others.
++// ----------------------------------------------------------------------------
++// File Name : AT91RM9200.h
++// Object : AT91RM9200 definitions
++// Generated : AT91 SW Application Group 12/03/2002 (10:48:02)
++//
++// ----------------------------------------------------------------------------
++
++#ifndef AT91RM9200_TC_H
++#define AT91RM9200_TC_H
++
++// *****************************************************************************
++// SOFTWARE API DEFINITION FOR Timer Counter Channel Interface
++// *****************************************************************************
++#ifndef __ASSEMBLY__
++
++typedef struct _AT91S_TC {
++ AT91_REG TC_CCR; // Channel Control Register
++ AT91_REG TC_CMR; // Channel Mode Register
++ AT91_REG Reserved0[2]; //
++ AT91_REG TC_CV; // Counter Value
++ AT91_REG TC_RA; // Register A
++ AT91_REG TC_RB; // Register B
++ AT91_REG TC_RC; // Register C
++ AT91_REG TC_SR; // Status Register
++ AT91_REG TC_IER; // Interrupt Enable Register
++ AT91_REG TC_IDR; // Interrupt Disable Register
++ AT91_REG TC_IMR; // Interrupt Mask Register
++} AT91S_TC, *AT91PS_TC;
++
++typedef struct _AT91S_TCB {
++ AT91S_TC TCB_TC0; // TC Channel 0
++ AT91_REG Reserved0[4]; //
++ AT91S_TC TCB_TC1; // TC Channel 1
++ AT91_REG Reserved1[4]; //
++ AT91S_TC TCB_TC2; // TC Channel 2
++ AT91_REG Reserved2[4]; //
++ AT91_REG TCB_BCR; // TC Block Control Register
++ AT91_REG TCB_BMR; // TC Block Mode Register
++} AT91S_TCB, *AT91PS_TCB;
++
++#endif
++
++// -------- TC_CCR : (TC Offset: 0x0) TC Channel Control Register --------
++#define AT91C_TC_CLKEN ( 0x1 << 0) // (TC) Counter Clock Enable Command
++#define AT91C_TC_CLKDIS ( 0x1 << 1) // (TC) Counter Clock Disable Command
++#define AT91C_TC_SWTRG ( 0x1 << 2) // (TC) Software Trigger Command
++// -------- TC_CMR : (TC Offset: 0x4) TC Channel Mode Register: Capture Mode / Waveform Mode --------
++#define AT91C_TC_TCCLKS ( 0x7 << 0) // (TC) Clock Selection
++#define AT91C_TC_TIMER_DIV1_CLOCK ( 0x0 << 0) // (TC) MCK/2
++#define AT91C_TC_TIMER_DIV2_CLOCK ( 0x1 << 0) // (TC) MCK/8
++#define AT91C_TC_TIMER_DIV3_CLOCK ( 0x2 << 0) // (TC) MCK/32
++#define AT91C_TC_TIMER_DIV4_CLOCK ( 0x3 << 0) // (TC) MCK/128
++#define AT91C_TC_TIMER_DIV5_CLOCK ( 0x4 << 0) // (TC) MCK/256 = SLOW CLOCK
++#define AT91C_TC_TIMER_XC0 ( 0x5 << 0) // (TC) XC0
++#define AT91C_TC_TIMER_XC1 ( 0x6 << 0) // (TC) XC1
++#define AT91C_TC_TIMER_XC2 ( 0x7 << 0) // (TC) XC2
++#define AT91C_TC_CLKI ( 0x1 << 3) // (TC) Clock Invert
++#define AT91C_TC_BURST ( 0x3 << 4) // (TC) Burst Signal Selection
++#define AT91C_TC_CPCSTOP ( 0x1 << 6) // (TC) Counter Clock Stopped with RC Compare
++#define AT91C_TC_CPCDIS ( 0x1 << 7) // (TC) Counter Clock Disable with RC Compare
++#define AT91C_TC_EEVTEDG ( 0x3 << 8) // (TC) External Event Edge Selection
++#define AT91C_TC_EEVTEDG_NONE ( 0x0 << 8) // (TC) Edge: None
++#define AT91C_TC_EEVTEDG_RISING ( 0x1 << 8) // (TC) Edge: rising edge
++#define AT91C_TC_EEVTEDG_FALLING ( 0x2 << 8) // (TC) Edge: falling edge
++#define AT91C_TC_EEVTEDG_BOTH ( 0x3 << 8) // (TC) Edge: each edge
++#define AT91C_TC_EEVT ( 0x3 << 10) // (TC) External Event Selection
++#define AT91C_TC_EEVT_NONE ( 0x0 << 10) // (TC) Signal selected as external event: TIOB TIOB direction: input
++#define AT91C_TC_EEVT_RISING ( 0x1 << 10) // (TC) Signal selected as external event: XC0 TIOB direction: output
++#define AT91C_TC_EEVT_FALLING ( 0x2 << 10) // (TC) Signal selected as external event: XC1 TIOB direction: output
++#define AT91C_TC_EEVT_BOTH ( 0x3 << 10) // (TC) Signal selected as external event: XC2 TIOB direction: output
++#define AT91C_TC_ENETRG ( 0x1 << 12) // (TC) External Event Trigger enable
++#define AT91C_TC_WAVESEL ( 0x3 << 13) // (TC) Waveform Selection
++#define AT91C_TC_WAVESEL_UP ( 0x0 << 13) // (TC) UP mode without atomatic trigger on RC Compare
++#define AT91C_TC_WAVESEL_UP_AUTO ( 0x1 << 13) // (TC) UP mode with automatic trigger on RC Compare
++#define AT91C_TC_WAVESEL_UPDOWN ( 0x2 << 13) // (TC) UPDOWN mode without automatic trigger on RC Compare
++#define AT91C_TC_WAVESEL_UPDOWN_AUTO ( 0x3 << 13) // (TC) UPDOWN mode with automatic trigger on RC Compare
++#define AT91C_TC_CPCTRG ( 0x1 << 14) // (TC) RC Compare Trigger Enable
++#define AT91C_TC_WAVE ( 0x1 << 15) // (TC)
++#define AT91C_TC_ACPA ( 0x3 << 16) // (TC) RA Compare Effect on TIOA
++#define AT91C_TC_ACPA_NONE ( 0x0 << 16) // (TC) Effect: none
++#define AT91C_TC_ACPA_SET ( 0x1 << 16) // (TC) Effect: set
++#define AT91C_TC_ACPA_CLEAR ( 0x2 << 16) // (TC) Effect: clear
++#define AT91C_TC_ACPA_TOGGLE ( 0x3 << 16) // (TC) Effect: toggle
++#define AT91C_TC_ACPC ( 0x3 << 18) // (TC) RC Compare Effect on TIOA
++#define AT91C_TC_ACPC_NONE ( 0x0 << 18) // (TC) Effect: none
++#define AT91C_TC_ACPC_SET ( 0x1 << 18) // (TC) Effect: set
++#define AT91C_TC_ACPC_CLEAR ( 0x2 << 18) // (TC) Effect: clear
++#define AT91C_TC_ACPC_TOGGLE ( 0x3 << 18) // (TC) Effect: toggle
++#define AT91C_TC_AEEVT ( 0x3 << 20) // (TC) External Event Effect on TIOA
++#define AT91C_TC_AEEVT_NONE ( 0x0 << 20) // (TC) Effect: none
++#define AT91C_TC_AEEVT_SET ( 0x1 << 20) // (TC) Effect: set
++#define AT91C_TC_AEEVT_CLEAR ( 0x2 << 20) // (TC) Effect: clear
++#define AT91C_TC_AEEVT_TOGGLE ( 0x3 << 20) // (TC) Effect: toggle
++#define AT91C_TC_ASWTRG ( 0x3 << 22) // (TC) Software Trigger Effect on TIOA
++#define AT91C_TC_ASWTRG_NONE ( 0x0 << 22) // (TC) Effect: none
++#define AT91C_TC_ASWTRG_SET ( 0x1 << 22) // (TC) Effect: set
++#define AT91C_TC_ASWTRG_CLEAR ( 0x2 << 22) // (TC) Effect: clear
++#define AT91C_TC_ASWTRG_TOGGLE ( 0x3 << 22) // (TC) Effect: toggle
++#define AT91C_TC_BCPB ( 0x3 << 24) // (TC) RB Compare Effect on TIOB
++#define AT91C_TC_BCPB_NONE ( 0x0 << 24) // (TC) Effect: none
++#define AT91C_TC_BCPB_SET ( 0x1 << 24) // (TC) Effect: set
++#define AT91C_TC_BCPB_CLEAR ( 0x2 << 24) // (TC) Effect: clear
++#define AT91C_TC_BCPB_TOGGLE ( 0x3 << 24) // (TC) Effect: toggle
++#define AT91C_TC_BCPC ( 0x3 << 26) // (TC) RC Compare Effect on TIOB
++#define AT91C_TC_BCPC_NONE ( 0x0 << 26) // (TC) Effect: none
++#define AT91C_TC_BCPC_SET ( 0x1 << 26) // (TC) Effect: set
++#define AT91C_TC_BCPC_CLEAR ( 0x2 << 26) // (TC) Effect: clear
++#define AT91C_TC_BCPC_TOGGLE ( 0x3 << 26) // (TC) Effect: toggle
++#define AT91C_TC_BEEVT ( 0x3 << 28) // (TC) External Event Effect on TIOB
++#define AT91C_TC_BEEVT_NONE ( 0x0 << 28) // (TC) Effect: none
++#define AT91C_TC_BEEVT_SET ( 0x1 << 28) // (TC) Effect: set
++#define AT91C_TC_BEEVT_CLEAR ( 0x2 << 28) // (TC) Effect: clear
++#define AT91C_TC_BEEVT_TOGGLE ( 0x3 << 28) // (TC) Effect: toggle
++#define AT91C_TC_BSWTRG ( 0x3 << 30) // (TC) Software Trigger Effect on TIOB
++#define AT91C_TC_BSWTRG_NONE ( 0x0 << 30) // (TC) Effect: none
++#define AT91C_TC_BSWTRG_SET ( 0x1 << 30) // (TC) Effect: set
++#define AT91C_TC_BSWTRG_CLEAR ( 0x2 << 30) // (TC) Effect: clear
++#define AT91C_TC_BSWTRG_TOGGLE ( 0x3 << 30) // (TC) Effect: toggle
++// -------- TC_SR : (TC Offset: 0x20) TC Channel Status Register --------
++#define AT91C_TC_COVFS ( 0x1 << 0) // (TC) Counter Overflow
++#define AT91C_TC_LOVRS ( 0x1 << 1) // (TC) Load Overrun
++#define AT91C_TC_CPAS ( 0x1 << 2) // (TC) RA Compare
++#define AT91C_TC_CPBS ( 0x1 << 3) // (TC) RB Compare
++#define AT91C_TC_CPCS ( 0x1 << 4) // (TC) RC Compare
++#define AT91C_TC_LDRAS ( 0x1 << 5) // (TC) RA Loading
++#define AT91C_TC_LDRBS ( 0x1 << 6) // (TC) RB Loading
++#define AT91C_TC_ETRCS ( 0x1 << 7) // (TC) External Trigger
++#define AT91C_TC_ETRGS ( 0x1 << 16) // (TC) Clock Enabling
++#define AT91C_TC_MTIOA ( 0x1 << 17) // (TC) TIOA Mirror
++#define AT91C_TC_MTIOB ( 0x1 << 18) // (TC) TIOA Mirror
++// -------- TC_IER : (TC Offset: 0x24) TC Channel Interrupt Enable Register --------
++// -------- TC_IDR : (TC Offset: 0x28) TC Channel Interrupt Disable Register --------
++// -------- TC_IMR : (TC Offset: 0x2c) TC Channel Interrupt Mask Register --------
++
++// *****************************************************************************
++// SOFTWARE API DEFINITION FOR Timer Counter Interface
++// *****************************************************************************
++// -------- TCB_BCR : (TCB Offset: 0xc0) TC Block Control Register --------
++#define AT91C_TCB_SYNC ( 0x1 << 0) // (TCB) Synchro Command
++// -------- TCB_BMR : (TCB Offset: 0xc4) TC Block Mode Register --------
++#define AT91C_TCB_TC0XC0S ( 0x1 << 0) // (TCB) External Clock Signal 0 Selection
++#define AT91C_TCB_TC0XC0S_TCLK0 ( 0x0) // (TCB) TCLK0 connected to XC0
++#define AT91C_TCB_TC0XC0S_NONE ( 0x1) // (TCB) None signal connected to XC0
++#define AT91C_TCB_TC0XC0S_TIOA1 ( 0x2) // (TCB) TIOA1 connected to XC0
++#define AT91C_TCB_TC0XC0S_TIOA2 ( 0x3) // (TCB) TIOA2 connected to XC0
++#define AT91C_TCB_TC1XC1S ( 0x1 << 2) // (TCB) External Clock Signal 1 Selection
++#define AT91C_TCB_TC1XC1S_TCLK1 ( 0x0 << 2) // (TCB) TCLK1 connected to XC1
++#define AT91C_TCB_TC1XC1S_NONE ( 0x1 << 2) // (TCB) None signal connected to XC1
++#define AT91C_TCB_TC1XC1S_TIOA0 ( 0x2 << 2) // (TCB) TIOA0 connected to XC1
++#define AT91C_TCB_TC1XC1S_TIOA2 ( 0x3 << 2) // (TCB) TIOA2 connected to XC1
++#define AT91C_TCB_TC2XC2S ( 0x1 << 4) // (TCB) External Clock Signal 2 Selection
++#define AT91C_TCB_TC2XC2S_TCLK2 ( 0x0 << 4) // (TCB) TCLK2 connected to XC2
++#define AT91C_TCB_TC2XC2S_NONE ( 0x1 << 4) // (TCB) None signal connected to XC2
++#define AT91C_TCB_TC2XC2S_TIOA0 ( 0x2 << 4) // (TCB) TIOA0 connected to XC2
++#define AT91C_TCB_TC2XC2S_TIOA2 ( 0x3 << 4) // (TCB) TIOA2 connected to XC2
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/at91rm9200dk.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/at91rm9200dk.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/at91rm9200dk.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/at91rm9200dk.h 2005-02-18 17:48:44.000000000 +0000
+@@ -17,9 +17,9 @@
+
+ /* AT91RM92000 clocks */
+ #define AT91C_MAIN_CLOCK 179712000 /* from 18.432 MHz crystal (18432000 / 4 * 39) */
+-#define AT91C_MASTER_CLOCK 59904000 /* peripheral clock (AT91C_MASTER_CLOCK / 3) */
++#define AT91C_MASTER_CLOCK 59904000 /* peripheral clock (AT91C_MAIN_CLOCK / 3) */
+ #define AT91C_SLOW_CLOCK 32768 /* slow clock */
+-
++#define AT91_PLLB_INIT 0x1048be0e /* (18.432 / 14 * 73) /2 = 47.9714 */
+
+ /* FLASH */
+ #define AT91_FLASH_BASE 0x10000000 // NCS0: Flash physical base address
+@@ -68,6 +68,7 @@
+ #define AT91_SMR_IRQ5 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ5)
+ #define AT91_SMR_IRQ6 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ6)
+
++#define AT91C_CONSOLE_DEFAULT_BAUDRATE 115200 /* default serial console baud-rate */
+
+ /*
+ * Serial port configuration.
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/csb337.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/csb337.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/csb337.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/csb337.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,82 @@
++/*
++ * linux/include/asm-arm/arch-at91rm9200/csb337.h
++ *
++ * Copyright (c) 2003 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.
++ *
++ */
++
++#ifndef __ASM_ARCH_HARDWARE_CSB337_H
++#define __ASM_ARCH_HARDWARE_CSB337_H
++
++
++/* AT91RM92000 clocks on CSB337 */
++#define AT91C_MAIN_CLOCK 184320000
++#define AT91C_MASTER_CLOCK 46080000 /* peripheral clock (AT91C_MAIN_CLOCK / 4) */
++#define AT91C_SLOW_CLOCK 32768 /* slow clock */
++#define AT91_PLLB_INIT 0x128a3e19 /* (3.6864 * (650+1) / 25) /2 = 47.9969 */
++
++/* FLASH */
++#define AT91_FLASH_BASE 0x10000000 // NCS0: Flash physical base address
++
++/* SDRAM */
++#define AT91_SDRAM_BASE 0x20000000 // NCS1: SDRAM physical base address
++
++/* SmartMedia */
++#define AT91_SMARTMEDIA_BASE 0x40000000 // NCS3: Smartmedia physical base address
++
++/* Multi-Master Memory controller */
++#define AT91_UHP_BASE 0x00300000 // USB Host controller
++
++
++/* Peripheral interrupt configuration */
++#define AT91_SMR_FIQ (AT91C_AIC_PRIOR_HIGHEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (FIQ)
++#define AT91_SMR_SYS (AT91C_AIC_PRIOR_HIGHEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // System Peripheral
++#define AT91_SMR_PIOA (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Parallel IO Controller A
++#define AT91_SMR_PIOB (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Parallel IO Controller B
++#define AT91_SMR_PIOC (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Parallel IO Controller C
++#define AT91_SMR_PIOD (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Parallel IO Controller D
++#define AT91_SMR_US0 (AT91C_AIC_PRIOR_6 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // USART 0
++#define AT91_SMR_US1 (AT91C_AIC_PRIOR_6 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // USART 1
++#define AT91_SMR_US2 (AT91C_AIC_PRIOR_6 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // USART 2
++#define AT91_SMR_US3 (AT91C_AIC_PRIOR_6 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // USART 3
++#define AT91_SMR_MCI (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Multimedia Card Interface
++#define AT91_SMR_UDP (AT91C_AIC_PRIOR_4 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // USB Device Port
++#define AT91_SMR_TWI (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Two-Wire Interface
++#define AT91_SMR_SPI (AT91C_AIC_PRIOR_6 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Serial Peripheral Interface
++#define AT91_SMR_SSC0 (AT91C_AIC_PRIOR_5 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Serial Synchronous Controller 0
++#define AT91_SMR_SSC1 (AT91C_AIC_PRIOR_5 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Serial Synchronous Controller 1
++#define AT91_SMR_SSC2 (AT91C_AIC_PRIOR_5 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Serial Synchronous Controller 2
++#define AT91_SMR_TC0 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Timer Counter 0
++#define AT91_SMR_TC1 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Timer Counter 1
++#define AT91_SMR_TC2 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Timer Counter 2
++#define AT91_SMR_TC3 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Timer Counter 3
++#define AT91_SMR_TC4 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Timer Counter 4
++#define AT91_SMR_TC5 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Timer Counter 5
++#define AT91_SMR_UHP (AT91C_AIC_PRIOR_3 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // USB Host port
++#define AT91_SMR_EMAC (AT91C_AIC_PRIOR_3 | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Ethernet MAC
++#define AT91_SMR_IRQ0 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ0)
++#define AT91_SMR_IRQ1 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ1)
++#define AT91_SMR_IRQ2 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ2)
++#define AT91_SMR_IRQ3 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ3)
++#define AT91_SMR_IRQ4 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ4)
++#define AT91_SMR_IRQ5 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ5)
++#define AT91_SMR_IRQ6 (AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE) // Advanced Interrupt Controller (IRQ6)
++
++
++#define AT91C_CONSOLE_DEFAULT_BAUDRATE 38400
++
++/*
++ * Serial port configuration.
++ * 0 .. 3 = USART0 .. USART3
++ * 4 = DBGU
++ */
++#define AT91C_UART_MAP { 4, 1, -1, -1, -1 } /* ttyS0, ..., ttyS4 */
++#define AT91C_CONSOLE 0 /* ttyS0 */
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/hardware.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/hardware.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/hardware.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/hardware.h 2005-02-18 17:48:44.000000000 +0000
+@@ -60,6 +60,7 @@
+
+
+ #define AT91C_BASE_SRAM 0x00200000 /* Internal SRAM base address */
++#define AT91C_SRAM_SIZE 0x00004000 /* Internal SRAM SIZE (16Kb) */
+
+ #define AT91C_NR_UART 5 /* 4 USART3's and one DBGU port */
+
+@@ -82,5 +83,8 @@
+ #include <asm/arch/at91rm9200dk.h>
+ #endif
+
++#ifdef CONFIG_MACH_CSB337
++#include <asm/arch/csb337.h>
++#endif
+
+ #endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/pio.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/pio.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/pio.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/pio.h 2005-02-18 17:48:44.000000000 +0000
+@@ -17,7 +17,15 @@
+
+ static inline void AT91_CfgPIO_USART0(void) {
+ AT91_SYS->PIOA_PDR = AT91C_PA17_TXD0 | AT91C_PA18_RXD0
+- | AT91C_PA20_CTS0 | AT91C_PA21_RTS0;
++ | AT91C_PA20_CTS0;
++
++ /*
++ * Errata #39 - RTS0 is not internally connected to PA21. We need to drive
++ * the pin manually. Default is off (RTS is active low).
++ */
++ AT91_SYS->PIOA_PER = AT91C_PA21_RTS0;
++ AT91_SYS->PIOA_OER = AT91C_PA21_RTS0;
++ AT91_SYS->PIOA_SODR = AT91C_PA21_RTS0;
+ }
+
+ static inline void AT91_CfgPIO_USART1(void) {
+@@ -66,6 +74,18 @@
+ }
+
+ /*
++ * Configure interrupt from Ethernet PHY.
++ */
++static inline void AT91_CfgPIO_EMAC_PHY(void) {
++ AT91_SYS->PMC_PCER = 1 << AT91C_ID_PIOC; /* enable peripheral clock */
++#ifdef CONFIG_MACH_CSB337
++ AT91_SYS->PIOC_ODR = AT91C_PIO_PC2;
++#else
++ AT91_SYS->PIOC_ODR = AT91C_PIO_PC4;
++#endif
++}
++
++/*
+ * Enable the Two-Wire interface.
+ */
+ static inline void AT91_CfgPIO_TWI(void) {
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/time.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/time.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-at91rm9200/time.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-at91rm9200/time.h 2005-02-18 17:48:44.000000000 +0000
+@@ -27,6 +27,21 @@
+ extern unsigned long (*gettimeoffset)(void);
+
+ /*
++ * The ST_CRTR is updated asynchronously to the master clock. It is therefore
++ * necessary to read it twice (with the same value) to ensure accuracy.
++ */
++static inline unsigned long read_CRTR(void) {
++ unsigned long x1, x2;
++
++ do {
++ x1 = AT91_SYS->ST_CRTR;
++ x2 = AT91_SYS->ST_CRTR;
++ } while (x1 != x2);
++
++ return x1;
++}
++
++/*
+ * Returns number of microseconds since last timer interrupt. Note that interrupts
+ * will have been disabled by do_gettimeofday()
+ * 'LATCH' is hwclock ticks (see CLOCK_TICK_RATE in timex.h) per jiffy.
+@@ -36,7 +51,7 @@
+ {
+ unsigned long elapsed;
+
+- elapsed = (AT91_SYS->ST_CRTR - AT91_SYS->ST_RTAR) & AT91C_ST_ALMV;
++ elapsed = (read_CRTR() - AT91_SYS->ST_RTAR) & AT91C_ST_ALMV;
+
+ return (unsigned long)(elapsed * tick) / LATCH;
+ }
+@@ -48,11 +63,12 @@
+ {
+ if (AT91_SYS->ST_SR & AT91C_ST_PITS) { /* This is a shared interrupt */
+ do {
++ do_leds();
+ do_timer(regs);
+
+ AT91_SYS->ST_RTAR = (AT91_SYS->ST_RTAR + LATCH) & AT91C_ST_ALMV;
+
+- } while (((AT91_SYS->ST_CRTR - AT91_SYS->ST_RTAR) & AT91C_ST_ALMV) >= LATCH);
++ } while (((read_CRTR() - AT91_SYS->ST_RTAR) & AT91C_ST_ALMV) >= LATCH);
+
+ do_profile(regs);
+ }
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/cpld.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/cpld.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/cpld.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/cpld.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,43 @@
++/* linux/include/asm-arm/arch-bast/cpld.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST - CPLD control constants
++ *
++ * 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:
++ * 25-May-2003 BJD Created file, added CTRL1 registers
++ * 11-Nov-2003 BJD Updated with CTRL2, 3 and 5
++ * 16-Mar-2004 BJD Added VR1000 CTRL2 definitions for RAM WP
++*/
++
++#define BAST_CPLD_CTRL1_LRCOFF (0x00)
++#define BAST_CPLD_CTRL1_LRCADC (0x01)
++#define BAST_CPLD_CTRL1_LRCDAC (0x02)
++#define BAST_CPLD_CTRL1_LRCARM (0x03)
++#define BAST_CPLD_CTRL1_LRMASK (0x03)
++
++#define BAST_CPLD_CTRL2_WNAND (0x04)
++#define BAST_CPLD_CTLR2_IDERST (0x08)
++
++/* VR1000 redefines the NAND WP to use for the onboard RAM */
++#define VR1000_CPLD_CTRL2_RAMWEN (0x04)
++
++#define BAST_CPLD_CTRL3_IDMASK (0x0e)
++#define BAST_CPLD_CTRL3_ROMWEN (0x01)
++
++/* CTRL5 - DMA routing */
++
++#define BAST_CPLD_DMA0_PRIIDE (0<<0)
++#define BAST_CPLD_DMA0_SECIDE (1<<0)
++#define BAST_CPLD_DMA0_ISA15 (2<<0)
++#define BAST_CPLD_DMA0_ISA36 (3<<0)
++
++#define BAST_CPLD_DMA1_PRIIDE (0<<2)
++#define BAST_CPLD_DMA1_SECIDE (1<<2)
++#define BAST_CPLD_DMA1_ISA15 (2<<2)
++#define BAST_CPLD_DMA1_ISA36 (3<<2)
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/dma.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/dma.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/dma.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/dma.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,320 @@
++/* linux/include/asm-arm/arch-bast/dma.h
++ *
++ * Copyright (C) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * Samsung S3C2410X DMA support
++ *
++ * 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:
++ * ??-May-2003 BJD Created file
++ * ??-Jun-2003 BJD Added more dma functionality to go with arch
++*/
++
++
++#ifndef __ASM_ARCH_DMA_H
++#define __ASM_ARCH_DMA_H
++
++#include <linux/config.h>
++#include "hardware.h"
++
++
++/*
++ * This is the maximum DMA address(physical address) that can be DMAd to.
++ *
++ */
++#define MAX_DMA_ADDRESS 0x20000000
++#define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */
++
++
++/* according to the samsung port, we cannot use the regular
++ * dma channels... we must therefore provide our own interface
++ * for DMA, and allow our drivers to use that.
++ */
++
++#define MAX_DMA_CHANNELS 0
++
++
++/* we have 4 dma channels */
++#define S3C2410_DMA_CHANNELS (4)
++
++/* types */
++
++typedef enum {
++ S3C2410_DMA_IDLE,
++ S3C2410_DMA_RUNNING,
++ S3C2410_DMA_PAUSED
++} s3c2410_dma_state_t;
++
++
++/* s3c2410_dma_loadst_t
++ *
++ * This represents the state of the DMA engine, wrt to the loaded / running
++ * transfers. Since we don't have any way of knowing exactly the state of
++ * the DMA transfers, we need to know the state to make decisions on wether
++ * we can
++ *
++ * S3C2410_DMA_NONE
++ *
++ * There are no buffers loaded (the channel should be inactive)
++ *
++ * S3C2410_DMA_1LOADED
++ *
++ * There is one buffer loaded, however it has not been confirmed to be
++ * loaded by the DMA engine. This may be because the channel is not
++ * yet running, or the DMA driver decided that it was too costly to
++ * sit and wait for it to happen.
++ *
++ * S3C2410_DMA_1RUNNING
++ *
++ * The buffer has been confirmed running, and not finisged
++ *
++ * S3C2410_DMA_1LOADED_1RUNNING
++ *
++ * There is a buffer waiting to be loaded by the DMA engine, and one
++ * currently running.
++*/
++
++typedef enum {
++ S3C2410_DMALOAD_NONE,
++ S3C2410_DMALOAD_1LOADED,
++ S3C2410_DMALOAD_1RUNNING,
++ S3C2410_DMALOAD_1LOADED_1RUNNING,
++} s3c2410_dma_loadst_t;
++
++typedef enum {
++ S3C2410_RES_OK,
++ S3C2410_RES_ERR,
++ S3C2410_RES_ABORT
++} s3c2410_dma_buffresult_t;
++
++
++typedef enum s3c2410_dmasrc_e s3c2410_dmasrc_t;
++
++enum s3c2410_dmasrc_e {
++ S3C2410_DMASRC_HW, /* source is memory */
++ S3C2410_DMASRC_MEM /* source is hardware */
++};
++
++/* enum s3c2410_chan_op_e
++ *
++ * operation codes passed to the DMA code by the user, and also used
++ * to inform the current channel owner of any changes to the system state
++*/
++
++enum s3c2410_chan_op_e {
++ S3C2410_DMAOP_START,
++ S3C2410_DMAOP_STOP,
++ S3C2410_DMAOP_PAUSE,
++ S3C2410_DMAOP_RESUME,
++ S3C2410_DMAOP_FLUSH,
++ S3C2410_DMAOP_TIMEOUT, /* internal signal to handler */
++};
++
++typedef enum s3c2410_chan_op_e s3c2410_chan_op_t;
++
++/* flags */
++
++#define S3C2410_DMAF_SLOW (1<<0) /* slow, so don't worry about
++ * waiting for reloads */
++#define S3C2410_DMAF_AUTOSTART (1<<1) /* auto-start if buffer queued */
++
++/* dma buffer */
++
++typedef struct s3c2410_dma_buf_s s3c2410_dma_buf_t;
++
++struct s3c2410_dma_client {
++ char *name;
++};
++
++typedef struct s3c2410_dma_client s3c2410_dma_client_t;
++
++/* s3c2410_dma_buf_s
++ *
++ * internally used buffer structure to describe a queued or running
++ * buffer.
++*/
++
++struct s3c2410_dma_buf_s {
++ s3c2410_dma_buf_t *next;
++ int magic; /* magic */
++ int size; /* buffer size in bytes */
++ dma_addr_t data; /* start of DMA data */
++ dma_addr_t ptr; /* where the DMA got to [1] */
++ void *id; /* client's id */
++};
++
++/* [1] is this updated for both recv/send modes? */
++
++typedef struct s3c2410_dma_chan_s s3c2410_dma_chan_t;
++
++/* s3c2410_dma_cbfn_t
++ *
++ * buffer callback routine type
++*/
++
++typedef void (*s3c2410_dma_cbfn_t)(s3c2410_dma_chan_t *, void *buf, int size,
++ s3c2410_dma_buffresult_t result);
++
++typedef int (*s3c2410_dma_opfn_t)(s3c2410_dma_chan_t *,
++ s3c2410_chan_op_t );
++
++struct s3c2410_dma_stats_s {
++ unsigned long loads;
++ unsigned long timeout_longest;
++ unsigned long timeout_shortest;
++ unsigned long timeout_avg;
++ unsigned long timeout_failed;
++};
++
++typedef struct s3c2410_dma_stats_s s3c2410_dma_stats_t;
++
++/* struct s3c2410_dma_chan_s
++ *
++ * full state information for each DMA channel
++*/
++
++struct s3c2410_dma_chan_s {
++ /* channel state flags and information */
++ unsigned char number; /* number of this dma channel */
++ unsigned char in_use; /* channel allocated */
++ unsigned char irq_claimed; /* irq claimed for channel */
++ unsigned char irq_enabled; /* irq enabled for channel */
++ unsigned char xfer_unit; /* size of an transfer */
++
++ /* channel state */
++
++ s3c2410_dma_state_t state;
++ s3c2410_dma_loadst_t load_state;
++ s3c2410_dma_client_t *client;
++
++ /* channel configuration */
++ s3c2410_dmasrc_t source;
++ unsigned long dev_addr;
++ unsigned long load_timeout;
++ unsigned int flags; /* channel flags */
++
++ /* channel's hardware position and configuration */
++ unsigned long regs; /* channels registers */
++ unsigned int irq; /* channel irq */
++ unsigned long addr_reg; /* data address register */
++ unsigned long dcon; /* default value of DCON */
++
++ /* driver handles */
++ s3c2410_dma_cbfn_t callback_fn; /* buffer done callback */
++ s3c2410_dma_opfn_t op_fn; /* channel operation callback */
++
++ /* stats gathering */
++ s3c2410_dma_stats_t *stats;
++ s3c2410_dma_stats_t stats_store;
++
++ /* buffer list and information */
++ s3c2410_dma_buf_t *curr; /* current dma buffer */
++ s3c2410_dma_buf_t *next; /* next buffer to load */
++ s3c2410_dma_buf_t *end; /* end of queue */
++};
++
++/* the currently allocated channel information */
++extern s3c2410_dma_chan_t s3c2410_chans[];
++
++/* note, we don't really use dma_device_t at the moment */
++typedef unsigned long dma_device_t;
++
++/* functions --------------------------------------------------------------- */
++
++/* s3c2410_dma_request
++ *
++ * request a dma channel exclusivley
++*/
++
++extern int s3c2410_dma_request(dmach_t channel,
++ s3c2410_dma_client_t *, void *dev);
++
++
++/* s3c2410_dma_ctrl
++ *
++ * change the state of the dma channel
++*/
++
++extern int s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op);
++
++/* s3c2410_dma_setflags
++ *
++ * set the channel's flags to a given state
++*/
++
++extern int s3c2410_dma_setflags(dmach_t channel,
++ unsigned int flags);
++
++/* s3c2410_dma_free
++ *
++ * free the dma channel (will also abort any outstanding operations)
++*/
++
++extern int s3c2410_dma_free(dmach_t channel, s3c2410_dma_client_t *);
++
++/* s3c2410_dma_enqueue
++ *
++ * place the given buffer onto the queue of operations for the channel.
++ * The buffer must be allocated from dma coherent memory, or the Dcache/WB
++ * drained before the buffer is given to the DMA system.
++*/
++
++extern int s3c2410_dma_enqueue(dmach_t channel, void *id,
++ dma_addr_t data, int size);
++
++/* s3c2410_dma_config
++ *
++ * configure the dma channel
++*/
++
++extern int s3c2410_dma_config(dmach_t channel, int xferunit, int dcon);
++
++/* s3c2410_dma_devconfig
++ *
++ * configure the device we're talking to
++*/
++
++extern int s3c2410_dma_devconfig(int channel, s3c2410_dmasrc_t source,
++ int hwcfg, unsigned long devaddr);
++
++extern int s3c2410_dma_set_opfn(dmach_t, s3c2410_dma_opfn_t rtn);
++extern int s3c2410_dma_set_buffdone_fn(dmach_t, s3c2410_dma_cbfn_t rtn);
++
++/* DMA Register definitions */
++
++#define S3C2410_DMA_DISRC (0x00)
++#define S3C2410_DMA_DISRCC (0x04)
++#define S3C2410_DMA_DIDST (0x08)
++#define S3C2410_DMA_DIDSTC (0x0C)
++#define S3C2410_DMA_DCON (0x10)
++#define S3C2410_DMA_DSTAT (0x14)
++#define S3C2410_DMA_DCSRC (0x18)
++#define S3C2410_DMA_DCDST (0x1C)
++#define S3C2410_DMA_DMASKTRIG (0x20)
++
++#define S3C2410_DMASKTRIG_STOP (1<<2)
++#define S3C2410_DMASKTRIG_ON (1<<1)
++#define S3C2410_DMASKTRIG_SWTRIG (1<<0)
++
++#define S3C2410_DCOM_DEMAND (0<<31)
++#define S3C2410_DCON_HANDSHAKE (1<<31)
++#define S3C2410_DCON_SYNC_PCLK (0<<30)
++#define S3C2410_DCON_SYNC_HCLK (1<<30)
++
++#define S3C2410_DCON_INTREQ (1<<29)
++
++#define S3C2410_DCON_SRCSHIFT (24)
++
++#define S3C2410_DCON_BYTE (0<<20)
++#define S3C2410_DCON_HALFWORD (1<<20)
++#define S3C2410_DCON_WORD (2<<20)
++
++#define S3C2410_DCON_AUTORELOAD (0<<22)
++#define S3C2410_DCON_NORELOAD (1<<22)
++#define S3C2410_DCON_HWTRIG (1<<23)
++
++#endif /* __ASM_ARCH_DMA_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/hardware.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/hardware.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/hardware.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/hardware.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,31 @@
++/* linux/include/asm-arm/arch-bast/hardware.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST - hardware include file
++ *
++ * 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:
++ * 21-May-2003 BJD Created file
++ * 06-Jun-2003 BJD Added CPU frequency settings
++*/
++
++#ifndef __ASSEMBLY__
++
++/* processor clock settings, in Hz */
++extern int s3c2410_pclk;
++extern int s3c2410_hclk;
++extern int s3c2410_fclk;
++
++#endif /* __ASSEMBLY__ */
++
++#include <asm/sizes.h>
++#include <asm/arch/map.h>
++#include <asm/arch/cpld.h>
++
++/* currently here until moved into config (todo) */
++#define CONFIG_NO_MULTIWORD_IO
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/ide.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/ide.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/ide.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/ide.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,44 @@
++/* linux/include/asm-arm/arch-bast/ide.h
++ *
++ * Copyright (C) 1997 Russell King
++ * Copyright (C) 2003 Simtec Electronics
++ *
++ * 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.
++ *
++ * Modifications:
++ * 29-07-1998 RMK Major re-work of IDE architecture specific code
++ * 16-05-2003 BJD Changed to work with BAST IDE ports
++ */
++
++#include <asm/irq.h>
++
++/*
++ * Set up a hw structure for a specified data port, control port and IRQ.
++ * This should follow whatever the default interface uses.
++ */
++
++static __inline__ void
++ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
++{
++ ide_ioreg_t reg = (ide_ioreg_t) data_port;
++ int i;
++
++ memset(hw, 0, sizeof(*hw));
++
++ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
++ hw->io_ports[i] = reg;
++ reg += 1;
++ }
++ hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port;
++ if (irq)
++ *irq = 0;
++}
++
++/* we initialise our ide devices from the main ide core, due to problems
++ * with doing it in this function
++*/
++
++#define ide_init_default_hwifs() do { } while(0)
++
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/io.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/io.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/io.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/io.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,320 @@
++/*
++ * linux/include/asm-arm/arch-bast/io.h
++ * linux/include/asm-arm/arch-s3c2410/io.h
++ *
++ * Copyright (C) 2002 SAMSUNG ELECTRONICS
++ * SW.LEE <hitchcar at sec.samsung.com>
++ *
++ * Modifications for BAST:
++ * Copyright (C) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * 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 __ASM_ARM_ARCH_IO_H
++#define __ASM_ARM_ARCH_IO_H
++
++#define IO_SPACE_LIMIT 0xffffffff
++ /* Used in kernel/resource.c */
++
++/*
++ * We have the this routine to use usb host device driver
++ *
++ */
++
++#define PCI_IO_VADDR (0x0)
++#define PCI_MEMORY_VADDR (0x0)
++
++/*
++ * Generic virtual read/write
++ */
++#define __arch_getw(a) (*(volatile unsigned short *)(a))
++#define __arch_putw(v,a) (*(volatile unsigned short *)(a) = (v))
++
++/*
++ * Validate and convert memory address for ioremap.
++ */
++#define iomem_valid_addr(iomem,size) (1)
++#define iomem_to_phys(iomem) (iomem)
++
++/*
++ * linux/include/asm-arm/arch-rpc/io.h
++ *
++ * Copyright (C) 1997 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.
++ *
++ * Modifications:
++ * 06-Dec-1997 RMK Created.
++ * 22-May-2003 BJD Modified to use with BAST system
++ */
++
++/*
++ * GCC is totally crap at loading/storing data. We try to persuade it
++ * to do the right thing by using these whereever possible instead of
++ * the above.
++ */
++#define __arch_base_getb(b,o) \
++ ({ \
++ unsigned int v, r = (b); \
++ __asm__ __volatile__( \
++ "ldrb %0, [%1, %2]" \
++ : "=r" (v) \
++ : "r" (r), "Ir" (o)); \
++ v; \
++ })
++
++#define __arch_base_getl(b,o) \
++ ({ \
++ unsigned int v, r = (b); \
++ __asm__ __volatile__( \
++ "ldr %0, [%1, %2]" \
++ : "=r" (v) \
++ : "r" (r), "Ir" (o)); \
++ v; \
++ })
++
++#define __arch_base_putb(v,b,o) \
++ ({ \
++ unsigned int r = (b); \
++ __asm__ __volatile__( \
++ "strb %0, [%1, %2]" \
++ : \
++ : "r" (v), "r" (r), "Ir" (o)); \
++ })
++
++#define __arch_base_putl(v,b,o) \
++ ({ \
++ unsigned int r = (b); \
++ __asm__ __volatile__( \
++ "str %0, [%1, %2]" \
++ : \
++ : "r" (v), "r" (r), "Ir" (o)); \
++ })
++
++/* we have several different areas to deal with IO... there is an standard
++ * ISA area which is used for the PC104 expansion slot, and the rest is
++ * mapped to our IO areas...
++ *
++ * we take anything below the value of A28 to be offset from the base
++ * of the IO base... we may change this later if we don't need to use
++ * in/outb with anything other than the 4 fast/slow/byte/word
++ */
++
++#define __PORT_PCIO(x) ((x) < (1<<28))
++
++#include <asm/arch/map.h>
++
++#define PCIO_BASE (BAST_VA_MULTISPACE)
++#define PCIO_BASE_b (BAST_VA_MULTISPACE | BAST_VAM_CS2)
++#define PCIO_BASE_w (BAST_VA_MULTISPACE | BAST_VAM_CS3)
++
++/* assume we can do outl with 4xoutb */
++#define PCIO_BASE_l (BAST_VA_MULTISPACE)
++
++/*
++ * Dynamic IO functions.
++ */
++
++/* we assume that any non pcio addresses are automatically correctly
++ * qualified for byte/word access at the moment... possibly we need to
++ * fix this
++*/
++
++#define DECLARE_DYN_OUT(sz,fnsuffix,instr) \
++static inline void __out##fnsuffix (unsigned int val, unsigned int port) \
++{ \
++ unsigned long temp; \
++ __asm__ __volatile__( \
++ "cmp %2, #(1<<28)\n\t" \
++ "mov %0, %2\n\t" \
++ "addcc %0, %0, %3\n\t" \
++ "str" instr " %1, [ %0, #0 ]" \
++ : "=&r" (temp) \
++ : "r" (val), "r" (port), "Ir" (PCIO_BASE_##fnsuffix) \
++ : "cc"); \
++}
++
++DECLARE_DYN_OUT(unsigned char, b, "b")
++DECLARE_DYN_OUT(unsigned short, w, "h")
++DECLARE_DYN_OUT(unsigned int, l, "")
++
++#define DECLARE_DYN_IN(sz,fnsuffix,instr) \
++static inline unsigned sz __in##fnsuffix (unsigned int port) \
++{ \
++ unsigned long temp, value; \
++ __asm__ __volatile__( \
++ "cmp %2, #(1<<28)\n\t" \
++ "mov %0, %2\n\t" \
++ "addcc %0, %0, %3\n\t" \
++ "ldr" instr " %1, [%0, #0 ] @ in" #fnsuffix \
++ : "=&r" (temp), "=r" (value) \
++ : "r" (port), "Ir" (PCIO_BASE_##fnsuffix) \
++ : "cc"); \
++ return (unsigned sz)value; \
++}
++
++static inline unsigned int __ioaddr (unsigned int port) \
++{ \
++ if (__PORT_PCIO(port)) \
++ return (unsigned int)(PCIO_BASE + (port)); \
++ else \
++ return (unsigned int)((port)); \
++}
++
++static inline unsigned int __ioaddr_b (unsigned int port) \
++{ \
++ if (__PORT_PCIO(port)) \
++ return (unsigned int)(PCIO_BASE_b + (port)); \
++ else \
++ return (unsigned int)(port); \
++}
++
++static inline unsigned int __ioaddr_w (unsigned int port) \
++{ \
++ if (__PORT_PCIO(port)) \
++ return (unsigned int)(PCIO_BASE_w + (port)); \
++ else \
++ return (unsigned int)((port)); \
++}
++
++
++
++#define DECLARE_IO(sz,fnsuffix,instr) \
++ DECLARE_DYN_IN(sz,fnsuffix,instr)
++
++DECLARE_IO(char,b,"b")
++DECLARE_IO(short,w,"h")
++DECLARE_IO(int,l,"")
++
++#undef DECLARE_IO
++#undef DECLARE_DYN_IN
++
++/*
++ * Constant address IO functions
++ *
++ * These have to be macros for the 'J' constraint to work -
++ * +/-4096 immediate operand.
++ */
++#define __outbc(value,port) \
++({ \
++ if (__PORT_PCIO((port))) \
++ __asm__ __volatile__( \
++ "strb %0, [%1, %2] @ outbc" \
++ : : "r" (value), "r" (PCIO_BASE_b), "Jr" (port)); \
++ else \
++ __asm__ __volatile__( \
++ "strb %0, [%1, #0] @ outbc" \
++ : : "r" (value), "r" (port)); \
++})
++
++#define __inbc(port) \
++({ \
++ unsigned char result; \
++ if (__PORT_PCIO((port))) \
++ __asm__ __volatile__( \
++ "ldrb %0, [%1, %2] @ inbc" \
++ : "=r" (result) : "r" (PCIO_BASE_b), "Jr" (port)); \
++ else \
++ __asm__ __volatile__( \
++ "ldrb %0, [%1, #0] @ inbc" \
++ : "=r" (result) : "r" (port)); \
++ result; \
++})
++
++#define __outwc(value,port) \
++({ \
++ unsigned long v = value; \
++ if (__PORT_PCIO((port))) \
++ __asm__ __volatile__( \
++ "strh %0, [%1, %2] @ outwc" \
++ : : "r" (v), "r" (PCIO_BASE_w), "Jr" (port)); \
++ else \
++ __asm__ __volatile__( \
++ "strh %0, [%1, #0] @ outwc" \
++ : : "r" (v), "r" (port)); \
++})
++
++#define __inwc(port) \
++({ \
++ unsigned short result; \
++ if (__PORT_PCIO((port))) \
++ __asm__ __volatile__( \
++ "ldrh %0, [%1, %2] @ inwc" \
++ : "=r" (result) : "r" (PCIO_BASE_w), "Jr" (port)); \
++ else \
++ __asm__ __volatile__( \
++ "ldrh %0, [%1, #0 ] @ inwc" \
++ : "=r" (result) : "r" (port)); \
++ result; \
++})
++
++#define __outlc(value,port) \
++({ \
++ unsigned long v = value; \
++ if (__PORT_PCIO((port))) \
++ __asm__ __volatile__( \
++ "strh %0, [%1, %2] @ outlc" \
++ : : "r" (v), "r" (PCIO_BASE_w), "Jr" (port)); \
++ else \
++ __asm__ __volatile__( \
++ "strh %0, [%1, #0] @ outlc" \
++ : : "r" (v), "r" (port)); \
++})
++
++#define __inlc(port) \
++({ \
++ unsigned long result; \
++ if (__PORT_PCIO((port))) \
++ __asm__ __volatile__( \
++ "ldr %0, [%1, %2] @ inlc" \
++ : "=r" (result) : "r" (PCIO_BASE), "Jr" (port)); \
++ else \
++ __asm__ __volatile__( \
++ "ldr %0, [%1, #0] @ inlc" \
++ : "=r" (result) : "r" (port)); \
++ result; \
++})
++
++#define __ioaddrc(port) \
++ (__PORT_PCIO((port)) ? PCIO_BASE + (port ) : (port))
++
++#define inb(p) (__builtin_constant_p((p)) ? __inbc(p) : __inb(p))
++#define inw(p) (__builtin_constant_p((p)) ? __inwc(p) : __inw(p))
++#define inl(p) (__builtin_constant_p((p)) ? __inlc(p) : __inl(p))
++#define outb(v,p) (__builtin_constant_p((p)) ? __outbc(v,p) : __outb(v,p))
++#define outw(v,p) (__builtin_constant_p((p)) ? __outwc(v,p) : __outw(v,p))
++#define outl(v,p) (__builtin_constant_p((p)) ? __outlc(v,p) : __outl(v,p))
++#define __ioaddr(p) (__builtin_constant_p((p)) ? __ioaddr(p) : __ioaddrc(p))
++/* the following macro is depreciated */
++#define ioaddr(port) __ioaddr((port))
++
++#define insb(p,d,l) __raw_readsb(__ioaddr_b(p),d,l)
++#define insw(p,d,l) __raw_readsw(__ioaddr_w(p),d,l)
++#define insl(p,d,l) __raw_readsl(__ioaddr_b(p),d,l)
++
++#define outsb(p,d,l) __raw_writesb(__ioaddr_b(p),d,l)
++#define outsw(p,d,l) __raw_writesw(__ioaddr_w(p),d,l)
++#define outsl(p,d,l) __raw_writesl(__ioaddr_b(p),d,l)
++
++
++#define iomem_valid_addr(o,s) (1)
++#define iomem_to_phys(a) (a)
++
++#define __mem_pci(x) (x)
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/irq.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/irq.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/irq.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/irq.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,127 @@
++/* linux/include/asm-arm/arch-bast/irq.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST - IRQ handling
++ *
++ * 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:
++ * 14-May-2003 BJD Created file
++ * 21-Jun-2003 BJD Added checking for sub irqs
++*/
++
++#include <asm/arch/map.h>
++#include <asm/io.h>
++
++#include <asm/arch-s3c2410/S3C2410-lcd.h>
++
++#include <asm/mach-types.h>
++
++static inline int
++fixup_irq(int irqno)
++{
++ unsigned long temp;
++ int i;
++
++ switch (irqno) {
++ case IRQ_UART0:
++ case IRQ_UART1:
++ case IRQ_UART2:
++ case IRQ_ADCPARENT:
++ /* all these indicate sub-source, read the pending */
++ temp = __raw_readl(S3C2410_VA_IRQ + 0x18);
++
++ /* ensure masked ignored */
++ temp &= ~__raw_readl(S3C2410_VA_IRQ + 0x1c);
++
++#if 0
++ llprintk("irq=%d, temp=%08x\n", irqno, temp);
++#endif
++ for (i = IRQ_S3CUART_RX0; i <= IRQ_ADC; i++) {
++ if (temp & 1)
++ return i;
++ temp >>= 1;
++ }
++
++ printk(KERN_ERR "unhandled sub irq (no=%d (%d), substat=%08x, submask=%08x, mask=%08x, pend=%08x, srcpend=%08x)\n",
++ irqno,
++ __raw_readl(S3C2410_VA_IRQ + 0x14),
++ __raw_readl(S3C2410_VA_IRQ + 0x18),
++ __raw_readl(S3C2410_VA_IRQ + 0x1c),
++ __raw_readl(S3C2410_VA_IRQ + 0x08),
++ __raw_readl(S3C2410_VA_IRQ + 0x10),
++ __raw_readl(S3C2410_VA_IRQ + 0x00));
++
++ temp = __raw_readl(S3C2410_VA_IRQ + 0x18);
++ temp &= __raw_readl(S3C2410_VA_IRQ + 0x1c);
++
++ /* we've got an unhandled interrupt... */
++
++ switch (irqno) {
++ case IRQ_UART0:
++ __raw_writel(7, S3C2410_VA_IRQ + 0x18);
++ return IRQ_UART0;
++
++ case IRQ_UART1:
++ __raw_writel(7<<3, S3C2410_VA_IRQ + 0x18);
++ return IRQ_UART1;
++
++ case IRQ_UART2:
++ __raw_writel(7<<6, S3C2410_VA_IRQ + 0x18);
++ return IRQ_UART2;
++
++ case IRQ_ADCPARENT:
++ __raw_writel(3<<9, S3C2410_VA_IRQ + 0x18);
++ return IRQ_ADCPARENT;
++ }
++
++ break;
++
++ case IRQ_ISA:
++ /* IRQ_ISA is the multiplexed irq from the system CPLD */
++
++ if (machine_is_bast()) {
++ temp = __raw_readb(BAST_VA_PC104_IRQREQ) & 0xf;
++
++ if (temp & 1)
++ return 3;
++
++ if (temp & 2)
++ return 5;
++
++ if (temp & 4)
++ return 7;
++
++ if (temp & 8)
++ return 10;
++ }
++
++ break;
++
++ case IRQ_LCD:
++ temp = __raw_readl(S3C2410_LCDINTPND);
++ if (temp & 2)
++ return IRQ_LCD_FRAME;
++
++ if (temp & 1)
++ return IRQ_LCD_FIFO;
++
++ printk(KERN_ERR "IRQ_LCD: invalid status=%08x\n", temp);
++
++ default:
++ }
++
++ return irqno;
++}
++
++static __inline__ void
++irq_init_irq(void)
++{
++ /* todo: check that we shouldn't just leave this to the
++ * architecture init sequence for our machine
++ */
++}
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/irqs.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/irqs.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/irqs.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/irqs.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,135 @@
++/* linux/include/asm-arm/arch-bast/irqs.h
++ *
++ * Copyright (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * 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:
++ * 12-May-2003 BJD Created file
++ */
++
++
++#ifndef __BAST_IRQ_H
++#define __BAST_IRQ_H __FILE__
++
++
++/* we keep the first set of CPU IRQs out of the range of
++ * the ISA space, so that the PC104 has them to itself
++ * and we don't end up having to do horrible things to the
++ * standard ISA drivers....
++ */
++
++#define BAST_CPUIRQ_OFFSET (16)
++
++#define BAST_IRQ(x) ((x) + BAST_CPUIRQ_OFFSET)
++
++/* main cpu interrupts */
++#define IRQ_EINT0 BAST_IRQ(0) /* 16 */
++#define IRQ_EINT1 BAST_IRQ(1)
++#define IRQ_EINT2 BAST_IRQ(2)
++#define IRQ_EINT3 BAST_IRQ(3)
++#define IRQ_EINT4t7 BAST_IRQ(4)
++#define IRQ_EINT8t23 BAST_IRQ(5)
++#define IRQ_RESERVED6 BAST_IRQ(6)
++#define IRQ_BATT_FLT BAST_IRQ(7)
++#define IRQ_TICK BAST_IRQ(8) /* 24 */
++#define IRQ_WDT BAST_IRQ(9)
++#define IRQ_TIMER0 BAST_IRQ(10)
++#define IRQ_TIMER1 BAST_IRQ(11)
++#define IRQ_TIMER2 BAST_IRQ(12)
++#define IRQ_TIMER3 BAST_IRQ(13)
++#define IRQ_TIMER4 BAST_IRQ(14)
++#define IRQ_UART2 BAST_IRQ(15)
++#define IRQ_LCD BAST_IRQ(16) /* 32 */
++#define IRQ_DMA0 BAST_IRQ(17)
++#define IRQ_DMA1 BAST_IRQ(18)
++#define IRQ_DMA2 BAST_IRQ(19)
++#define IRQ_DMA3 BAST_IRQ(20)
++#define IRQ_SDI BAST_IRQ(21)
++#define IRQ_SPI0 BAST_IRQ(22)
++#define IRQ_UART1 BAST_IRQ(23)
++#define IRQ_RESERVED24 BAST_IRQ(24) /* 40 */
++#define IRQ_USBD BAST_IRQ(25)
++#define IRQ_USBH BAST_IRQ(26)
++#define IRQ_IIC BAST_IRQ(27)
++#define IRQ_UART0 BAST_IRQ(28) /* 44 */
++#define IRQ_SPI1 BAST_IRQ(29)
++#define IRQ_RTC BAST_IRQ(30)
++#define IRQ_ADCPARENT BAST_IRQ(31)
++
++/* interrupts generated from the external interrupts sources */
++#define IRQ_EINT4 BAST_IRQ(32) /* 48 */
++#define IRQ_EINT5 BAST_IRQ(33)
++#define IRQ_EINT6 BAST_IRQ(34)
++#define IRQ_EINT7 BAST_IRQ(35)
++#define IRQ_EINT8 BAST_IRQ(36)
++#define IRQ_EINT9 BAST_IRQ(37)
++#define IRQ_EINT10 BAST_IRQ(38)
++#define IRQ_EINT11 BAST_IRQ(39)
++#define IRQ_EINT12 BAST_IRQ(40)
++#define IRQ_EINT13 BAST_IRQ(41)
++#define IRQ_EINT14 BAST_IRQ(42)
++#define IRQ_EINT15 BAST_IRQ(43)
++#define IRQ_EINT16 BAST_IRQ(44)
++#define IRQ_EINT17 BAST_IRQ(45)
++#define IRQ_EINT18 BAST_IRQ(46)
++#define IRQ_EINT19 BAST_IRQ(47)
++#define IRQ_EINT20 BAST_IRQ(48) /* 64 */
++#define IRQ_EINT21 BAST_IRQ(49)
++#define IRQ_EINT22 BAST_IRQ(50)
++#define IRQ_EINT23 BAST_IRQ(51)
++
++
++#define IRQ_EINT(x) BAST_IRQ((x >= 4) ? (IRQ_EINT4 + (x) - 4) : (BAST_IRQ(0) + (x)))
++
++#define IRQ_LCD_FIFO BAST_IRQ(52)
++#define IRQ_LCD_FRAME BAST_IRQ(53)
++
++/* IRQs for the interal UARTs, and ADC
++ * these need to be ordered in number of appearance in the
++ * SUBSRC mask register
++*/
++#define IRQ_S3CUART_RX0 BAST_IRQ(54) /* 70 */
++#define IRQ_S3CUART_TX0 BAST_IRQ(55) /* 71 */
++#define IRQ_S3CUART_ERR0 BAST_IRQ(56)
++
++#define IRQ_S3CUART_RX1 BAST_IRQ(57)
++#define IRQ_S3CUART_TX1 BAST_IRQ(58)
++#define IRQ_S3CUART_ERR1 BAST_IRQ(59)
++
++#define IRQ_S3CUART_RX2 BAST_IRQ(60)
++#define IRQ_S3CUART_TX2 BAST_IRQ(61)
++#define IRQ_S3CUART_ERR2 BAST_IRQ(62)
++
++#define IRQ_TC BAST_IRQ(63)
++#define IRQ_ADC BAST_IRQ(64)
++
++#define NR_IRQS (IRQ_ADC+1)
++
++
++/* irq numbers to onboard peripherals */
++
++#define IRQ_IDE0 IRQ_EINT16
++#define IRQ_IDE1 IRQ_EINT17
++#define IRQ_PCSERIAL1 IRQ_EINT15
++#define IRQ_PCSERIAL2 IRQ_EINT14
++#define IRQ_PCPARALLEL IRQ_EINT13
++#define IRQ_ASIX IRQ_EINT11
++#define IRQ_DM9000 IRQ_EINT10
++#define IRQ_ISA IRQ_EINT9
++#define IRQ_SMALERT IRQ_EINT8
++
++/* VR1000 serial interrupts */
++#define IRQ_VR1000_SERIAL IRQ_EINT12
++
++#define IRQ_VR1000_SERIAL0 IRQ_EINT12
++#define IRQ_VR1000_SERIAL1 IRQ_EINT13
++#define IRQ_VR1000_SERIAL2 IRQ_EINT14
++#define IRQ_VR1000_SERIAL3 IRQ_EINT15
++
++/* usb over-current on EINT19 */
++
++#endif /* __BAST_IRQ_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/keyboard.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/keyboard.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/keyboard.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/keyboard.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,79 @@
++/* linux/include/asm-arm/mach-bast/keyboard.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * Based on linux/include/asm-arm/arch-sa1100/keyboard.h
++ *
++ * BAST - CPLD control constants
++ *
++ * 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:
++ * 14-Jun-2003 BJD Created file
++ *
++ */
++
++#ifndef __BAST_KEYBOARD_H
++#define __BAST_KEYBOARD_H
++
++#include <linux/config.h>
++#include <asm/mach-types.h>
++
++/* currently we don't have any keyboard code, but
++ * we can implement a scanning keyboard on GPIO
++ * pins or we can have an usb keyboard connected...
++ */
++
++#define kbd_disable_irq() do { } while(0)
++#define kbd_enable_irq() do { } while(0)
++
++extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
++extern int pckbd_getkeycode(unsigned int scancode);
++extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
++ char raw_mode);
++
++extern char pckbd_unexpected_up(unsigned char keycode);
++extern void pckbd_leds(unsigned char leds);
++extern void pckbd_init_hw(void);
++extern unsigned char pckbd_sysrq_xlate[128];
++
++static inline void kbd_init_hw(void)
++{
++ k_setkeycode = pckbd_setkeycode;
++ k_getkeycode = pckbd_getkeycode;
++ k_translate = pckbd_translate;
++ k_unexpected_up = pckbd_unexpected_up;
++ k_leds = pckbd_leds;
++#ifdef CONFIG_MAGIC_SYSRQ
++ k_sysrq_key = 0x54;
++ k_sysrq_xlate = pckbd_sysrq_xlate;
++#endif
++ //pckbd_init_hw();
++}
++
++
++/*
++ * The rest of this file is to do with supporting pc_keyb.c
++ */
++
++/* resource allocation */
++#define kbd_request_region() do { } while(0)
++#define kbd_request_irq(handler) do { } while(0)
++
++/* How to access the keyboard macros on this platform. */
++#define kbd_read_input() 0
++#define kbd_read_status() 0
++#define kbd_write_output(val) do { } while(0)
++#define kbd_write_command(val) do { } while(0)
++
++/* Some stoneage hardware needs delays after some operations. */
++#define kbd_pause() do { } while(0)
++
++#define aux_request_irq(hand, dev_id) 0
++#define aux_free_irq(dev_id) do { } while(0)
++
++
++#endif /* __BAST_KEYBOARD_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/map.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/map.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/map.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/map.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,255 @@
++/* linux/include/asm-arm/arch-bast/map.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST - Memory map definitions
++ *
++ * 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:
++ * 12-May-2003 BJD Created file
++ *
++ */
++
++#ifndef __ASM_ARCH_MAP_H
++#define __ASM_ARCH_MAP_H
++
++/* we have a bit of a tight squeeze to fit all our registers from
++ * 0xD80000000 upwards, since we use all of the nGCS space in some
++ * capacity, and also need to fit the S3C2410 registers in as well...
++ *
++ * we try to ensure stuff like the IRQ registers are available for
++ * an single MOVS instruction (ie, only 8 bits of set data)
++ */
++
++#define S3C2410_ADDR(x) (0xD8000000 + (x))
++
++/* interrupt controller is the first thing we put in, to make
++ * the assembly code for the irq detection easier
++ */
++#define S3C2410_VA_IRQ S3C2410_ADDR(0x00000000)
++#define S3C2410_PA_IRQ (0x4A000000)
++
++/* memory controller registers */
++#define S3C2410_VA_MEMCTRL S3C2410_ADDR(0x00100000)
++#define S3C2410_PA_MEMCTRL (0x48000000)
++
++/* USB host controller */
++#define S3C2410_VA_USBHOST S3C2410_ADDR(0x00200000)
++#define S3C2410_PA_USBHOST (0x49000000)
++
++/* DMA controller */
++#define S3C2410_VA_DMA S3C2410_ADDR(0x00300000)
++#define S3C2410_PA_DMA (0x4B000000)
++
++/* Clock and Power management */
++#define S3C2410_VA_CLKPWR S3C2410_ADDR(0x00400000)
++#define S3C2410_PA_CLKPWR (0x4C000000)
++
++/* LCD controller */
++#define S3C2410_VA_LCD S3C2410_ADDR(0x00600000)
++#define S3C2410_PA_LCD (0x4D000000)
++
++/* NAND flash controller */
++#define S3C2410_VA_NAND S3C2410_ADDR(0x00700000)
++#define S3C2410_PA_NAND (0x4E000000)
++
++/* UARTs */
++#define S3C2410_VA_UART S3C2410_ADDR(0x00800000)
++#define S3C2410_PA_UART (0x50000000)
++
++/* Timers */
++#define S3C2410_VA_TIMER S3C2410_ADDR(0x00900000)
++#define S3C2410_PA_TIMER (0x51000000)
++
++/* USB Device port */
++#define S3C2410_VA_USBDEV S3C2410_ADDR(0x00A00000)
++#define S3C2410_PA_USBDEV (0x52000000)
++
++/* Watchdog */
++#define S3C2410_VA_WATCHDOG S3C2410_ADDR(0x00B00000)
++#define S3C2410_PA_WATCHDOG (0x53000000)
++
++/* IIC hardware controller */
++#define S3C2410_VA_IIC S3C2410_ADDR(0x00C00000)
++#define S3C2410_PA_IIC (0x54000000)
++
++#define VA_IIC_BASE (S3C2410_VA_IIC)
++
++/* IIS controller */
++#define S3C2410_VA_IIS S3C2410_ADDR(0x00D00000)
++#define S3C2410_PA_IIS (0x55000000)
++
++/* GPIO ports */
++#define S3C2410_VA_GPIO S3C2410_ADDR(0x00E00000)
++#define S3C2410_PA_GPIO (0x56000000)
++
++/* RTC */
++#define S3C2410_VA_RTC S3C2410_ADDR(0x00F00000)
++#define S3C2410_PA_RTC (0x57000000)
++
++/* ADC */
++#define S3C2410_VA_ADC S3C2410_ADDR(0x01000000)
++#define S3C2410_PA_ADC (0x58000000)
++
++/* SPI */
++#define S3C2410_VA_SPI S3C2410_ADDR(0x01100000)
++#define S3C2410_PA_SPI (0x59000000)
++
++/* SDI */
++#define S3C2410_VA_SDI S3C2410_ADDR(0x01200000)
++#define S3C2410_PA_SDI (0x5A000000)
++
++/* physical addresses of all the chip-select areas */
++
++#define S3C2410_CS0 (0x00000000)
++#define S3C2410_CS1 (0x08000000)
++#define S3C2410_CS2 (0x10000000)
++#define S3C2410_CS3 (0x18000000)
++#define S3C2410_CS4 (0x20000000)
++#define S3C2410_CS5 (0x28000000)
++#define S3C2410_CS6 (0x30000000)
++#define S3C2410_CS7 (0x38000000)
++
++#define S3C2410_SDRAM_PA (S3C2410_CS6)
++
++/* ok, we've used up to 0x13000000, now we need to find space for the
++ * peripherals that live in the nGCS[x] areas, which are quite numerous
++ * in their space. We also have the board's CPLD to find register space
++ * for.
++ */
++
++#define BAST_IOADDR(x) (S3C2410_ADDR((x) + 0x01300000))
++
++/* we put the CPLD registers next, to get them out of the way */
++
++#define BAST_VA_CTRL1 BAST_IOADDR(0x00000000) /* 0x01300000 */
++#define BAST_PA_CTRL1 (S3C2410_CS5 | 0x7800000)
++
++#define BAST_VA_CTRL2 BAST_IOADDR(0x00100000) /* 0x01400000 */
++#define BAST_PA_CTRL2 (S3C2410_CS1 | 0x6000000)
++
++#define BAST_VA_CTRL3 BAST_IOADDR(0x00200000) /* 0x01500000 */
++#define BAST_PA_CTRL3 (S3C2410_CS1 | 0x6800000)
++
++#define BAST_VA_CTRL4 BAST_IOADDR(0x00300000) /* 0x01600000 */
++#define BAST_PA_CTRL4 (S3C2410_CS1 | 0x7000000)
++
++/* next, we have the PC104 ISA interrupt registers */
++
++#define BAST_PA_PC104_IRQREQ (S3C2410_CS5 | 0x6000000) /* 0x01700000 */
++#define BAST_VA_PC104_IRQREQ BAST_IOADDR(0x00400000)
++
++#define BAST_PA_PC104_IRQRAW (S3C2410_CS5 | 0x6800000) /* 0x01800000 */
++#define BAST_VA_PC104_IRQRAW BAST_IOADDR(0x00500000)
++
++#define BAST_PA_PC104_IRQMASK (S3C2410_CS5 | 0x7000000) /* 0x01900000 */
++#define BAST_VA_PC104_IRQMASK BAST_IOADDR(0x00600000)
++
++#define BAST_PA_LCD_RCMD1 (0x8800000)
++#define BAST_VA_LCD_RCMD1 BAST_IOADDR(0x00700000)
++
++#define BAST_PA_LCD_WCMD1 (0x8000000)
++#define BAST_VA_LCD_WCMD1 BAST_IOADDR(0x00800000)
++
++#define BAST_PA_LCD_RDATA1 (0x9800000)
++#define BAST_VA_LCD_RDATA1 BAST_IOADDR(0x00900000)
++
++#define BAST_PA_LCD_WDATA1 (0x9000000)
++#define BAST_VA_LCD_WDATA1 BAST_IOADDR(0x00A00000)
++
++#define BAST_PA_LCD_RCMD2 (0xA800000)
++#define BAST_VA_LCD_RCMD2 BAST_IOADDR(0x00B00000)
++
++#define BAST_PA_LCD_WCMD2 (0xA000000)
++#define BAST_VA_LCD_WCMD2 BAST_IOADDR(0x00C00000)
++
++#define BAST_PA_LCD_RDATA2 (0xB800000)
++#define BAST_VA_LCD_RDATA2 BAST_IOADDR(0x00D00000)
++
++#define BAST_PA_LCD_WDATA2 (0xB000000)
++#define BAST_VA_LCD_WDATA2 BAST_IOADDR(0x00E00000)
++
++#define VR1000_PA_DEBUG (0x11800000)
++#define VR1000_VA_DEBUG BAST_IOADDR(0x00F00000)
++
++
++/* 0xE0000000 contains the IO space that is split by speed and
++ * wether the access is for 8 or 16bit IO... this ensures that
++ * the correct access is made
++ *
++ * 0x10000000 of space, partitioned as so:
++ *
++ * 0x00000000 to 0x04000000 8bit, slow
++ * 0x04000000 to 0x08000000 16bit, slow
++ * 0x08000000 to 0x0C000000 16bit, net
++ * 0x0C000000 to 0x10000000 16bit, fast
++ *
++ * each of these spaces has the following in:
++ *
++ * 0x00000000 to 0x01000000 16MB ISA IO space
++ * 0x01000000 to 0x02000000 16MB ISA memory space
++ * 0x02000000 to 0x02100000 1MB IDE primary channel
++ * 0x02100000 to 0x02200000 1MB IDE primary channel aux
++ * 0x02200000 to 0x02400000 1MB IDE secondary channel
++ * 0x02300000 to 0x02400000 1MB IDE secondary channel aux
++ * 0x02400000 to 0x02500000 1MB ASIX ethernet controller
++ * 0x02500000 to 0x02600000 1MB Davicom DM9000 ethernet controller
++ * 0x02600000 to 0x02700000 1MB PC SuperIO controller
++ *
++ * the phyiscal layout of the zones are:
++ * nGCS2 - 8bit, slow
++ * nGCS3 - 16bit, slow
++ * nGCS4 - 16bit, net
++ * nGCS5 - 16bit, fast
++ */
++
++#define BAST_VA_MULTISPACE (0xE0000000)
++
++#define BAST_VA_ISAIO (BAST_VA_MULTISPACE + 0x00000000)
++#define BAST_VA_ISAMEM (BAST_VA_MULTISPACE + 0x01000000)
++#define BAST_VA_IDEPRI (BAST_VA_MULTISPACE + 0x02000000)
++#define BAST_VA_IDEPRIAUX (BAST_VA_MULTISPACE + 0x02100000)
++#define BAST_VA_IDESEC (BAST_VA_MULTISPACE + 0x02200000)
++#define BAST_VA_IDESECAUX (BAST_VA_MULTISPACE + 0x02300000)
++#define BAST_VA_ASIXNET (BAST_VA_MULTISPACE + 0x02400000)
++#define BAST_VA_DM9000 (BAST_VA_MULTISPACE + 0x02500000)
++#define BAST_VA_SUPERIO (BAST_VA_MULTISPACE + 0x02600000)
++
++#define BAST_VA_MULTISPACE (0xE0000000)
++
++#define BAST_VAM_CS2 (0x00000000)
++#define BAST_VAM_CS3 (0x04000000)
++#define BAST_VAM_CS4 (0x08000000)
++#define BAST_VAM_CS5 (0x0C000000)
++
++/* physical offset addresses for the peripherals */
++
++#define BAST_PA_ISAIO (0x00000000)
++#define BAST_PA_ASIXNET (0x01000000)
++#define BAST_PA_SUPERIO (0x01800000)
++#define BAST_PA_IDEPRI (0x02000000)
++#define BAST_PA_IDEPRIAUX (0x02800000)
++#define BAST_PA_IDESEC (0x03000000)
++#define BAST_PA_IDESECAUX (0x03800000)
++#define BAST_PA_ISAMEM (0x04000000)
++#define BAST_PA_DM9000 (0x05000000)
++
++/* some configurations for the peripherals */
++
++#define BAST_PCSIO (BAST_VA_SUPERIO + BAST_VAM_CS2)
++/* */
++
++#define BAST_ASIXNET_CS BAST_VAM_CS5
++#define BAST_IDE_CS BAST_VAM_CS5
++#define BAST_DM9000_CS BAST_VAM_CS4
++
++#define VR1000_PA_SERIAL (0x11800000)
++
++/* VR1000 ram is in CS1, with A26..A24 = 2_101 */
++#define VR1000_PA_SRAM (0x08000000 + 0x05000000)
++
++#endif /* __ASM_ARCH_MAP_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/memory.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/memory.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/memory.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/memory.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,81 @@
++/* linux/include/asm-arm/arch-bast/memory.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * Based on linux/include/asm-arm/arch-s3c2410/memory.h
++ *
++ * BAST - BAST Memory cdefines
++ *
++ * 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:
++ * 12-May-2003 BJD Created file, added CTRL1 registers
++ *
++ */
++
++
++#ifndef __ASM_ARCH_MEMORY_H
++#define __ASM_ARCH_MEMORY_H
++
++#include <linux/config.h>
++
++/*
++ * Task size: 3GB
++ */
++#define TASK_SIZE (0xc0000000UL)
++#define TASK_SIZE_26 (0x04000000UL)
++
++/*
++ * This decides where the kernel will search for a free chunk of vm
++ * space during mmap's.
++ */
++#define TASK_UNMAPPED_BASE (TASK_SIZE / 3)
++
++/*
++ * Page offset: 3GB
++ */
++#define PAGE_OFFSET (0xC0000000UL)
++
++/*
++ * Physical SDRAM offset is 0x30000000 on the S3C2410
++ */
++#define PHYS_OFFSET (0x30000000UL)
++
++/*
++ * We take advantage of the fact that physical and virtual address can be the
++ * same. The NUMA code is handling the large holes that might exist between
++ * all memory banks.
++ */
++#define __virt_to_phys__is_a_macro
++#define __phys_to_virt__is_a_macro
++
++/* Modified by SW.LEE */
++#define __virt_to_phys(x) ( ((x)- PAGE_OFFSET)+PHYS_OFFSET )
++#define __phys_to_virt(x) ( ((x)- PHYS_OFFSET)+PAGE_OFFSET )
++
++/*
++ * Virtual view <-> DMA view memory address translations
++ * virt_to_bus: Used to translate the virtual address to an
++ * address suitable to be passed to set_dma_addr
++ * bus_to_virt: Used to convert an address for DMA operations
++ * to an address that the kernel can use.
++ *
++ *
++ */
++#define __Distance_PA_VA 0x90000000
++#define __virt_to_bus__is_a_macro
++#define __bus_to_virt__is_a_macro
++#define __virt_to_bus(x) ((x - __Distance_PA_VA))
++#define __bus_to_virt(x) ((x + __Distance_PA_VA))
++
++
++#define PHYS_TO_NID(addr) (0) /* node 0 */
++#define NR_NODES 1
++ /* used in fucntion bootmem_init arch/arm/mm/init.c */
++
++
++
++#endif /* __ASM_ARCH_MEMORY_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/param.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/param.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/param.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/param.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,25 @@
++/* linux/include/asm-arm/arch-bast/param.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST - Machine parameters
++ *
++ * 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:
++ * ??-May-2003 BJD Created file, sorted out HZ problems
++ *
++*/
++
++/* we cannot get our timer down to 100Hz with the setup as is, but we can
++ * manage 200 clock ticks per second...
++ */
++
++#define HZ 200
++
++#if defined(__KERNEL__)
++#define hz_to_std(a) ((a * HZ)/100)
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/s3c2410.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/s3c2410.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/s3c2410.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/s3c2410.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,22 @@
++/* linux/include/asm-arm/arch-bast/s3c2410.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST - S3C2410 include file (for Samsing OHCI code)
++ *
++ * 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:
++ * ??-May-2003 BJD Created file
++*/
++
++/* this allows the usb ohci controller to build
++ */
++
++#ifdef CONFIG_ARCH_BAST
++#include <asm/arch/map.h>
++#define VA_USB_BASE S3C2410_VA_USBHOST
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/serial.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/serial.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/serial.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/serial.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,54 @@
++/*
++ * linux/include/asm-arm/arch-rpc/serial.h
++ * linux/include/asm-arm/arch-bast/serial.h
++ *
++ * Copyright (C) 1996 Russell King.
++ * Copyright (C) 2003 Simtec Electronics
++ *
++ * 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:
++ * 15-10-1996 RMK Created
++ * 19-06-2003 BJD Modified for use with BAST machine
++ * 15-03-2004 BJD Removed standard serial ports
++ */
++#ifndef __ASM_ARCH_SERIAL_H
++#define __ASM_ARCH_SERIAL_H
++
++#include <asm/arch/map.h>
++
++/*
++ * This assumes you have a 1.8432 MHz clock for your UART.
++ *
++ * It'd be nice if someone built a serial card with a 24.576 MHz
++ * clock, since the 16550A is capable of handling a top speed of 1.5
++ * megabits/second; but this requires the faster clock.
++ */
++#define BASE_BAUD (1843200 / 16)
++
++#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
++
++#define RS_TABLE_SIZE 16
++
++ /* UART CLK PORT IRQ FLAGS */
++#define STD_SERIAL_PORT_DEFNS \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS0 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS1 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS2 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS3 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS4 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS5 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS6 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS7 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS8 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS9 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS10 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS11 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS12 */ \
++ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS } /* ttyS13 */
++
++#define EXTRA_SERIAL_PORT_DEFNS
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/system.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/system.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/system.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/system.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,81 @@
++/* linux/include/asm-arm/arch-bast/system.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST - System function defines and includes
++ *
++ * 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:
++ * 12-May-2003 BJD Created file
++ * 14-May-2003 BJD Removed idle to aid debugging
++ * 12-Jun-2003 BJD Added reset via watchdog
++ */
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++#include <asm/arch/map.h>
++
++#include <asm/arch-s3c2410/S3C2410-watchdog.h>
++#include <asm/arch-s3c2410/S3C2410-clock.h>
++
++extern void printascii(const char *);
++
++void
++arch_idle(void)
++{
++ unsigned long reg = S3C2410_CLKCON;
++ int tmp;
++ int i;
++
++ //printascii("arch_idle:\n");
++
++ /* idle the system by using the idle mode which will wait for an
++ * interrupt to happen before restarting the system.
++ */
++
++ /* going into idle state upsets the jtag, so don't do it
++ * at the moment */
++
++#if 0
++ __raw_writel(__raw_readl(reg) | (1<<2), reg);
++
++ /* the samsung port seems to do a loop and then unset idle.. */
++ for (i = 0; i < 50; i++) {
++ tmp = __raw_readl(reg); /* ensure loop not optimised out */
++ }
++
++ //printascii("arch_idle: done\n");
++
++ __raw_writel(__raw_readl(reg) & ~(1<<2), reg);
++#endif
++}
++
++
++static void
++arch_reset(char mode)
++{
++ printk("arch_reset: attempting watchdog reset\n");
++
++ __raw_writel(0, S3C2410_WTCON); /* disable watchdog, to be safe */
++
++ /* put initial values into count and data */
++ __raw_writel(0x100, S3C2410_WTCNT);
++ __raw_writel(0x100, S3C2410_WTDAT);
++
++ /* set the watchdog to go and reset... */
++ __raw_writel(S3C2410_WTCON_ENABLE|S3C2410_WTCON_DIV16|S3C2410_WTCON_RSTEN |
++ S3C2410_WTCON_PRESCALE(0x80), S3C2410_WTCON);
++
++ /* wait for reset to assert... */
++ mdelay(5000);
++
++ panic("Watchdog reset failed to assert reset\n");
++
++ /* we'll take a jump through zero as a poor second */
++ cpu_reset(0);
++}
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/time.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/time.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/time.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/time.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,156 @@
++/*
++ * linux/include/asm-arm/arch-bast/time.h
++ *
++ * Copyright (C) SAMSUNG ELECTRONICS
++ * SW.LEE <hitchcar at sec.samsung.com>
++ *
++ * Copyright (C) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * Modifications to original S3C2410 for BAST
++ *
++ * 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 <asm/system.h>
++#include <asm/leds.h>
++
++#include <asm/io.h>
++#include <asm/arch/map.h>
++#include <asm/arch-s3c2410/S3C2410-timer.h>
++
++extern unsigned long (*gettimeoffset)(void);
++
++/* we assume that the timer has already been configured
++ * by the bootloader to use the TCNT1 input.
++ *
++ * we have 12MHz presented at TCNT1, which we can reasonably
++ * make any HZ value down to 183Hz....
++ */
++
++
++/* timer_startval = the value of the counter counts down from
++ */
++static unsigned long timer_startval;
++
++#ifdef CONFIG_BAST_RTC
++extern void bast_rtc_check();
++#else
++#define bast_rtc_check()
++#endif
++
++/***
++ * Returns microsecond since last clock interrupt. Note that interrupts
++ * will have been disabled by do_gettimeoffset()
++ * IRQs are disabled before entering here from do_gettimeofday()
++ */
++static unsigned long bast_gettimeoffset (void)
++{
++ unsigned long elapsed;
++ unsigned long usec;
++ unsigned long tcnt;
++
++ /*
++ * tick is microsecond unit
++ * long tick = (1000000 + HZ/2) / HZ; timer interrupt period
++ * kernel/timer.c
++ */
++
++ tcnt = __raw_readl(S3C2410_TCNTO(4));
++
++ /* note, we don't check to see if an irq has gone
++ * off and hasn't been handled... */
++
++ elapsed = timer_startval - tcnt;
++
++ /*
++ * A SIMPLE RATIO
++ * (elapsed / rTCNTB4) = ( usec / tick )
++ */
++
++ usec = (elapsed * tick) / timer_startval ;
++ return usec;
++}
++
++
++/*
++ * IRQ handler for the timer
++ */
++static void bast_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++ long flags;
++
++ do_leds();
++ save_flags_cli( flags );
++ do_timer(regs);
++ restore_flags( flags );
++ do_set_rtc();
++ bast_rtc_check();
++ do_profile(regs);
++
++}
++
++/*
++ * Set up timer interrupt, and return the current time in seconds.
++ */
++
++#define dbg(x) printk x
++
++static inline void setup_timer (void)
++{
++ unsigned long tcon;
++ unsigned long tcnt;
++
++ dbg((__FUNCTION__ ": TCNTB4 at %08x\n", S3C2410_TCNTB(4)));
++
++ gettimeoffset = bast_gettimeoffset;
++ timer_irq.handler = bast_timer_interrupt;
++
++ tcon = __raw_readl(S3C2410_TCON);
++ tcnt = (12*(1000*1000)) / HZ;
++
++ dbg((__FUNCTION__ ": tcon=%08lx, tcnt should be %04lx\n", tcon, tcnt));
++
++ /* check to see if timer is within 16bit range... */
++ if (tcnt > 0xffff) {
++ dbg((__FUNCTION__ ": tcnt too large\n"));
++ panic("setup_timer: HZ is too small, cannot configure timer!");
++ return;
++ }
++
++ dbg((__FUNCTION__ ": about to configure timer4\n"));
++ timer_startval = tcnt;
++ __raw_writel(tcnt, S3C2410_TCNTB(4));
++
++ dbg((__FUNCTION__ ": stopping timer\n"));
++
++ /* ensure timer is stopped... */
++
++ tcon &= ~(7<<20);
++ tcon |= S3C2410_TCON_T4RELOAD;
++ tcon |= S3C2410_TCON_T4MANUALUPD;
++
++ __raw_writel(tcon, S3C2410_TCON);
++ __raw_writel(tcnt, S3C2410_TCNTB(4));
++ __raw_writel(tcnt, S3C2410_TCMPB(4));
++
++ dbg((__FUNCTION__ ": adding interrupt\n"));
++ setup_arm_irq(IRQ_TIMER4, &timer_irq);
++
++ dbg((__FUNCTION__ ": starting timer\n"));
++
++ /* start the timer running */
++ tcon |= S3C2410_TCON_T4START;
++ tcon &= ~S3C2410_TCON_T4MANUALUPD;
++ __raw_writel(tcon, S3C2410_TCON);
++}
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/timex.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/timex.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/timex.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/timex.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,27 @@
++/* linux/include/asm-arm/arch-bast/timex.h
++ *
++ * Copyright (C) 2002 Simtec Electronics
++ *
++ * 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
++ */
++
++/* our system timer is generated from the 12MHz board clock, so we
++ * can get a stabe clock, whatever people want to do with the main
++ * pll (oh for a video/audio clock with it's own pll, or a better divisor
++ * scheme... maybe it would have been nice to source the audio clock from
++ * the usb)
++ */
++
++#define CLOCK_TICK_RATE (12*1000*1000)
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/uncompress.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/uncompress.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/uncompress.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/uncompress.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,102 @@
++/* linux/include/asm-arm/arch-bast/uncompress.h
++ *
++ * (c) 2003 Simtec Electronics
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * BAST - Boot uncompress header
++ *
++ * 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, I think this is original work, but it may well be based on someone
++ * elses' uncompress.h... sorry if i've forgotten to credit.
++ *
++ * Changelog:
++ * 22-May-2003 BJD Created file,
++*/
++
++/* defines for UART registers */
++#include "asm/hardware/serial_s3c2410.h"
++
++#include <asm/arch/map.h>
++
++/* how many bytes we allow into the FIFO at a time in FIFO mode */
++#define FIFO_MAX (14)
++
++static unsigned int uart_base = S3C2410_PA_UART;
++
++static __inline__ void
++uart_wr(unsigned int reg, unsigned int val)
++{
++ volatile unsigned int *ptr;
++
++ ptr = (volatile unsigned int *)(reg + uart_base);
++ *ptr = val;
++}
++
++static __inline__ unsigned int
++uart_rd(unsigned int reg)
++{
++ volatile unsigned int *ptr;
++
++ ptr = (volatile unsigned int *)(reg + uart_base);
++ return *ptr;
++}
++
++
++/* currently we do not need the watchdog... */
++#define arch_decomp_wdog()
++
++
++static void error(char *err);
++
++static void
++arch_decomp_setup(void)
++{
++ /* we may need to setup the uart(s) here if we are not running
++ * on an BAST... the BAST will have left the uarts configured
++ * after calling linux.
++ */
++}
++
++/* we can deal with the case the UARTs are being run
++ * in FIFO mode, so that we don't hold up our execution
++ * waiting for tx to happen...
++*/
++
++static void
++putc(char ch)
++{
++ if (ch == '\n')
++ putc('\r'); /* expand newline to \r\n */
++
++ if (uart_rd(S3C2410_UARTFCON_OFF) & S3C2410_UFCON_FIFOMODE) {
++ int level;
++
++ while (1) {
++ level = uart_rd(S3C2410_UARTFSTAT_OFF);
++ level &= S3C2410_UFSTAT_TXMASK;
++ level >>= S3C2410_UFSTAT_TXSHIFT;
++
++ if (level < FIFO_MAX)
++ break;
++ }
++
++ } else {
++ /* not using fifos */
++
++ while ((uart_rd(S3C2410_UARTTRSTAT_OFF) & S3C2410_UTRSTAT_TXFE) != S3C2410_UTRSTAT_TXFE);
++ }
++
++ /* write byte to transmission register */
++ uart_wr(S3C2410_UARTTXH0_OFF, ch);
++}
++
++static void
++puts(const char *ptr)
++{
++ for (; *ptr != '\0'; ptr++) {
++ putc(*ptr);
++ }
++}
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-bast/vmalloc.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/vmalloc.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-bast/vmalloc.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-bast/vmalloc.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,40 @@
++/*
++ * linux/include/asm-arm/arch-s3c2410/vmalloc.h
++ *
++ * Copyright (C) 2000 SAMSUNG ELECTRONICS
++ * SW.LEE <hitchcar at sec.samsung.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
++ */
++
++/*
++ * Just any arbitrary offset to the start of the vmalloc VM area: the
++ * current 8MB value just means that there will be a 8MB "hole" after the
++ * physical memory until the kernel virtual memory starts. That means that
++ * any out-of-bounds memory accesses will hopefully be caught.
++ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
++ * area for the same reason. ;)
++ */
++#define VMALLOC_OFFSET (8*1024*1024)
++
++/* largest physical memory size that can be attached to the S3C2410 */
++#define MAX_S3C2410_SDRAM_SIZE (0x10000000)
++
++/* The VMALLOC area *must* have space for the VM offset and ioremaps etc */
++#define S3C2410_VMALLOC_SIZE (0x8000000)
++
++#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
++#define VMALLOC_VMADDR(x) ((unsigned long)(x))
++#define VMALLOC_END (PAGE_OFFSET + MAX_S3C2410_SDRAM_SIZE + S3C2410_VMALLOC_SIZE)
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-clps711x/system.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-clps711x/system.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-clps711x/system.h 2003-06-13 15:51:38.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-clps711x/system.h 2005-02-18 17:48:44.000000000 +0000
+@@ -28,8 +28,8 @@
+ {
+ clps_writel(1, HALT);
+ __asm__ __volatile__(
+- "mov r0, r0
+- mov r0, r0");
++ "mov r0, r0\n\t"
++ "mov r0, r0");
+ }
+
+ static inline void arch_reset(char mode)
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-integrator/uncompress.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-integrator/uncompress.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-integrator/uncompress.h 2002-08-03 01:39:45.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-integrator/uncompress.h 2005-02-18 17:48:44.000000000 +0000
+@@ -18,6 +18,8 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
++#include <asm/hardware/serial_amba.h>
++
+ #define AMBA_UART_DR (*(volatile unsigned char *)0x16000000)
+ #define AMBA_UART_LCRH (*(volatile unsigned char *)0x16000008)
+ #define AMBA_UART_LCRM (*(volatile unsigned char *)0x1600000c)
+@@ -30,21 +32,25 @@
+ */
+ static void puts(const char *s)
+ {
++ /* Do nothing if the UART is not enabled. */
++ if (!(AMBA_UART_CR & AMBA_UARTCR_UARTEN))
++ return;
++
+ while (*s) {
+- while (AMBA_UART_FR & (1 << 5))
++ while (AMBA_UART_FR & AMBA_UARTFR_TXFF)
+ barrier();
+
+ AMBA_UART_DR = *s;
+
+ if (*s == '\n') {
+- while (AMBA_UART_FR & (1 << 5))
++ while (AMBA_UART_FR & AMBA_UARTFR_TXFF)
+ barrier();
+
+ AMBA_UART_DR = '\r';
+ }
+ s++;
+ }
+- while (AMBA_UART_FR & (1 << 3));
++ while (AMBA_UART_FR & AMBA_UARTFR_BUSY);
+ }
+
+ /*
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-riscstation/system.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-riscstation/system.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-riscstation/system.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-riscstation/system.h 2005-02-18 17:48:44.000000000 +0000
+@@ -1,5 +1,5 @@
+ /*
+- * linux/include/asm-arm/arch-rpc/system.h
++ * linux/include/asm-arm/arch-riscstation/system.h
+ *
+ * Copyright (C) 1996-1999 Russell King.
+ *
+@@ -18,7 +18,7 @@
+
+ static inline void arch_reset(char mode)
+ {
+- iomd_writeb(0, IOMD_ROMCR0);
++ iomd_writeb(0x40, IOMD_ROMCR0);
+
+ /*
+ * Jump into the ROM
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-rpc/uncompress.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-rpc/uncompress.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-rpc/uncompress.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-rpc/uncompress.h 2005-02-18 17:48:44.000000000 +0000
+@@ -11,29 +11,12 @@
+
+ #include <asm/hardware.h>
+ #include <asm/io.h>
++#include <asm/setup.h>
+
+ int video_num_columns, video_num_lines, video_size_row;
+ int white, bytes_per_char_h;
+ extern unsigned long con_charconvtable[256];
+
+-struct param_struct {
+- unsigned long page_size;
+- unsigned long nr_pages;
+- unsigned long ramdisk_size;
+- unsigned long mountrootrdonly;
+- unsigned long rootdev;
+- unsigned long video_num_cols;
+- unsigned long video_num_rows;
+- unsigned long video_x;
+- unsigned long video_y;
+- unsigned long memc_control_reg;
+- unsigned char sounddefault;
+- unsigned char adfsdrives;
+- unsigned char bytes_per_char_h;
+- unsigned char bytes_per_char_v;
+- unsigned long unused[256/4-11];
+-};
+-
+ static const unsigned long palette_4[16] = {
+ 0x00000000,
+ 0x000000cc,
+@@ -69,8 +52,12 @@
+ unsigned char c;
+ char *ptr;
+
+- x = params->video_x;
+- y = params->video_y;
++ /* Don't bother when using a tagged list */
++ if (((struct tag *)params)->hdr.tag == ATAG_CORE)
++ return;
++
++ x = params->u1.s.video_x;
++ y = params->u1.s.video_y;
+
+ while ( ( c = *(unsigned char *)s++ ) != '\0' ) {
+ if ( c == '\n' ) {
+@@ -79,7 +66,7 @@
+ y--;
+ }
+ } else {
+- ptr = VIDMEM + ((y*video_num_columns*params->bytes_per_char_v+x)*bytes_per_char_h);
++ ptr = VIDMEM + ((y*video_num_columns*params->u1.s.bytes_per_char_v+x)*bytes_per_char_h);
+ ll_write_char(ptr, c, white);
+ if ( ++x >= video_num_columns ) {
+ x = 0;
+@@ -90,8 +77,6 @@
+ }
+ }
+
+- params->video_x = x;
+- params->video_y = y;
+ }
+
+ static void error(char *x);
+@@ -103,9 +88,13 @@
+ {
+ int i;
+
+- video_num_lines = params->video_num_rows;
+- video_num_columns = params->video_num_cols;
+- bytes_per_char_h = params->bytes_per_char_h;
++ /* Don't bother when using a tagged list */
++ if (((struct tag *)params)->hdr.tag == ATAG_CORE)
++ return;
++
++ video_num_lines = params->u1.s.video_num_rows;
++ video_num_columns = params->u1.s.video_num_cols;
++ bytes_per_char_h = params->u1.s.bytes_per_char_h;
+ video_size_row = video_num_columns * bytes_per_char_h;
+ if (bytes_per_char_h == 4)
+ for (i = 0; i < 256; i++)
+@@ -140,7 +129,7 @@
+ white = 7;
+ }
+
+- if (params->nr_pages * params->page_size < 4096*1024) error("<4M of mem\n");
++ if (params->u1.s.nr_pages * params->u1.s.page_size < 4096*1024) error("<4M of mem\n");
+ }
+ #endif
+
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-clock.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-clock.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-clock.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-clock.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,76 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-clock.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 clock register definitions
++ *
++ * Changelog:
++ * 19-06-2003 BJD Created file
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_CLOCK_H
++#define ASMARM_ARCH_S3C2410_CLOCK_H
++
++#define S3C2410_CLKREG(x) ((x) + S3C2410_VA_CLKPWR)
++
++#define S3C2410_PLLVAL(_m,_p,_s) ((_m) << 12 | ((_p) << 4) | ((_s)))
++
++#define S3C2410_LOCKTIME S3C2410_CLKREG(0x00)
++#define S3C2410_MPLLCON S3C2410_CLKREG(0x04)
++#define S3C2410_UPLLCON S3C2410_CLKREG(0x08)
++#define S3C2410_CLKCON S3C2410_CLKREG(0x0C)
++#define S3C2410_CLKSLOW S3C2410_CLKREG(0x10)
++#define S3C2410_CLKDIVN S3C2410_CLKREG(0x14)
++
++#define S3C2410_PLLCON_MDIVSHIFT 12
++#define S3C2410_PLLCON_PDIVSHIFT 4
++#define S3C2410_PLLCON_SDIVSHIFT 0
++#define S3C2410_PLLCON_MDIVMASK ((1<<(1+(19-12)))-1)
++#define S3C2410_PLLCON_PDIVMASK ((1<<5)-1)
++#define S3C2410_PLLCON_SDIVMASK 3
++
++/* DCLKCON register addresses in gpio.h */
++
++#define S3C2410_DCLKCON_DCLK0EN (1<<0)
++#define S3C2410_DCLKCON_DCLK0_PCLK (0<<1)
++#define S3C2410_DCLKCON_DCLK0_UCLK (1<<1)
++#define S3C2410_DCLKCON_DCLK0_DIV(x) (((x) - 1 )<<4)
++#define S3C2410_DCLKCON_DCLK0_CMP(x) (((x) - 1 )<<8)
++
++#define S3C2410_DCLKCON_DCLK1EN (1<<16)
++#define S3C2410_DCLKCON_DCLK1_PCLK (0<<17)
++#define S3C2410_DCLKCON_DCLK1_UCLK (1<<17)
++#define S3C2410_DCLKCON_DCLK1_DIV(x) (((x) - 1) <<20)
++
++#define S3C2410_CLKDIVN_PDIVN (1<<0)
++#define S3C2410_CLKDIVN_HDIVN (1<<1)
++
++static inline unsigned int
++s3c2410_get_pll(int pllval, int baseclk)
++{
++ int mdiv, pdiv, sdiv;
++
++ mdiv = pllval >> S3C2410_PLLCON_MDIVSHIFT;
++ pdiv = pllval >> S3C2410_PLLCON_PDIVSHIFT;
++ sdiv = pllval >> S3C2410_PLLCON_SDIVSHIFT;
++
++ mdiv &= S3C2410_PLLCON_MDIVMASK;
++ pdiv &= S3C2410_PLLCON_PDIVMASK;
++ sdiv &= S3C2410_PLLCON_SDIVMASK;
++
++ return (baseclk * (mdiv + 8)) / ((pdiv + 2) << sdiv);
++}
++
++#endif /* ASMARM_ARCH_S3C2410_CLOCK_H */
++
++
++
++
++
++
++
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-gpio.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-gpio.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-gpio.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-gpio.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,602 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-gpio.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 GPIO register definitions
++ *
++ * Changelog:
++ * 19-06-2003 BJD Created file
++ * 23-06-2003 BJD Updated GSTATUS registers
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_GPIO_H
++#define ASMARM_ARCH_S3C2410_GPIO_H
++
++/* configure GPIO ports A..G */
++
++#define S3C2410_GPIOREG(x) ((x) + S3C2410_VA_GPIO)
++
++/* port A - 22bits, zero in bit X makes pin X output
++ * 1 makes port special function, this is default
++*/
++#define S3C2410_GPACON S3C2410_GPIOREG(0x00)
++#define S3C2410_GPADAT S3C2410_GPIOREG(0x04)
++
++/* 0x08 and 0x0c are reserved */
++
++/* GPB is 10 IO pins, each configured by 2 bits each in GPBCON.
++ * 00 = input, 01 = output, 10=special function, 11=reserved
++ * bit 0,1 = pin 0, 2,3= pin 1...
++ *
++ * CPBUP = pull up resistor control, 1=disabled, 0=enabled
++*/
++
++#define S3C2410_GPBCON S3C2410_GPIOREG(0x10)
++#define S3C2410_GPBDAT S3C2410_GPIOREG(0x14)
++#define S3C2410_GPBUP S3C2410_GPIOREG(0x18)
++
++/* no i/o pin in port b can have value 3! */
++
++#define S3C2410_GPB0_INP (0x00 << 0)
++#define S3C2410_GPB0_OUTP (0x01 << 0)
++#define S3C2410_GPB0_TOUT0 (0x02 << 0)
++
++#define S3C2410_GPB1_INP (0x00 << 2)
++#define S3C2410_GPB1_OUTP (0x01 << 2)
++#define S3C2410_GPB1_TOUT1 (0x02 << 2)
++
++#define S3C2410_GPB2_INP (0x00 << 4)
++#define S3C2410_GPB2_OUTP (0x01 << 4)
++#define S3C2410_GPB2_TOUT2 (0x02 << 4)
++
++#define S3C2410_GPB3_INP (0x00 << 6)
++#define S3C2410_GPB3_OUTP (0x01 << 6)
++#define S3C2410_GPB3_TOUT3 (0x02 << 6)
++
++#define S3C2410_GPB4_INP (0x00 << 8)
++#define S3C2410_GPB4_OUTP (0x01 << 8)
++#define S3C2410_GPB4_TCLK0 (0x02 << 8)
++#define S3C2410_GPB4_MASK (0x03 << 8)
++
++#define S3C2410_GPB5_INP (0x00 << 10)
++#define S3C2410_GPB5_OUTP (0x01 << 10)
++#define S3C2410_GPB5_nXBACK (0x02 << 10)
++
++#define S3C2410_GPB6_INP (0x00 << 12)
++#define S3C2410_GPB6_OUTP (0x01 << 12)
++#define S3C2410_GPB6_nXBREQ (0x02 << 12)
++
++#define S3C2410_GPB7_INP (0x00 << 14)
++#define S3C2410_GPB7_OUTP (0x01 << 14)
++#define S3C2410_GPB7_nXDACK1 (0x02 << 14)
++
++#define S3C2410_GPB8_INP (0x00 << 16)
++#define S3C2410_GPB8_OUTP (0x01 << 16)
++#define S3C2410_GPB8_nXDREQ1 (0x02 << 16)
++
++#define S3C2410_GPB9_INP (0x00 << 18)
++#define S3C2410_GPB9_OUTP (0x01 << 18)
++#define S3C2410_GPB9_nXDACK0 (0x02 << 18)
++
++#define S3C2410_GPB10_INP (0x00 << 18)
++#define S3C2410_GPB10_OUTP (0x01 << 18)
++#define S3C2410_GPB10_nXDRE0 (0x02 << 18)
++
++/* Port C consits of 16 GPIO/Special function
++ *
++ * almost identical setup to port b, but the special functions are mostly
++ * to do with the video system's sync/etc.
++*/
++
++#define S3C2410_GPCCON S3C2410_GPIOREG(0x20)
++#define S3C2410_GPCDAT S3C2410_GPIOREG(0x24)
++#define S3C2410_GPCUP S3C2410_GPIOREG(0x28)
++
++#define S3C2410_GPC0_INP (0x00 << 0)
++#define S3C2410_GPC0_OUTP (0x01 << 0)
++#define S3C2410_GPC0_LEND (0x02 << 0)
++
++#define S3C2410_GPC1_INP (0x00 << 2)
++#define S3C2410_GPC1_OUTP (0x01 << 2)
++#define S3C2410_GPC1_VCLK (0x02 << 2)
++
++#define S3C2410_GPC2_INP (0x00 << 4)
++#define S3C2410_GPC2_OUTP (0x01 << 4)
++#define S3C2410_GPC2_VLINE (0x02 << 4)
++
++#define S3C2410_GPC3_INP (0x00 << 6)
++#define S3C2410_GPC3_OUTP (0x01 << 6)
++#define S3C2410_GPC3_VFRAME (0x02 << 6)
++
++#define S3C2410_GPC4_INP (0x00 << 8)
++#define S3C2410_GPC4_OUTP (0x01 << 8)
++#define S3C2410_GPC4_VM (0x02 << 8)
++
++#define S3C2410_GPC5_INP (0x00 << 10)
++#define S3C2410_GPC5_OUTP (0x01 << 10)
++#define S3C2410_GPC5_LCDVF0 (0x02 << 10)
++
++#define S3C2410_GPC6_INP (0x00 << 12)
++#define S3C2410_GPC6_OUTP (0x01 << 12)
++#define S3C2410_GPC6_LCDVF1 (0x02 << 12)
++
++#define S3C2410_GPC7_INP (0x00 << 14)
++#define S3C2410_GPC7_OUTP (0x01 << 14)
++#define S3C2410_GPC7_LCDVF2 (0x02 << 14)
++
++#define S3C2410_GPC8_INP (0x00 << 16)
++#define S3C2410_GPC8_OUTP (0x01 << 16)
++#define S3C2410_GPC8_VD0 (0x02 << 16)
++
++#define S3C2410_GPC9_INP (0x00 << 18)
++#define S3C2410_GPC9_OUTP (0x01 << 18)
++#define S3C2410_GPC9_VD1 (0x02 << 18)
++
++#define S3C2410_GPC10_INP (0x00 << 20)
++#define S3C2410_GPC10_OUTP (0x01 << 20)
++#define S3C2410_GPC10_VD2 (0x02 << 20)
++
++#define S3C2410_GPC11_INP (0x00 << 22)
++#define S3C2410_GPC11_OUTP (0x01 << 22)
++#define S3C2410_GPC11_VD3 (0x02 << 22)
++
++#define S3C2410_GPC12_INP (0x00 << 24)
++#define S3C2410_GPC12_OUTP (0x01 << 24)
++#define S3C2410_GPC12_VD4 (0x02 << 24)
++
++#define S3C2410_GPC13_INP (0x00 << 26)
++#define S3C2410_GPC13_OUTP (0x01 << 26)
++#define S3C2410_GPC13_VD5 (0x02 << 26)
++
++#define S3C2410_GPC14_INP (0x00 << 28)
++#define S3C2410_GPC14_OUTP (0x01 << 28)
++#define S3C2410_GPC14_VD6 (0x02 << 28)
++
++#define S3C2410_GPC15_INP (0x00 << 30)
++#define S3C2410_GPC15_OUTP (0x01 << 30)
++#define S3C2410_GPC15_VD7 (0x02 << 30)
++
++/* Port D consists of 16 GPIO/Special function
++ *
++ * almost identical setup to port b, but the special functions are mostly
++ * to do with the video system's data.
++*/
++
++#define S3C2410_GPDCON S3C2410_GPIOREG(0x30)
++#define S3C2410_GPDDAT S3C2410_GPIOREG(0x34)
++#define S3C2410_GPDUP S3C2410_GPIOREG(0x38)
++
++#define S3C2410_GPD0_INP (0x00 << 0)
++#define S3C2410_GPD0_OUTP (0x01 << 0)
++#define S3C2410_GPD0_VD8 (0x02 << 0)
++
++#define S3C2410_GPD1_INP (0x00 << 2)
++#define S3C2410_GPD1_OUTP (0x01 << 2)
++#define S3C2410_GPD1_VD9 (0x02 << 2)
++
++#define S3C2410_GPD2_INP (0x00 << 4)
++#define S3C2410_GPD2_OUTP (0x01 << 4)
++#define S3C2410_GPD2_VD10 (0x02 << 4)
++
++#define S3C2410_GPD3_INP (0x00 << 6)
++#define S3C2410_GPD3_OUTP (0x01 << 6)
++#define S3C2410_GPD3_VD11 (0x02 << 6)
++
++#define S3C2410_GPD4_INP (0x00 << 8)
++#define S3C2410_GPD4_OUTP (0x01 << 8)
++#define S3C2410_GPD4_VD12 (0x02 << 8)
++
++#define S3C2410_GPD5_INP (0x00 << 10)
++#define S3C2410_GPD5_OUTP (0x01 << 10)
++#define S3C2410_GPD5_VD13 (0x02 << 10)
++
++#define S3C2410_GPD6_INP (0x00 << 12)
++#define S3C2410_GPD6_OUTP (0x01 << 12)
++#define S3C2410_GPD6_VD14 (0x02 << 12)
++
++#define S3C2410_GPD7_INP (0x00 << 14)
++#define S3C2410_GPD7_OUTP (0x01 << 14)
++#define S3C2410_GPD7_VD15 (0x02 << 14)
++
++#define S3C2410_GPD8_INP (0x00 << 16)
++#define S3C2410_GPD8_OUTP (0x01 << 16)
++#define S3C2410_GPD8_VD16 (0x02 << 16)
++
++#define S3C2410_GPD9_INP (0x00 << 18)
++#define S3C2410_GPD9_OUTP (0x01 << 18)
++#define S3C2410_GPD9_VD17 (0x02 << 18)
++
++#define S3C2410_GPD10_INP (0x00 << 20)
++#define S3C2410_GPD10_OUTP (0x01 << 20)
++#define S3C2410_GPD10_VD18 (0x02 << 20)
++
++#define S3C2410_GPD11_INP (0x00 << 22)
++#define S3C2410_GPD11_OUTP (0x01 << 22)
++#define S3C2410_GPD11_VD19 (0x02 << 22)
++
++#define S3C2410_GPD12_INP (0x00 << 24)
++#define S3C2410_GPD12_OUTP (0x01 << 24)
++#define S3C2410_GPD12_VD20 (0x02 << 24)
++
++#define S3C2410_GPD13_INP (0x00 << 26)
++#define S3C2410_GPD13_OUTP (0x01 << 26)
++#define S3C2410_GPD13_VD21 (0x02 << 26)
++
++#define S3C2410_GPD14_INP (0x00 << 28)
++#define S3C2410_GPD14_OUTP (0x01 << 28)
++#define S3C2410_GPD14_VD22 (0x02 << 28)
++
++#define S3C2410_GPD15_INP (0x00 << 30)
++#define S3C2410_GPD15_OUTP (0x01 << 30)
++#define S3C2410_GPD15_VD23 (0x02 << 30)
++
++/* Port E consists of 16 GPIO/Special function
++ *
++ * again, the same as port B, but dealing with I2S, SDI, and
++ * more miscellaneous functions
++*/
++
++#define S3C2410_GPECON S3C2410_GPIOREG(0x40)
++#define S3C2410_GPEDAT S3C2410_GPIOREG(0x44)
++#define S3C2410_GPEUP S3C2410_GPIOREG(0x48)
++
++#define S3C2410_GPE0_INP (0x00 << 0)
++#define S3C2410_GPE0_OUTP (0x01 << 0)
++#define S3C2410_GPE0_I2SLRCK (0x02 << 0)
++#define S3C2410_GPE0_MASK (0x03 << 0)
++
++#define S3C2410_GPE1_INP (0x00 << 2)
++#define S3C2410_GPE1_OUTP (0x01 << 2)
++#define S3C2410_GPE1_I2SSCLK (0x02 << 2)
++#define S3C2410_GPE1_MASK (0x03 << 2)
++
++#define S3C2410_GPE2_INP (0x00 << 4)
++#define S3C2410_GPE2_OUTP (0x01 << 4)
++#define S3C2410_GPE2_CDCLK (0x02 << 4)
++
++#define S3C2410_GPE3_INP (0x00 << 6)
++#define S3C2410_GPE3_OUTP (0x01 << 6)
++#define S3C2410_GPE3_I2SSDI (0x02 << 6)
++#define S3C2410_GPE3_MASK (0x03 << 6)
++
++#define S3C2410_GPE4_INP (0x00 << 8)
++#define S3C2410_GPE4_OUTP (0x01 << 8)
++#define S3C2410_GPE4_I2SSDO (0x02 << 8)
++#define S3C2410_GPE4_MASK (0x03 << 8)
++
++#define S3C2410_GPE5_INP (0x00 << 10)
++#define S3C2410_GPE5_OUTP (0x01 << 10)
++#define S3C2410_GPE5_SDCLK (0x02 << 10)
++
++#define S3C2410_GPE6_INP (0x00 << 12)
++#define S3C2410_GPE6_OUTP (0x01 << 12)
++#define S3C2410_GPE6_SDCLK (0x02 << 12)
++
++#define S3C2410_GPE7_INP (0x00 << 14)
++#define S3C2410_GPE7_OUTP (0x01 << 14)
++#define S3C2410_GPE7_SDCMD (0x02 << 14)
++
++#define S3C2410_GPE8_INP (0x00 << 16)
++#define S3C2410_GPE8_OUTP (0x01 << 16)
++#define S3C2410_GPE8_SDDAT1 (0x02 << 16)
++
++#define S3C2410_GPE9_INP (0x00 << 18)
++#define S3C2410_GPE9_OUTP (0x01 << 18)
++#define S3C2410_GPE9_SDDAT2 (0x02 << 18)
++
++#define S3C2410_GPE10_INP (0x00 << 20)
++#define S3C2410_GPE10_OUTP (0x01 << 20)
++#define S3C2410_GPE10_SDDAT3 (0x02 << 20)
++
++#define S3C2410_GPE11_INP (0x00 << 22)
++#define S3C2410_GPE11_OUTP (0x01 << 22)
++#define S3C2410_GPE11_SPIMISO0 (0x02 << 22)
++
++#define S3C2410_GPE12_INP (0x00 << 24)
++#define S3C2410_GPE12_OUTP (0x01 << 24)
++#define S3C2410_GPE12_SPIMOSI0 (0x02 << 24)
++
++#define S3C2410_GPE13_INP (0x00 << 26)
++#define S3C2410_GPE13_OUTP (0x01 << 26)
++#define S3C2410_GPE13_SPICLK0 (0x02 << 26)
++
++#define S3C2410_GPE14_INP (0x00 << 28)
++#define S3C2410_GPE14_OUTP (0x01 << 28)
++#define S3C2410_GPE14_IICSCL (0x02 << 28)
++#define S3C2410_GPE14_MASK (0x03 << 28)
++
++#define S3C2410_GPE15_INP (0x00 << 30)
++#define S3C2410_GPE15_OUTP (0x01 << 30)
++#define S3C2410_GPE15_IICSDA (0x02 << 30)
++#define S3C2410_GPE15_MASK (0x03 << 30)
++
++#define S3C2410_GPE_PUPDIS(x) (1<<(x))
++
++/* Port F consists of 8 GPIO/Special function
++ *
++ * GPIO / interrupt inputs
++ *
++ * GPFCON has 2 bits for each of the input pins on port F
++ * 00 = 0 input, 1 output, 2 interrupt (EINT0..7), 3 undefined
++ *
++ * pull up works like all other ports.
++*/
++
++#define S3C2410_GPFCON S3C2410_GPIOREG(0x50)
++#define S3C2410_GPFDAT S3C2410_GPIOREG(0x54)
++#define S3C2410_GPFUP S3C2410_GPIOREG(0x58)
++
++
++#define S3C2410_GPF0_INP (0x00 << 0)
++#define S3C2410_GPF0_OUTP (0x01 << 0)
++#define S3C2410_GPF0_EINT0 (0x02 << 0)
++
++#define S3C2410_GPF1_INP (0x00 << 2)
++#define S3C2410_GPF1_OUTP (0x01 << 2)
++#define S3C2410_GPF1_EINT1 (0x02 << 2)
++
++#define S3C2410_GPF2_INP (0x00 << 4)
++#define S3C2410_GPF2_OUTP (0x01 << 4)
++#define S3C2410_GPF2_EINT2 (0x02 << 4)
++
++#define S3C2410_GPF3_INP (0x00 << 6)
++#define S3C2410_GPF3_OUTP (0x01 << 6)
++#define S3C2410_GPF3_EINT3 (0x02 << 6)
++
++#define S3C2410_GPF4_INP (0x00 << 8)
++#define S3C2410_GPF4_OUTP (0x01 << 8)
++#define S3C2410_GPF4_EINT4 (0x02 << 8)
++
++#define S3C2410_GPF5_INP (0x00 << 10)
++#define S3C2410_GPF5_OUTP (0x01 << 10)
++#define S3C2410_GPF5_EINT5 (0x02 << 10)
++
++#define S3C2410_GPF6_INP (0x00 << 12)
++#define S3C2410_GPF6_OUTP (0x01 << 12)
++#define S3C2410_GPF6_EINT6 (0x02 << 12)
++
++#define S3C2410_GPF7_INP (0x00 << 14)
++#define S3C2410_GPF7_OUTP (0x01 << 14)
++#define S3C2410_GPF7_EINT7 (0x02 << 14)
++
++/* Port G consists of 8 GPIO/IRQ/Special function
++ *
++ * GPGCON has 2 bits for each of the input pins on port F
++ * 00 = 0 input, 1 output, 2 interrupt (EINT0..7), 3 special func
++ *
++ * pull up works like all other ports.
++*/
++
++#define S3C2410_GPGCON S3C2410_GPIOREG(0x60)
++#define S3C2410_GPGDAT S3C2410_GPIOREG(0x64)
++#define S3C2410_GPGUP S3C2410_GPIOREG(0x68)
++
++#define S3C2410_GPG0_INP (0x00 << 0)
++#define S3C2410_GPG0_OUTP (0x01 << 0)
++#define S3C2410_GPG0_EINT8 (0x02 << 0)
++
++#define S3C2410_GPG1_INP (0x00 << 2)
++#define S3C2410_GPG1_OUTP (0x01 << 2)
++#define S3C2410_GPG1_EINT9 (0x02 << 2)
++
++#define S3C2410_GPG2_INP (0x00 << 4)
++#define S3C2410_GPG2_OUTP (0x01 << 4)
++#define S3C2410_GPG2_EINT10 (0x02 << 4)
++
++#define S3C2410_GPG3_INP (0x00 << 6)
++#define S3C2410_GPG3_OUTP (0x01 << 6)
++#define S3C2410_GPG3_EINT11 (0x02 << 6)
++
++#define S3C2410_GPG4_INP (0x00 << 8)
++#define S3C2410_GPG4_OUTP (0x01 << 8)
++#define S3C2410_GPG4_EINT12 (0x02 << 8)
++#define S3C2410_GPG4_LCDPWREN (0x03 << 8)
++
++#define S3C2410_GPG5_INP (0x00 << 10)
++#define S3C2410_GPG5_OUTP (0x01 << 10)
++#define S3C2410_GPG5_EINT13 (0x02 << 10)
++#define S3C2410_GPG5_SPIMISO1 (0x03 << 10)
++
++#define S3C2410_GPG6_INP (0x00 << 12)
++#define S3C2410_GPG6_OUTP (0x01 << 12)
++#define S3C2410_GPG6_EINT14 (0x02 << 12)
++#define S3C2410_GPG6_SPIMOSI1 (0x03 << 12)
++
++#define S3C2410_GPG7_INP (0x00 << 14)
++#define S3C2410_GPG7_OUTP (0x01 << 14)
++#define S3C2410_GPG7_EINT15 (0x02 << 14)
++#define S3C2410_GPG7_SPICLK1 (0x03 << 14)
++
++#define S3C2410_GPG8_INP (0x00 << 16)
++#define S3C2410_GPG8_OUTP (0x01 << 16)
++#define S3C2410_GPG8_EINT16 (0x02 << 16)
++
++#define S3C2410_GPG9_INP (0x00 << 18)
++#define S3C2410_GPG9_OUTP (0x01 << 18)
++#define S3C2410_GPG9_EINT17 (0x02 << 18)
++
++#define S3C2410_GPG10_INP (0x00 << 20)
++#define S3C2410_GPG10_OUTP (0x01 << 20)
++#define S3C2410_GPG10_EINT18 (0x02 << 20)
++
++#define S3C2410_GPG11_INP (0x00 << 22)
++#define S3C2410_GPG11_OUTP (0x01 << 22)
++#define S3C2410_GPG11_EINT19 (0x02 << 22)
++#define S3C2410_GPG11_TCLK1 (0x03 << 22)
++
++#define S3C2410_GPG12_INP (0x00 << 24)
++#define S3C2410_GPG12_OUTP (0x01 << 24)
++#define S3C2410_GPG12_EINT18 (0x02 << 24)
++#define S3C2410_GPG12_XMON (0x03 << 24)
++
++#define S3C2410_GPG13_INP (0x00 << 26)
++#define S3C2410_GPG13_OUTP (0x01 << 26)
++#define S3C2410_GPG13_EINT18 (0x02 << 26)
++#define S3C2410_GPG13_nXPON (0x03 << 26)
++
++#define S3C2410_GPG14_INP (0x00 << 28)
++#define S3C2410_GPG14_OUTP (0x01 << 28)
++#define S3C2410_GPG14_EINT18 (0x02 << 28)
++#define S3C2410_GPG14_YMON (0x03 << 28)
++
++#define S3C2410_GPG15_INP (0x00 << 30)
++#define S3C2410_GPG15_OUTP (0x01 << 30)
++#define S3C2410_GPG15_EINT18 (0x02 << 30)
++#define S3C2410_GPG15_nYPON (0x03 << 30)
++
++
++#define S3C2410_GPG_PUPDIS(x) (1<<(x))
++
++/* Port H consists of11 GPIO/serial/Misc pins
++ *
++ * GPGCON has 2 bits for each of the input pins on port F
++ * 00 = 0 input, 1 output, 2 interrupt (EINT0..7), 3 special func
++ *
++ * pull up works like all other ports.
++*/
++
++#define S3C2410_GPHCON S3C2410_GPIOREG(0x70)
++#define S3C2410_GPHDAT S3C2410_GPIOREG(0x74)
++#define S3C2410_GPHUP S3C2410_GPIOREG(0x78)
++
++#define S3C2410_GPH0_INP (0x00 << 0)
++#define S3C2410_GPH0_OUTP (0x01 << 0)
++#define S3C2410_GPH0_nCTS0 (0x02 << 0)
++
++#define S3C2410_GPH1_INP (0x00 << 2)
++#define S3C2410_GPH1_OUTP (0x01 << 2)
++#define S3C2410_GPH1_nRTS0 (0x02 << 2)
++
++#define S3C2410_GPH2_INP (0x00 << 4)
++#define S3C2410_GPH2_OUTP (0x01 << 4)
++#define S3C2410_GPH2_TXD0 (0x02 << 4)
++
++#define S3C2410_GPH3_INP (0x00 << 6)
++#define S3C2410_GPH3_OUTP (0x01 << 6)
++#define S3C2410_GPH3_RXD0 (0x02 << 6)
++
++#define S3C2410_GPH4_INP (0x00 << 8)
++#define S3C2410_GPH4_OUTP (0x01 << 8)
++#define S3C2410_GPH4_TXD1 (0x02 << 8)
++
++#define S3C2410_GPH5_INP (0x00 << 10)
++#define S3C2410_GPH5_OUTP (0x01 << 10)
++#define S3C2410_GPH5_RXD1 (0x02 << 10)
++
++#define S3C2410_GPH6_INP (0x00 << 12)
++#define S3C2410_GPH6_OUTP (0x01 << 12)
++#define S3C2410_GPH6_TXD2 (0x02 << 12)
++#define S3C2410_GPH6_nRTS1 (0x03 << 12)
++
++#define S3C2410_GPH7_INP (0x00 << 14)
++#define S3C2410_GPH7_OUTP (0x01 << 14)
++#define S3C2410_GPH7_RXD2 (0x02 << 14)
++#define S3C2410_GPH7_nCTS1 (0x03 << 14)
++
++#define S3C2410_GPH8_INP (0x00 << 16)
++#define S3C2410_GPH8_OUTP (0x01 << 16)
++#define S3C2410_GPH8_UCLK (0x02 << 16)
++
++#define S3C2410_GPH9_INP (0x00 << 18)
++#define S3C2410_GPH9_OUTP (0x01 << 18)
++#define S3C2410_GPH9_CLKOUT0 (0x02 << 18)
++
++#define S3C2410_GPH10_INP (0x00 << 20)
++#define S3C2410_GPH10_OUTP (0x01 << 20)
++#define S3C2410_GPH10_CLKOUT1 (0x02 << 20)
++
++/* miscellaneous control */
++
++#define S3C2410_MISCCR S3C2410_GPIOREG(0x80)
++#define S3C2410_DCLKCON S3C2410_GPIOREG(0x84)
++
++/* see clock.h for dclk definitions */
++
++/* pullup control on databus */
++#define S3C2410_MISCCR_SPUCR_HEN (0)
++#define S3C2410_MISCCR_SPUCR_HDIS (1<<0)
++#define S3C2410_MISCCR_SPUCR_LEN (0)
++#define S3C2410_MISCCR_SPUCR_LDIS (1<<1)
++
++#define S3C2410_MISCCR_USBDEV (0)
++#define S3C2410_MISCCR_USBHOST (1<<3)
++
++#define S3C2410_MISCCR_CLK0_MPLL (0<<4)
++#define S3C2410_MISCCR_CLK0_UPLL (1<<4)
++#define S3C2410_MISCCR_CLK0_FCLK (2<<4)
++#define S3C2410_MISCCR_CLK0_HCLK (3<<4)
++#define S3C2410_MISCCR_CLK0_PCLK (4<<4)
++#define S3C2410_MISCCR_CLK0_DCLK0 (5<<4)
++
++#define S3C2410_MISCCR_CLK1_MPLL (0<<8)
++#define S3C2410_MISCCR_CLK1_UPLL (1<<8)
++#define S3C2410_MISCCR_CLK1_FCLK (2<<8)
++#define S3C2410_MISCCR_CLK1_HCLK (3<<8)
++#define S3C2410_MISCCR_CLK1_PCLK (4<<8)
++#define S3C2410_MISCCR_CLK1_DCLK1 (5<<8)
++
++#define S3C2410_MISCCR_USBSUSPND0 (1<<12)
++#define S3C2410_MISCCR_USBSUSPND1 (1<<13)
++
++#define S3C2410_MISCCR_nRSTCON (1<<16)
++
++/* external interrupt control... */
++/* S3C2410_EXTINT0 -> irq sense control for EINT0..EINT7
++ * S3C2410_EXTINT1 -> irq sense control for EINT8..EINT15
++ * S3C2410_EXTINT2 -> irq sense control for EINT16..EINT23
++ *
++ * note S3C2410_EXTINT2 has filtering options for EINT16..EINT23
++ *
++ * Samsung datasheet p9-25
++*/
++
++#define S3C2410_EXTINT0 S3C2410_GPIOREG(0x88)
++#define S3C2410_EXTINT1 S3C2410_GPIOREG(0x8C)
++#define S3C2410_EXTINT2 S3C2410_GPIOREG(0x90)
++
++/* values for S3C2410_EXTINT0/1/2 */
++#define S3C2410_EXTINT_LOWLEV (0x00)
++#define S3C2410_EXTINT_HILEV (0x01)
++#define S3C2410_EXTINT_FALLEDGE (0x02)
++#define S3C2410_EXTINT_RISEEDGE (0x04)
++#define S3C2410_EXTINT_BOTHEDGE (0x06)
++
++/* interrupt filtering conrrol for EINT16..EINT23 */
++#define S3C2410_EINFLT0 S3C2410_GPIOREG(0x94)
++#define S3C2410_EINFLT1 S3C2410_GPIOREG(0x98)
++#define S3C2410_EINFLT2 S3C2410_GPIOREG(0x9C)
++#define S3C2410_EINFLT3 S3C2410_GPIOREG(0xA0)
++
++/* mask: 0=enable, 1=disable
++ * 1 bit EINT, 4=EINT4, 23=EINT23
++ * EINT0,1,2,3 are not handled here.
++*/
++#define S3C2410_EINTMASK S3C2410_GPIOREG(0xA4)
++#define S3C2410_EINTPEND S3C2410_GPIOREG(0xA8)
++
++/* GSTATUS have miscellaneous information in them
++ *
++ */
++
++#define S3C2410_GSTATUS0 S3C2410_GPIOREG(0x0AC)
++#define S3C2410_GSTATUS1 S3C2410_GPIOREG(0x0B0)
++#define S3C2410_GSTATUS2 S3C2410_GPIOREG(0x0B4)
++#define S3C2410_GSTATUS3 S3C2410_GPIOREG(0x0B8)
++#define S3C2410_GSTATUS4 S3C2410_GPIOREG(0x0BC)
++
++#define S3C2410_GSTATUS0_nWAIT (1<<3)
++#define S3C2410_GSTATUS0_NCON (1<<2)
++#define S3C2410_GSTATUS0_RnB (1<<1)
++#define S3C2410_GSTATUS0_nBATTFLT (1<<0)
++
++#define S3C2410_GSTATUS2_WTRESET (1<<2)
++#define S3C2410_GSTATUs2_OFFRESET (1<<1)
++#define S3C2410_GSTATUS2_PONRESET (1<<0)
++
++#endif /* ASMARM_ARCH_S3C2410_GPIO_H */
++
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-iis.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-iis.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-iis.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-iis.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,61 @@
++/* linux/include/asm/hardware/s3c2410/iis.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 IIS register definition
++ *
++ * Changelog:
++ * 19-06-2003 BJD Created file
++ * 26-06-2003 BJD Finished off definitions for register addresses
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_IIS_H
++#define ASMARM_ARCH_S3C2410_IIS_H
++
++#define S3C2410_IISCON (S3C2410_VA_IIS + 0x00)
++
++#define S3C2410_IISCON_LRINDEX (1<<8)
++#define S3C2410_IISCON_TXFIFORDY (1<<7)
++#define S3C2410_IISCON_RXFIFORDY (1<<6)
++#define S3C2410_IISCON_TXDMAEN (1<<5)
++#define S3C2410_IISCON_RXDMAEN (1<<4)
++#define S3C2410_IISCON_TXIDLE (1<<3)
++#define S3C2410_IISCON_RXIDLE (1<<2)
++#define S3C2410_IISCON_IISEN (1<<0)
++
++#define S3C2410_IISMOD (S3C2410_VA_IIS + 0x04)
++
++#define S3C2410_IISMOD_SLAVE (1<<8)
++#define S3C2410_IISMOD_NOXFER (0<<6)
++#define S3C2410_IISMOD_RXMODE (1<<6)
++#define S3C2410_IISMOD_TXMODE (2<<6)
++#define S3C2410_IISMOD_TXRXMODE (3<<6)
++#define S3C2410_IISMOD_LR_LLOW (0<<5)
++#define S3C2410_IISMOD_LR_RLOW (1<<5)
++#define S3C2410_IISMOD_IIS (0<<4)
++#define S3C2410_IISMOD_MSB (1<<4)
++#define S3C2410_IISMOD_8BIT (0<<3)
++#define S3C2410_IISMOD_16BIT (1<<3)
++#define S3C2410_IISMOD_256FS (0<<1)
++#define S3C2410_IISMOD_384FS (1<<1)
++#define S3C2410_IISMOD_16FS (0<<0)
++#define S3C2410_IISMOD_32FS (1<<0)
++#define S3C2410_IISMOD_48FS (2<<0)
++
++#define S3C2410_IISPSR (S3C2410_VA_IIS + 0x08)
++
++#define S3C2410_IISFCON (S3C2410_VA_IIS + 0x0c)
++
++#define S3C2410_IISFCON_TXDMA (1<<15)
++#define S3C2410_IISFCON_RXDMA (1<<14)
++#define S3C2410_IISFCON_TXENABLE (1<<13)
++#define S3C2410_IISFCON_RXENABLE (1<<12)
++
++#define S3C2410_IISFIFO (S3C2410_VA_IIS + 0x10)
++
++#endif /* ASMARM_ARCH_S3C2410_IIS_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-irq.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-irq.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-irq.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-irq.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,34 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-irq.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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:
++ * 19-06-2003 BJD Created file
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_IRQ_H
++#define ASMARM_ARCH_S3C2410_IRQ_H
++
++/* interrupt controller */
++
++#define S3C2410_IRQREG(x) ((x) + S3C2410_VA_IRQ)
++#define S3C2410_EINTREG(x) ((x) + S3C2410_VA_GPIO)
++
++#define S3C2410_SRCPND S3C2410_IRQREG(0x000)
++#define S3C2410_INTMOD S3C2410_IRQREG(0x004)
++#define S3C2410_INTMSK S3C2410_IRQREG(0x008)
++#define S3C2410_PRIORITY S3C2410_IRQREG(0x00C)
++#define S3C2410_INTPND S3C2410_IRQREG(0x010)
++#define S3C2410_INTOFFSET S3C2410_IRQREG(0x014)
++#define S3C2410_SUBSRCPND S3C2410_IRQREG(0x018)
++#define S3C2410_INTSUBMSK S3C2410_IRQREG(0x01C)
++
++#define S3C2410_EINTMASK S3C2410_EINTREG(0x0A4)
++#define S3C2410_EINTPEND S3C2410_EINTREG(0X0A8)
++
++#endif /* ASMARM_ARCH_S3C2410_IRQ_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-lcd.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-lcd.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-lcd.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-lcd.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,107 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-lcd.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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:
++ * 12-06-2003 BJD Created file
++ * 26-06-2003 BJD Updated LCDCON register definitions
++*/
++
++#ifndef ASMARM_ARCH_S3C2410_LCD_H
++#define ASMARM_ARCH_S3C2410_LCD_H
++
++#define S3C2410_LCDREG(x) ((x) + S3C2410_VA_LCD)
++
++/* LCD control registers */
++#define S3C2410_LCDCON1 S3C2410_LCDREG(0x00)
++#define S3C2410_LCDCON2 S3C2410_LCDREG(0x04)
++#define S3C2410_LCDCON3 S3C2410_LCDREG(0x08)
++#define S3C2410_LCDCON4 S3C2410_LCDREG(0x0C)
++#define S3C2410_LCDCON5 S3C2410_LCDREG(0x10)
++
++#define S3C2410_LCDCON1_CLKVAL(x) ((x) << 8)
++#define S3C2410_LCDCON1_MMODE (1<<7)
++#define S3C2410_LCDCON1_DSCAN4 (0<<5)
++#define S3C2410_LCDCON1_STN4 (1<<5)
++#define S3C2410_LCDCON1_STN8 (2<<5)
++#define S3C2410_LCDCON1_TFT (3<<5)
++
++#define S3C2410_LCDCON1_STN1BPP (0<<1)
++#define S3C2410_LCDCON1_STN2GREY (1<<1)
++#define S3C2410_LCDCON1_STN4GREY (2<<1)
++#define S3C2410_LCDCON1_STN8BPP (3<<1)
++#define S3C2410_LCDCON1_STN12BPP (4<<1)
++
++#define S3C2410_LCDCON1_TFT1BPP (8<<1)
++#define S3C2410_LCDCON1_TFT2BPP (9<<1)
++#define S3C2410_LCDCON1_TFT4BPP (10<<1)
++#define S3C2410_LCDCON1_TFT8BPP (11<<1)
++#define S3C2410_LCDCON1_TFT16BPP (12<<1)
++#define S3C2410_LCDCON1_TFT24BPP (13<<1)
++
++#define S3C2410_LCDCON1_ENVDI (1)
++
++#define S3C2410_LCDCON2_VBPD(x) ((x) << 24)
++#define S3C2410_LCDCON2_LINEVAL(x) ((x) << 14)
++#define S3C2410_LCDCON2_VFPD(x) ((x) << 6)
++#define S3C2410_LCDCON2_VSPW(x) ((x) << 0)
++
++#define S3C2410_LCDCON3_HBPD(x) ((x) << 19)
++#define S3C2410_LCDCON3_WDLY(x) ((x) << 19)
++#define S3C2410_LCDCON3_HOZVAL(x) ((x) << 8)
++#define S3C2410_LCDCON3_HFPD(x) ((x) << 0)
++#define S3C2410_LCDCON3_LINEBLANK(x)((x) << 0)
++
++#define S3C2410_LCDCON4_MVAL(x) ((x) << 8)
++#define S3C2410_LCDCON4_HSPW(x) ((x) << 0)
++#define S3C2410_LCDCON4_WLH(x) ((x) << 0)
++
++#define S3C2410_LCDCON5_BPP24BL (1<<12)
++#define S3C2410_LCDCON5_FRM565 (1<<11)
++#define S3C2410_LCDCON5_INVVCLK (1<<10)
++#define S3C2410_LCDCON5_INVVLINE (1<<9)
++#define S3C2410_LCDCON5_INVVFRAME (1<<8)
++#define S3C2410_LCDCON5_INVVD (1<<7)
++#define S3C2410_LCDCON5_INVVSYNC (1<<8)
++#define S3C2410_LCDCON5_INVHSYNC (1<<9)
++#define S3C2410_LCDCON5_INVVDEN (1<<6)
++#define S3C2410_LCDCON5_INVPWREN (1<<5)
++#define S3C2410_LCDCON5_INVLEND (1<<4)
++#define S3C2410_LCDCON5_PWREN (1<<3)
++#define S3C2410_LCDCON5_ENLEND (1<<2)
++#define S3C2410_LCDCON5_BSWP (1<<1)
++#define S3C2410_LCDCON5_HWSWP (1<<0)
++
++/* framebuffer start addressed */
++#define S3C2410_LCDSADDR1 S3C2410_LCDREG(0x14)
++#define S3C2410_LCDSADDR2 S3C2410_LCDREG(0x18)
++#define S3C2410_LCDSADDR3 S3C2410_LCDREG(0x1C)
++
++/* colour lookup and miscellaneous controls */
++
++#define S3C2410_REDLUT S3C2410_LCDREG(0x20)
++#define S3C2410_GREENLUT S3C2410_LCDREG(0x24)
++#define S3C2410_BLUELUT S3C2410_LCDREG(0x28)
++
++#define S3C2410_DITHMODE S3C2410_LCDREG(0x4C)
++#define S3C2410_TPAL S3C2410_LCDREG(0x50)
++
++/* interrupt info */
++#define S3C2410_LCDINTPND S3C2410_LCDREG(0x54)
++#define S3C2410_LCDSRCPND S3C2410_LCDREG(0x58)
++#define S3C2410_LCDINTMSK S3C2410_LCDREG(0x5C)
++#define S3C2410_LPCSEL S3C2410_LCDREG(0x60)
++
++#define S3C2410_TFTPAL(x) S3C2410_LCDREG((0x400 + (x)*4))
++
++#endif /* ASMARM_ARCH_S3C2410_LCD_H */
++
++
++
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-rtc.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-rtc.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-rtc.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-rtc.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,61 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-rtc.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 Timer configuration
++ *
++ * Changelog:
++ * 05-06-2003 BJD Created file
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_RTC_H
++#define ASMARM_ARCH_S3C2410_RTC_H
++
++#define S3C2410_RTCREG(x) ((x) + S3C2410_VA_RTC)
++
++#define S3C2410_RTCCON S3C2410_RTCREG(0x40)
++#define S3C2410_RTCCON_RTCEN (1<<0)
++#define S3C2410_RTCCON_CLKRST (1<<3)
++
++#define S3C2410_TICNT S3C2410_RTCREG(0x44)
++#define S3C2410_TICNT_ENABLE (1<<7)
++
++#define S3C2410_RTCALM S3C2410_RTCREG(0x50)
++#define S3C2410_RTCALM_ALMEN (1<<6)
++#define S3C2410_RTCALM_YEAREN (1<<5)
++#define S3C2410_RTCALM_MONEN (1<<4)
++#define S3C2410_RTCALM_DAYEN (1<<3)
++#define S3C2410_RTCALM_HOUREN (1<<2)
++#define S3C2410_RTCALM_MINEN (1<<1)
++#define S3C2410_RTCALM_SECEN (1<<0)
++
++#define S3C2410_RTCALM_ALL \
++ S3C2410_RTCALM_ALMEN | S3C2410_RTCALM_YEAREN | S3C2410_RTCALM_MONEN |\
++ S3C2410_RTCALM_DAYEN | S3C2410_RTCALM_HOUREN | S3C2410_RTCALM_MINEN |\
++ S3C2410_RTCALM_SECEN
++
++
++#define S3C2410_ALMSEC S3C2410_RTCREG(0x54)
++#define S3C2410_ALMMIN S3C2410_RTCREG(0x58)
++#define S3C2410_ALMHOUR S3C2410_RTCREG(0x5c)
++
++#define S3C2410_ALMDATE S3C2410_RTCREG(0x60)
++#define S3C2410_ALMMON S3C2410_RTCREG(0x64)
++#define S3C2410_ALMYEAR S3C2410_RTCREG(0x68)
++
++#define S3C2410_RTCRST S3C2410_RTCREG(0x6c)
++
++#define S3C2410_RTCSEC S3C2410_RTCREG(0x70)
++#define S3C2410_RTCMIN S3C2410_RTCREG(0x74)
++#define S3C2410_RTCHOUR S3C2410_RTCREG(0x78)
++#define S3C2410_RTCDATE S3C2410_RTCREG(0x7c)
++#define S3C2410_RTCDAY S3C2410_RTCREG(0x80)
++#define S3C2410_RTCMON S3C2410_RTCREG(0x84)
++#define S3C2410_RTCYEAR S3C2410_RTCREG(0x88)
++
++#endif /* ASMARM_ARCH_S3C2410_RTC_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-serial.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-serial.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-serial.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-serial.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,101 @@
++/*
++ * linux/include/asm-arm/arch-s3c2410/S3C2410-serial.h
++ *
++ * Internal header file for Samsung S3C2410 serial ports (UART0-2)
++ *
++ * Copyright (C) 2002 Shane Nay (shane at minirl.com)
++ *
++ * Additional defines, (c) 2003 Simtec Electronics (linux at simtec.co.uk)
++ *
++ * Adapted from:
++ *
++ * Internal header file for MX1ADS serial ports (UART1 & 2)
++ *
++ * Copyright (C) 2002 Shane Nay (shane at minirl.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
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_SERIAL_H
++#define ASMARM_ARCH_S3C2410_SERIAL_H
++
++#define S3C2410_UARTRXH0_OFF (0x24)
++#define S3C2410_UARTTXH0_OFF (0x20)
++#define S3C2410_UARTLCON_OFF (0x00)
++#define S3C2410_UARTCON_OFF (0x04)
++#define S3C2410_UARTFCON_OFF (0x08)
++#define S3C2410_UARTMCON_OFF (0x0C)
++#define S3C2410_UARTBRDIV_OFF (0x28)
++#define S3C2410_UARTTRSTAT_OFF (0x10)
++#define S3C2410_UARTERSTAT_OFF (0x14)
++#define S3C2410_UARTFSTAT_OFF (0x18)
++#define S3C2410_UARTMSTAT_OFF (0x1C)
++
++
++#define S3C2410_UART1_OFF (0x4000)
++#define S3C2410_UART2_OFF (0x8000)
++
++#define S3C2410_LCON_CFGMASK ((0xF<<3)|(0x3))
++
++#define S3C2410_LCON_CS5 (0x0)
++#define S3C2410_LCON_CS6 (0x1)
++#define S3C2410_LCON_CS7 (0x2)
++#define S3C2410_LCON_CS8 (0x3)
++
++#define S3C2410_LCON_1STOP (0x0)
++#define S3C2410_LCON_2STOP (0x04)
++
++#define S3C2410_LCON_PNONE (0x0)
++#define S3C2410_LCON_PEVEN ((0x5)<<3)
++#define S3C2410_LCON_PODD ((0x4)<<3)
++
++#define S3C2410_UMCON_AFC (0x10)
++#define S3C2410_UMCON_RTS (0x1)
++
++#define S3C2410_UMSTAT_CTS (0x1)
++
++#define S3C2410_UCON_SBREAK (1<<4)
++
++#define S3C2410_UCON_TXILEVEL (1<<9)
++#define S3C2410_UCON_RXILEVEL (1<<8)
++#define S3C2410_UCON_TXIRQMODE (1<<2)
++#define S3C2410_UCON_RXIRQMODE (1<<0)
++#define S3C2410_UCON_RXFIFO_TOI (1<<7)
++
++#define S3C2410_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | S3C2410_UCON_RXILEVEL \
++ | S3C2410_UCON_TXIRQMODE | S3C2410_UCON_RXIRQMODE \
++ | S3C2410_UCON_RXFIFO_TOI)
++
++#define S3C2410_UFCON_FIFOMODE (1<<0)
++#define S3C2410_UFCON_TXTRIG0 (0<<6)
++#define S3C2410_UFCON_RXTRIG8 (1<<4)
++#define S3C2410_UFCON_RXTRIG12 (2<<4)
++
++#define S3C2410_UFCON_RESETBOTH (3<<1)
++
++#define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | S3C2410_UFCON_TXTRIG0 \
++ | S3C2410_UFCON_RXTRIG8 )
++
++#define S3C2410_UFSTAT_TXFULL (1<<9)
++#define S3C2410_UFSTAT_RXFULL (1<<8)
++#define S3C2410_UFSTAT_TXMASK (15<<4)
++#define S3C2410_UFSTAT_TXSHIFT (4)
++#define S3C2410_UFSTAT_RXMASK (15<<0)
++#define S3C2410_UFSTAT_RXSHIFT (0)
++
++#define S3C2410_UTRSTAT_TXFE (1<<1)
++#define S3C2410_UTRSTAT_RXDR (1<<0)
++
++#endif /* ASMARM_ARCH_S3C2410_SERIAL_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-timer.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-timer.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-timer.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-timer.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,81 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-timer.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 Timer configuration
++ *
++ * Changelog:
++ * 05-06-2003 BJD Created file
++ * 26-06-2003 BJD Added more timer definitions to mux / control
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_TIMER_H
++#define ASMARM_ARCH_S3C2410_TIMER_H
++
++#define S3C2410_TIMERREG(x) (S3C2410_VA_TIMER + (x))
++#define S3C2410_TIMERREG2(tmr,reg) S3C2410_TIMERREG((reg)+0x0c+((tmr)*0x0c))
++
++#define S3C2410_TCFG0 S3C2410_TIMERREG(0x00)
++#define S3C2410_TCFG1 S3C2410_TIMERREG(0x04)
++#define S3C2410_TCON S3C2410_TIMERREG(0x08)
++
++
++#define S3C2410_TCFG1_MUX4_TCLK1 (4<<16)
++#define S3C2410_TCFG1_MUX4_MASK (15<<16)
++
++#define S3C2410_TCFG1_MUX3_TCLK1 (4<<12)
++#define S3C2410_TCFG1_MUX3_MASK (15<<12)
++
++#define S3C2410_TCFG1_MUX2_TCLK1 (4<<8)
++#define S3C2410_TCFG1_MUX2_MASK (15<<8)
++
++#define S3C2410_TCFG1_MUX1_TCLK0 (4<<4)
++#define S3C2410_TCFG1_MUX1_MASK (15<<4)
++
++#define S3C2410_TCFG1_MUX0_TCLK0 (4<<0)
++#define S3C2410_TCFG1_MUX0_MASK (15<<0)
++
++/* for each timer, we have an count buffer, an compare buffer and an
++ * observation buffer
++ */
++
++/* WARNING - timer 4 has no buffer reg, and it's observation is at +4 */
++
++#define S3C2410_TCNTB(tmr) S3C2410_TIMERREG2(tmr, 0x00)
++#define S3C2410_TCMPB(tmr) S3C2410_TIMERREG2(tmr, 0x04)
++#define S3C2410_TCNTO(tmr) S3C2410_TIMERREG2(tmr, (((tmr) == 4) ? 0x04 : 0x08))
++
++#define S3C2410_TCON_T4RELOAD (1<<22)
++#define S3C2410_TCON_T4MANUALUPD (1<<21)
++#define S3C2410_TCON_T4START (1<<20)
++
++#define S3C2410_TCON_T3RELOAD (1<<19)
++#define S3C2410_TCON_T3INVERT (1<<18)
++#define S3C2410_TCON_T3MANUALUPD (1<<17)
++#define S3C2410_TCON_T3START (1<<16)
++
++#define S3C2410_TCON_T2RELOAD (1<<15)
++#define S3C2410_TCON_T2INVERT (1<<14)
++#define S3C2410_TCON_T2MANUALUPD (1<<13)
++#define S3C2410_TCON_T2START (1<<12)
++
++#define S3C2410_TCON_T1RELOAD (1<<11)
++#define S3C2410_TCON_T1INVERT (1<<10)
++#define S3C2410_TCON_T1MANUALUPD (1<<9)
++#define S3C2410_TCON_T1START (1<<8)
++
++#define S3C2410_TCON_T0DEADZONE (1<<4)
++#define S3C2410_TCON_T0RELOAD (1<<3)
++#define S3C2410_TCON_T0INVERT (1<<2)
++#define S3C2410_TCON_T0MANUALUPD (1<<1)
++#define S3C2410_TCON_T0START (1<<0)
++
++#endif /* ASMARM_ARCH_S3C2410_TIMER_H */
++
++
++
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-watchdog.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-watchdog.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/S3C2410-watchdog.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/S3C2410-watchdog.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,40 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-watchdog.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 Watchdog timer control
++ *
++ * Changelog:
++ * 21-06-2003 BJD Created file
++*/
++
++#ifndef ASMARM_ARCH_S3C2410_WATCHDOG_H
++#define ASMARM_ARCH_S3C2410_WATCHDOG_H
++
++#define S3C2410_WDOGREG(x) ((x) + S3C2410_VA_WATCHDOG)
++
++#define S3C2410_WTCON S3C2410_WDOGREG(0x00)
++#define S3C2410_WTDAT S3C2410_WDOGREG(0x04)
++#define S3C2410_WTCNT S3C2410_WDOGREG(0x08)
++
++/* the watchdog can either generate a reset pulse, or an interrupt. */
++
++#define S3C2410_WTCON_RSTEN (0x01)
++#define S3C2410_WTCON_INTEN (1<<2)
++#define S3C2410_WTCON_ENABLE (1<<5)
++
++#define S3C2410_WTCON_DIV16 (0<<3)
++#define S3C2410_WTCON_DIV32 (1<<3)
++#define S3C2410_WTCON_DIV64 (2<<3)
++#define S3C2410_WTCON_DIV128 (3<<3)
++
++#define S3C2410_WTCON_PRESCALE(x) ((x) << 8)
++
++#endif /* ASMARM_ARCH_S3C2410_WATCHDOG_H */
++
++
+diff -urN kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/nand.h kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/nand.h
+--- kernel-source-2.4.27-8/include/asm-arm/arch-s3c2410/nand.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/arch-s3c2410/nand.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,40 @@
++/* core/system/cpu/s3c2410x/nand.h
++ *
++ * (c) 2003 Simtec Electronics
++ *
++ * Samsung S3C2410X nand-flash controller definitions
++ *
++ * Ben Dooks
++ */
++
++#ifndef __INCLUDE_ASM_ARCHS3C2410_NAND_H
++#define __INCLUDE_ASM_ARCHS3C2410_NAND_H "$Id: nand.h,v 1.3 2003/12/09 11:36:29 ben Exp $"
++
++
++#define S3C2410_NFREG(x) (x)
++
++#define S3C2410_NFCONF S3C2410_NFREG(0x00)
++
++#define S3C2410_NFCONF_EN (1<<15)
++#define S3C2410_NFCONF_512BYTE (1<<14)
++#define S3C2410_NFCONF_4STEP (1<<13)
++#define S3C2410_NFCONF_INITECC (1<<12)
++#define S3C2410_NFCONF_nFCE (1<<11)
++#define S3C2410_NFCONF_TACLS(x) ((x)<<8)
++#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)
++#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)
++
++/* CMD, ADDR and DATA are all byte */
++
++#define S3C2410_NFCMD S3C2410_NFREG(0x04)
++#define S3C2410_NFADDR S3C2410_NFREG(0x08)
++#define S3C2410_NFDATA S3C2410_NFREG(0x0C)
++#define S3C2410_NFSTAT S3C2410_NFREG(0x10)
++
++#define S3C2410_NFSTAT_BUSY (1<<0)
++
++/* think ECC can only be 8bit read? */
++#define S3C2410_NFECC S3C2410_NFREG(0x14)
++
++#endif /* __CORE_SYSTEM_S3C2410_NAND_H */
++
+diff -urN kernel-source-2.4.27-8/include/asm-arm/bugs.h kernel-source-2.4.27-8-arm-1/include/asm-arm/bugs.h
+--- kernel-source-2.4.27-8/include/asm-arm/bugs.h 2000-09-18 23:15:23.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/bugs.h 2005-02-18 17:48:44.000000000 +0000
+@@ -10,8 +10,17 @@
+ #ifndef __ASM_BUGS_H
+ #define __ASM_BUGS_H
+
++#include <linux/config.h>
+ #include <asm/proc-fns.h>
+
+-#define check_bugs() cpu_check_bugs()
++extern void check_writebuffer_bugs(void);
++
++static inline void check_bugs(void)
++{
++#ifdef CONFIG_CPU_32
++ check_writebuffer_bugs();
++#endif
++ cpu_check_bugs();
++}
+
+ #endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/constants.h kernel-source-2.4.27-8-arm-1/include/asm-arm/constants.h
+--- kernel-source-2.4.27-8/include/asm-arm/constants.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/constants.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,26 @@
++/*
++ * This file is automatically generated from arch/arm/tools/getconstants.c.
++ * Do not edit! Only include this file in assembly (.S) files!
++ */
++
++#define TSK_SIGPENDING 8
++#define TSK_ADDR_LIMIT 12
++#define TSK_NEED_RESCHED 20
++#define TSK_PTRACE 24
++#define TSK_USED_MATH 576
++#define TSS_SAVE 796
++#define TSS_FPESAVE 636
++#define TSS_DOMAIN 800
++#define HPTE_TYPE_SMALL 2
++#define HPTE_AP_READ 2720
++#define HPTE_AP_WRITE 1360
++#define LPTE_PRESENT 1
++#define LPTE_YOUNG 2
++#define LPTE_BUFFERABLE 4
++#define LPTE_CACHEABLE 8
++#define LPTE_USER 16
++#define LPTE_WRITE 32
++#define LPTE_EXEC 64
++#define LPTE_DIRTY 128
++#define PAGE_SZ 4096
++#define SYS_ERROR0 10420224
+diff -urN kernel-source-2.4.27-8/include/asm-arm/debug.h kernel-source-2.4.27-8-arm-1/include/asm-arm/debug.h
+--- kernel-source-2.4.27-8/include/asm-arm/debug.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/debug.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,19 @@
++/* linux/include/asm-arm/debug.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux at simtec.co.uk>
++ * http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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:
++ * 26-06-2003 BJD Created file
++ */
++
++
++#ifdef CONFIG_DEBUG_LL
++extern void llprintk(const char *msg, ...);
++#else
++#define llprintk(x...)
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/div64.h kernel-source-2.4.27-8-arm-1/include/asm-arm/div64.h
+--- kernel-source-2.4.27-8/include/asm-arm/div64.h 2000-01-13 21:30:31.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/div64.h 2005-02-18 17:48:44.000000000 +0000
+@@ -4,9 +4,13 @@
+ /* We're not 64-bit, but... */
+ #define do_div(n,base) \
+ ({ \
+- int __res; \
+- __res = ((unsigned long)n) % (unsigned int)base; \
+- n = ((unsigned long)n) / (unsigned int)base; \
++ register int __res asm("r2") = base; \
++ register unsigned long long __n asm("r0") = n; \
++ asm("bl do_div64" \
++ : "=r" (__n), "=r" (__res) \
++ : "0" (__n), "1" (__res) \
++ : "r3", "ip", "lr", "cc"); \
++ n = __n; \
+ __res; \
+ })
+
+diff -urN kernel-source-2.4.27-8/include/asm-arm/hardware/serial_s3c2410.h kernel-source-2.4.27-8-arm-1/include/asm-arm/hardware/serial_s3c2410.h
+--- kernel-source-2.4.27-8/include/asm-arm/hardware/serial_s3c2410.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/hardware/serial_s3c2410.h 2005-02-18 17:48:44.000000000 +0000
+@@ -0,0 +1,100 @@
++/*
++ * linux/include/asm-arm/hardware/serial_s3c2410.h
++ *
++ * Internal header file for Samsung S3C2410 serial ports (UART0-2)
++ *
++ * Copyright (C) 2002 Shane Nay (shane at minirl.com)
++ *
++ * Additional defines, (c) 2003 Simtec Electronics (linux at simtec.co.uk)
++ *
++ * Adapted from:
++ *
++ * Internal header file for MX1ADS serial ports (UART1 & 2)
++ *
++ * Copyright (C) 2002 Shane Nay (shane at minirl.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
++ */
++#ifndef ASM_ARM_HARDWARE_SERIAL_S3C2410_H
++#define ASM_ARM_HARDWARE_SERIAL_S3C2410_H
++
++#define S3C2410_UARTRXH0_OFF (0x24)
++#define S3C2410_UARTTXH0_OFF (0x20)
++#define S3C2410_UARTLCON_OFF (0x00)
++#define S3C2410_UARTCON_OFF (0x04)
++#define S3C2410_UARTFCON_OFF (0x08)
++#define S3C2410_UARTMCON_OFF (0x0C)
++#define S3C2410_UARTBRDIV_OFF (0x28)
++#define S3C2410_UARTTRSTAT_OFF (0x10)
++#define S3C2410_UARTERSTAT_OFF (0x14)
++#define S3C2410_UARTFSTAT_OFF (0x18)
++#define S3C2410_UARTMSTAT_OFF (0x1C)
++
++
++#define S3C2410_UART1_OFF (0x4000)
++#define S3C2410_UART2_OFF (0x8000)
++
++#define S3C2410_LCON_CFGMASK ((0xF<<3)|(0x3))
++
++#define S3C2410_LCON_CS5 (0x0)
++#define S3C2410_LCON_CS6 (0x1)
++#define S3C2410_LCON_CS7 (0x2)
++#define S3C2410_LCON_CS8 (0x3)
++
++#define S3C2410_LCON_1STOP (0x0)
++#define S3C2410_LCON_2STOP (0x04)
++
++#define S3C2410_LCON_PNONE (0x0)
++#define S3C2410_LCON_PEVEN ((0x5)<<3)
++#define S3C2410_LCON_PODD ((0x4)<<3)
++
++#define S3C2410_UMCON_AFC (0x10)
++#define S3C2410_UMCON_RTS (0x1)
++
++#define S3C2410_UMSTAT_CTS (0x1)
++
++#define S3C2410_UCON_SBREAK (1<<4)
++
++#define S3C2410_UCON_TXILEVEL (1<<9)
++#define S3C2410_UCON_RXILEVEL (1<<8)
++#define S3C2410_UCON_TXIRQMODE (1<<2)
++#define S3C2410_UCON_RXIRQMODE (1<<0)
++#define S3C2410_UCON_RXFIFO_TOI (1<<7)
++
++#define S3C2410_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | S3C2410_UCON_RXILEVEL \
++ | S3C2410_UCON_TXIRQMODE | S3C2410_UCON_RXIRQMODE \
++ | S3C2410_UCON_RXFIFO_TOI)
++
++#define S3C2410_UFCON_FIFOMODE (1<<0)
++#define S3C2410_UFCON_TXTRIG0 (0<<6)
++#define S3C2410_UFCON_RXTRIG8 (1<<4)
++#define S3C2410_UFCON_RXTRIG12 (2<<4)
++
++#define S3C2410_UFCON_RESETBOTH (3<<1)
++
++#define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | S3C2410_UFCON_TXTRIG0 \
++ | S3C2410_UFCON_RXTRIG8 )
++
++#define S3C2410_UFSTAT_TXFULL (1<<9)
++#define S3C2410_UFSTAT_RXFULL (1<<8)
++#define S3C2410_UFSTAT_TXMASK (15<<4)
++#define S3C2410_UFSTAT_TXSHIFT (4)
++#define S3C2410_UFSTAT_RXMASK (15<<0)
++#define S3C2410_UFSTAT_RXSHIFT (0)
++
++#define S3C2410_UTRSTAT_TXFE (1<<1)
++#define S3C2410_UTRSTAT_RXDR (1<<0)
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/asm-arm/hc_sl811-hw.h kernel-source-2.4.27-8-arm-1/include/asm-arm/hc_sl811-hw.h
+--- kernel-source-2.4.27-8/include/asm-arm/hc_sl811-hw.h 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/hc_sl811-hw.h 2005-02-18 17:48:44.000000000 +0000
+@@ -38,7 +38,7 @@
+ */
+ static void inline sl811_write_index (hcipriv_t *hp, __u8 index)
+ {
+- writeb (offset, hp->hcport);
++ writeb (index, hp->hcport);
+ wmb ();
+ }
+
+@@ -55,7 +55,7 @@
+ */
+ static void inline sl811_write_index_data (hcipriv_t *hp, __u8 index, __u8 data)
+ {
+- writeb (offset, hp->hcport);
++ writeb (index, hp->hcport);
+ writeb (data, hp->hcport2);
+ wmb ();
+ }
+diff -urN kernel-source-2.4.27-8/include/asm-arm/mach/dma.h kernel-source-2.4.27-8-arm-1/include/asm-arm/mach/dma.h
+--- kernel-source-2.4.27-8/include/asm-arm/mach/dma.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/mach/dma.h 2005-02-18 17:48:44.000000000 +0000
+@@ -41,6 +41,7 @@
+ unsigned int dma_base; /* Controller base address */
+ int dma_irq; /* Controller IRQ */
+ struct scatterlist cur_sg; /* Current controller buffer */
++ unsigned int state; /* RiscPC DMA status */
+
+ struct dma_ops *d_ops;
+ };
+diff -urN kernel-source-2.4.27-8/include/asm-arm/mach/serial_at91rm9200.h kernel-source-2.4.27-8-arm-1/include/asm-arm/mach/serial_at91rm9200.h
+--- kernel-source-2.4.27-8/include/asm-arm/mach/serial_at91rm9200.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/mach/serial_at91rm9200.h 2005-02-18 17:48:44.000000000 +0000
+@@ -10,7 +10,6 @@
+ #include <linux/config.h>
+
+ struct uart_port;
+-struct uart_info;
+
+ /*
+ * This is a temporary structure for registering these
+@@ -22,11 +21,11 @@
+ void (*enable_ms)(struct uart_port *);
+ void (*pm)(struct uart_port *, u_int, u_int);
+ int (*set_wake)(struct uart_port *, u_int);
+- int (*open)(struct uart_port *, struct uart_info *);
+- void (*close)(struct uart_port *, struct uart_info *);
++ int (*open)(struct uart_port *);
++ void (*close)(struct uart_port *);
+ };
+
+-#if defined(CONFIG_SERIAL_AT91RM9200)
++#if defined(CONFIG_SERIAL_AT91)
+ void at91rm9200_register_uart_fns(struct at91rm9200_port_fns *fns);
+ void at91rm9200_register_uart(int idx, int port);
+ #else
+diff -urN kernel-source-2.4.27-8/include/asm-arm/pci.h kernel-source-2.4.27-8-arm-1/include/asm-arm/pci.h
+--- kernel-source-2.4.27-8/include/asm-arm/pci.h 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/pci.h 2005-02-18 17:48:44.000000000 +0000
+@@ -113,6 +113,28 @@
+ /* nothing to do */
+ }
+
++/*
++ * pci_{map,unmap}_page maps a kernel page to a dma_addr_t. identical
++ * to pci_map_single, but takes a struct page instead of a virtual address
++ */
++static inline dma_addr_t pci_map_page(struct pci_dev *hwdev, struct page *page,
++ unsigned long offset, size_t size,
++ int direction)
++{
++ void *start;
++ BUG_ON(direction == PCI_DMA_NONE);
++ start = page_address(page) + offset;
++ consistent_sync(start, size, direction);
++ return virt_to_bus(start);
++}
++
++static inline void pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address,
++ size_t size, int direction)
++{
++ BUG_ON(direction == PCI_DMA_NONE);
++ /* Nothing to do */
++}
++
+ /* Whether pci_unmap_{single,page} is a nop depends upon the
+ * configuration.
+ */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/proc-armv/processor.h kernel-source-2.4.27-8-arm-1/include/asm-arm/proc-armv/processor.h
+--- kernel-source-2.4.27-8/include/asm-arm/proc-armv/processor.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/proc-armv/processor.h 2005-02-18 17:48:44.000000000 +0000
+@@ -54,7 +54,9 @@
+ regs->ARM_cpsr = USR_MODE; \
+ else \
+ regs->ARM_cpsr = USR26_MODE; \
+- regs->ARM_pc = pc; /* pc */ \
++ if (elf_hwcap & HWCAP_THUMB && pc & 1) \
++ regs->ARM_cpsr |= PSR_T_BIT; \
++ regs->ARM_pc = pc & ~1; /* pc */ \
+ regs->ARM_sp = sp; /* sp */ \
+ regs->ARM_r2 = stack[2]; /* r2 (envp) */ \
+ regs->ARM_r1 = stack[1]; /* r1 (argv) */ \
+diff -urN kernel-source-2.4.27-8/include/asm-arm/proc-armv/ptrace.h kernel-source-2.4.27-8-arm-1/include/asm-arm/proc-armv/ptrace.h
+--- kernel-source-2.4.27-8/include/asm-arm/proc-armv/ptrace.h 2000-11-28 01:07:59.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/proc-armv/ptrace.h 2005-02-18 17:48:44.000000000 +0000
+@@ -33,6 +33,16 @@
+ #define CC_N_BIT (1 << 31)
+ #define PCMASK 0
+
++/* 2.5 versions */
++#define PSR_T_BIT 0x00000020
++#define PSR_F_BIT 0x00000040
++#define PSR_I_BIT 0x00000080
++#define PSR_J_BIT 0x01000000
++#define PSR_V_BIT 0x10000000
++#define PSR_C_BIT 0x20000000
++#define PSR_Z_BIT 0x40000000
++#define PSR_N_BIT 0x80000000
++
+ #ifndef __ASSEMBLY__
+
+ /* this struct defines the way the registers are stored on the
+diff -urN kernel-source-2.4.27-8/include/asm-arm/proc-armv/uaccess.h kernel-source-2.4.27-8-arm-1/include/asm-arm/proc-armv/uaccess.h
+--- kernel-source-2.4.27-8/include/asm-arm/proc-armv/uaccess.h 2001-10-25 21:53:55.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/proc-armv/uaccess.h 2005-02-18 17:48:44.000000000 +0000
+@@ -12,7 +12,7 @@
+ * Note that this is actually 0x1,0000,0000
+ */
+ #define KERNEL_DS 0x00000000
+-#define USER_DS PAGE_OFFSET
++#define USER_DS TASK_SIZE
+
+ static inline void set_fs (mm_segment_t fs)
+ {
+@@ -38,7 +38,7 @@
+ : "cc"); \
+ (flag == 0); })
+
+-#define __put_user_asm_byte(x,addr,err) \
++#define __put_user_asm_byte(x,__pu_addr,err) \
+ __asm__ __volatile__( \
+ "1: strbt %1,[%2],#0\n" \
+ "2:\n" \
+@@ -51,18 +51,18 @@
+ " .align 3\n" \
+ " .long 1b, 3b\n" \
+ " .previous" \
+- : "=r" (err) \
+- : "r" (x), "r" (addr), "i" (-EFAULT), "0" (err))
++ : "+r" (err) \
++ : "r" (x), "r" (__pu_addr), "i" (-EFAULT) \
++ : "cc")
+
+-#define __put_user_asm_half(x,addr,err) \
++#define __put_user_asm_half(x,__pu_addr,err) \
+ ({ \
+ unsigned long __temp = (unsigned long)(x); \
+- unsigned long __ptr = (unsigned long)(addr); \
+- __put_user_asm_byte(__temp, __ptr, err); \
+- __put_user_asm_byte(__temp >> 8, __ptr + 1, err); \
++ __put_user_asm_byte(__temp, __pu_addr, err); \
++ __put_user_asm_byte(__temp >> 8, __pu_addr + 1, err); \
+ })
+
+-#define __put_user_asm_word(x,addr,err) \
++#define __put_user_asm_word(x,__pu_addr,err) \
+ __asm__ __volatile__( \
+ "1: strt %1,[%2],#0\n" \
+ "2:\n" \
+@@ -75,8 +75,28 @@
+ " .align 3\n" \
+ " .long 1b, 3b\n" \
+ " .previous" \
+- : "=r" (err) \
+- : "r" (x), "r" (addr), "i" (-EFAULT), "0" (err))
++ : "+r" (err) \
++ : "r" (x), "r" (__pu_addr), "i" (-EFAULT) \
++ : "cc")
++
++#define __put_user_asm_dword(x,__pu_addr,err) \
++ __asm__ __volatile__( \
++ "1: strt %R2, [%1], #4\n" \
++ "2: strt %Q2, [%1], #0\n" \
++ "3:\n" \
++ " .section .fixup,\"ax\"\n" \
++ " .align 2\n" \
++ "4: mov %0, %3\n" \
++ " b 3b\n" \
++ " .previous\n" \
++ " .section __ex_table,\"a\"\n" \
++ " .align 3\n" \
++ " .long 1b, 4b\n" \
++ " .long 2b, 4b\n" \
++ " .previous" \
++ : "+r" (err), "+r" (__pu_addr) \
++ : "r" (x), "i" (-EFAULT) \
++ : "cc")
+
+ #define __get_user_asm_byte(x,addr,err) \
+ __asm__ __volatile__( \
+@@ -92,14 +112,15 @@
+ " .align 3\n" \
+ " .long 1b, 3b\n" \
+ " .previous" \
+- : "=r" (err), "=&r" (x) \
+- : "r" (addr), "i" (-EFAULT), "0" (err))
++ : "+r" (err), "=&r" (x) \
++ : "r" (addr), "i" (-EFAULT) \
++ : "cc")
+
+-#define __get_user_asm_half(x,addr,err) \
++#define __get_user_asm_half(x,__gu_addr,err) \
+ ({ \
+- unsigned long __b1, __b2, __ptr = (unsigned long)addr; \
+- __get_user_asm_byte(__b1, __ptr, err); \
+- __get_user_asm_byte(__b2, __ptr + 1, err); \
++ unsigned long __b1, __b2; \
++ __get_user_asm_byte(__b1, __gu_addr, err); \
++ __get_user_asm_byte(__b2, __gu_addr + 1, err); \
+ (x) = __b1 | (__b2 << 8); \
+ })
+
+@@ -118,8 +139,9 @@
+ " .align 3\n" \
+ " .long 1b, 3b\n" \
+ " .previous" \
+- : "=r" (err), "=&r" (x) \
+- : "r" (addr), "i" (-EFAULT), "0" (err))
++ : "+r" (err), "=&r" (x) \
++ : "r" (addr), "i" (-EFAULT) \
++ : "cc")
+
+ extern unsigned long __arch_copy_from_user(void *to, const void *from, unsigned long n);
+ #define __do_copy_from_user(to,from,n) \
+diff -urN kernel-source-2.4.27-8/include/asm-arm/proc-fns.h kernel-source-2.4.27-8-arm-1/include/asm-arm/proc-fns.h
+--- kernel-source-2.4.27-8/include/asm-arm/proc-fns.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/proc-fns.h 2005-02-18 17:48:44.000000000 +0000
+@@ -76,6 +76,30 @@
+ # define CPU_NAME arm926
+ # endif
+ # endif
++# ifdef CONFIG_CPU_ARM1020
++# ifdef CPU_NAME
++# undef MULTI_CPU
++# define MULTI_CPU
++# else
++# define CPU_NAME arm1020
++# endif
++# endif
++# ifdef CONFIG_CPU_ARM1020E
++# ifdef CPU_NAME
++# undef MULTI_CPU
++# define MULTI_CPU
++# else
++# define CPU_NAME arm1020E
++# endif
++# endif
++# ifdef CONFIG_CPU_ARM1022
++# ifdef CPU_NAME
++# undef MULTI_CPU
++# define MULTI_CPU
++# else
++# define CPU_NAME arm1022
++# endif
++# endif
+ # ifdef CONFIG_CPU_ARM1026
+ # ifdef CPU_NAME
+ # undef MULTI_CPU
+diff -urN kernel-source-2.4.27-8/include/asm-arm/processor.h kernel-source-2.4.27-8-arm-1/include/asm-arm/processor.h
+--- kernel-source-2.4.27-8/include/asm-arm/processor.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/processor.h 2005-02-18 17:48:44.000000000 +0000
+@@ -43,6 +43,7 @@
+ #include <asm/atomic.h>
+ #include <asm/ptrace.h>
+ #include <asm/arch/memory.h>
++#include <asm/elf.h>
+ #include <asm/proc/processor.h>
+ #include <asm/types.h>
+
+diff -urN kernel-source-2.4.27-8/include/asm-arm/system.h kernel-source-2.4.27-8-arm-1/include/asm-arm/system.h
+--- kernel-source-2.4.27-8/include/asm-arm/system.h 2003-08-25 12:44:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/system.h 2005-02-18 17:48:44.000000000 +0000
+@@ -29,6 +29,10 @@
+
+ void die_if_kernel(const char *str, struct pt_regs *regs, int err);
+
++void hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
++ struct pt_regs *),
++ int sig, const char *name);
++
+ #include <asm/proc-fns.h>
+
+ #define xchg(ptr,x) \
+@@ -89,6 +93,14 @@
+ #define sti() local_irq_enable()
+ #define clf() __clf()
+ #define stf() __stf()
++
++#define irqs_disabled() \
++({ \
++ unsigned long flags; \
++ local_save_flags(flags); \
++ flags & PSR_I_BIT; \
++})
++
+ #define save_flags(x) local_save_flags(x)
+ #define restore_flags(x) local_irq_restore(x)
+ #define save_flags_cli(x) local_irq_save(x)
+diff -urN kernel-source-2.4.27-8/include/asm-arm/termios.h kernel-source-2.4.27-8-arm-1/include/asm-arm/termios.h
+--- kernel-source-2.4.27-8/include/asm-arm/termios.h 2001-06-12 03:15:27.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/termios.h 2005-02-18 17:48:44.000000000 +0000
+@@ -47,6 +47,8 @@
+ #define TIOCM_OUT2 0x4000
+ #define TIOCM_LOOP 0x8000
+
++#define TIOCM_MODEM_BITS TIOCM_OUT2 /* IRDA support */
++
+ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+
+ /* line disciplines */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/uaccess.h kernel-source-2.4.27-8-arm-1/include/asm-arm/uaccess.h
+--- kernel-source-2.4.27-8/include/asm-arm/uaccess.h 2001-10-25 21:53:55.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/uaccess.h 2005-02-18 17:48:44.000000000 +0000
+@@ -74,14 +74,14 @@
+ __asm__ __volatile__ ("bl __get_user_" #__s \
+ : "=&r" (__e), "=r" (__r1) \
+ : "0" (__p) \
+- : __i)
++ : __i, "cc")
+
+ #define get_user(x,p) \
+ ({ \
+ const register typeof(*(p)) *__p asm("r0") = (p); \
+ register typeof(*(p)) __r1 asm("r1"); \
+ register int __e asm("r0"); \
+- switch (sizeof(*(p))) { \
++ switch (sizeof(*(__p))) { \
+ case 1: \
+ __get_user_x(__r1, __p, __e, 1, "lr"); \
+ break; \
+@@ -100,8 +100,31 @@
+ __e; \
+ })
+
+-#define __get_user(x,p) __get_user_nocheck((x),(p),sizeof(*(p)))
+-#define __get_user_error(x,p,e) __get_user_nocheck_error((x),(p),sizeof(*(p)),(e))
++#define __get_user(x,ptr) \
++({ \
++ long __gu_err = 0; \
++ __get_user_err((x),(ptr),__gu_err); \
++ __gu_err; \
++})
++
++#define __get_user_error(x,ptr,err) \
++({ \
++ __get_user_err((x),(ptr),err); \
++ (void) 0; \
++})
++
++#define __get_user_err(x,ptr,err) \
++do { \
++ unsigned long __gu_addr = (unsigned long)(ptr); \
++ unsigned long __gu_val; \
++ switch (sizeof(*(ptr))) { \
++ case 1: __get_user_asm_byte(__gu_val,__gu_addr,err); break; \
++ case 2: __get_user_asm_half(__gu_val,__gu_addr,err); break; \
++ case 4: __get_user_asm_word(__gu_val,__gu_addr,err); break; \
++ default: (__gu_val) = __get_user_bad(); \
++ } \
++ (x) = (__typeof__(*(ptr)))__gu_val; \
++} while (0)
+
+ extern int __put_user_1(void *, unsigned int);
+ extern int __put_user_2(void *, unsigned int);
+@@ -113,22 +136,22 @@
+ __asm__ __volatile__ ("bl __put_user_" #__s \
+ : "=&r" (__e) \
+ : "0" (__p), "r" (__r1) \
+- : __i)
++ : __i, "cc")
+
+ #define put_user(x,p) \
+ ({ \
+ const register typeof(*(p)) __r1 asm("r1") = (x); \
+ const register typeof(*(p)) *__p asm("r0") = (p); \
+ register int __e asm("r0"); \
+- switch (sizeof(*(p))) { \
++ switch (sizeof(*(__p))) { \
+ case 1: \
+- __put_user_x(__r1, __p, __e, 1, "r2", "lr"); \
++ __put_user_x(__r1, __p, __e, 1, "ip", "lr"); \
+ break; \
+ case 2: \
+- __put_user_x(__r1, __p, __e, 2, "r2", "lr"); \
++ __put_user_x(__r1, __p, __e, 2, "ip", "lr"); \
+ break; \
+ case 4: \
+- __put_user_x(__r1, __p, __e, 4, "r2", "lr"); \
++ __put_user_x(__r1, __p, __e, 4, "ip", "lr"); \
+ break; \
+ case 8: \
+ __put_user_x(__r1, __p, __e, 8, "ip", "lr"); \
+@@ -138,8 +161,31 @@
+ __e; \
+ })
+
+-#define __put_user(x,p) __put_user_nocheck((__typeof(*(p)))(x),(p),sizeof(*(p)))
+-#define __put_user_error(x,p,e) __put_user_nocheck_error((x),(p),sizeof(*(p)),(e))
++#define __put_user(x,ptr) \
++({ \
++ long __pu_err = 0; \
++ __put_user_err((x),(ptr),__pu_err); \
++ __pu_err; \
++})
++
++#define __put_user_error(x,ptr,err) \
++({ \
++ __put_user_err((x),(ptr),err); \
++ (void) 0; \
++})
++
++#define __put_user_err(x,ptr,err) \
++do { \
++ unsigned long __pu_addr = (unsigned long)(ptr); \
++ __typeof__(*(ptr)) __pu_val = (x); \
++ switch (sizeof(*(ptr))) { \
++ case 1: __put_user_asm_byte(__pu_val,__pu_addr,err); break; \
++ case 2: __put_user_asm_half(__pu_val,__pu_addr,err); break; \
++ case 4: __put_user_asm_word(__pu_val,__pu_addr,err); break; \
++ case 8: __put_user_asm_dword(__pu_val,__pu_addr,err); break; \
++ default: __put_user_bad(); \
++ } \
++} while (0)
+
+ static __inline__ unsigned long copy_from_user(void *to, const void *from, unsigned long n)
+ {
+@@ -209,82 +255,4 @@
+ return res;
+ }
+
+-/*
+- * These are the work horses of the get/put_user functions
+- */
+-#if 0
+-#define __get_user_check(x,ptr,size) \
+-({ \
+- long __gu_err = -EFAULT, __gu_val = 0; \
+- const __typeof__(*(ptr)) *__gu_addr = (ptr); \
+- if (access_ok(VERIFY_READ,__gu_addr,size)) { \
+- __gu_err = 0; \
+- __get_user_size(__gu_val,__gu_addr,(size),__gu_err); \
+- } \
+- (x) = (__typeof__(*(ptr)))__gu_val; \
+- __gu_err; \
+-})
+-#endif
+-
+-#define __get_user_nocheck(x,ptr,size) \
+-({ \
+- long __gu_err = 0, __gu_val; \
+- __get_user_size(__gu_val,(ptr),(size),__gu_err); \
+- (x) = (__typeof__(*(ptr)))__gu_val; \
+- __gu_err; \
+-})
+-
+-#define __get_user_nocheck_error(x,ptr,size,err) \
+-({ \
+- long __gu_val; \
+- __get_user_size(__gu_val,(ptr),(size),(err)); \
+- (x) = (__typeof__(*(ptr)))__gu_val; \
+- (void) 0; \
+-})
+-
+-#define __put_user_check(x,ptr,size) \
+-({ \
+- long __pu_err = -EFAULT; \
+- __typeof__(*(ptr)) *__pu_addr = (ptr); \
+- if (access_ok(VERIFY_WRITE,__pu_addr,size)) { \
+- __pu_err = 0; \
+- __put_user_size((x),__pu_addr,(size),__pu_err); \
+- } \
+- __pu_err; \
+-})
+-
+-#define __put_user_nocheck(x,ptr,size) \
+-({ \
+- long __pu_err = 0; \
+- __typeof__(*(ptr)) *__pu_addr = (ptr); \
+- __put_user_size((x),__pu_addr,(size),__pu_err); \
+- __pu_err; \
+-})
+-
+-#define __put_user_nocheck_error(x,ptr,size,err) \
+-({ \
+- __put_user_size((x),(ptr),(size),err); \
+- (void) 0; \
+-})
+-
+-#define __get_user_size(x,ptr,size,retval) \
+-do { \
+- switch (size) { \
+- case 1: __get_user_asm_byte(x,ptr,retval); break; \
+- case 2: __get_user_asm_half(x,ptr,retval); break; \
+- case 4: __get_user_asm_word(x,ptr,retval); break; \
+- default: (x) = __get_user_bad(); \
+- } \
+-} while (0)
+-
+-#define __put_user_size(x,ptr,size,retval) \
+-do { \
+- switch (size) { \
+- case 1: __put_user_asm_byte(x,ptr,retval); break; \
+- case 2: __put_user_asm_half(x,ptr,retval); break; \
+- case 4: __put_user_asm_word(x,ptr,retval); break; \
+- default: __put_user_bad(); \
+- } \
+-} while (0)
+-
+ #endif /* _ASMARM_UACCESS_H */
+diff -urN kernel-source-2.4.27-8/include/asm-arm/unistd.h kernel-source-2.4.27-8-arm-1/include/asm-arm/unistd.h
+--- kernel-source-2.4.27-8/include/asm-arm/unistd.h 2005-01-19 09:57:51.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-arm/unistd.h 2005-02-18 17:48:44.000000000 +0000
+@@ -31,7 +31,7 @@
+ #define __NR_write (__NR_SYSCALL_BASE+ 4)
+ #define __NR_open (__NR_SYSCALL_BASE+ 5)
+ #define __NR_close (__NR_SYSCALL_BASE+ 6)
+-#define __NR_waitpid (__NR_SYSCALL_BASE+ 7)
++ /* 7 was sys_waitpid */
+ #define __NR_creat (__NR_SYSCALL_BASE+ 8)
+ #define __NR_link (__NR_SYSCALL_BASE+ 9)
+ #define __NR_unlink (__NR_SYSCALL_BASE+ 10)
+@@ -41,8 +41,8 @@
+ #define __NR_mknod (__NR_SYSCALL_BASE+ 14)
+ #define __NR_chmod (__NR_SYSCALL_BASE+ 15)
+ #define __NR_lchown (__NR_SYSCALL_BASE+ 16)
+-#define __NR_break (__NR_SYSCALL_BASE+ 17)
+-
++ /* 17 was sys_break */
++ /* 18 was sys_stat */
+ #define __NR_lseek (__NR_SYSCALL_BASE+ 19)
+ #define __NR_getpid (__NR_SYSCALL_BASE+ 20)
+ #define __NR_mount (__NR_SYSCALL_BASE+ 21)
+@@ -52,14 +52,14 @@
+ #define __NR_stime (__NR_SYSCALL_BASE+ 25)
+ #define __NR_ptrace (__NR_SYSCALL_BASE+ 26)
+ #define __NR_alarm (__NR_SYSCALL_BASE+ 27)
+-
++ /* 28 was sys_fstat */
+ #define __NR_pause (__NR_SYSCALL_BASE+ 29)
+ #define __NR_utime (__NR_SYSCALL_BASE+ 30)
+-#define __NR_stty (__NR_SYSCALL_BASE+ 31)
+-#define __NR_gtty (__NR_SYSCALL_BASE+ 32)
++ /* 31 was sys_stty */
++ /* 32 was sys_gtty */
+ #define __NR_access (__NR_SYSCALL_BASE+ 33)
+ #define __NR_nice (__NR_SYSCALL_BASE+ 34)
+-#define __NR_ftime (__NR_SYSCALL_BASE+ 35)
++ /* 35 was sys_ftime */
+ #define __NR_sync (__NR_SYSCALL_BASE+ 36)
+ #define __NR_kill (__NR_SYSCALL_BASE+ 37)
+ #define __NR_rename (__NR_SYSCALL_BASE+ 38)
+@@ -68,22 +68,23 @@
+ #define __NR_dup (__NR_SYSCALL_BASE+ 41)
+ #define __NR_pipe (__NR_SYSCALL_BASE+ 42)
+ #define __NR_times (__NR_SYSCALL_BASE+ 43)
+-#define __NR_prof (__NR_SYSCALL_BASE+ 44)
++ /* 44 was sys_prof */
+ #define __NR_brk (__NR_SYSCALL_BASE+ 45)
+ #define __NR_setgid (__NR_SYSCALL_BASE+ 46)
+ #define __NR_getgid (__NR_SYSCALL_BASE+ 47)
+-#define __NR_signal (__NR_SYSCALL_BASE+ 48)
++ /* 48 was sys_signal */
+ #define __NR_geteuid (__NR_SYSCALL_BASE+ 49)
+ #define __NR_getegid (__NR_SYSCALL_BASE+ 50)
+ #define __NR_acct (__NR_SYSCALL_BASE+ 51)
+ #define __NR_umount2 (__NR_SYSCALL_BASE+ 52)
+-#define __NR_lock (__NR_SYSCALL_BASE+ 53)
++ /* 53 was sys_lock */
+ #define __NR_ioctl (__NR_SYSCALL_BASE+ 54)
+ #define __NR_fcntl (__NR_SYSCALL_BASE+ 55)
+-#define __NR_mpx (__NR_SYSCALL_BASE+ 56)
++ /* 56 was sys_mpx */
+ #define __NR_setpgid (__NR_SYSCALL_BASE+ 57)
+ #define __NR_ulimit (__NR_SYSCALL_BASE+ 58)
+-
++ /* 58 was sys_ulimit */
++ /* 59 was sys_olduname */
+ #define __NR_umask (__NR_SYSCALL_BASE+ 60)
+ #define __NR_chroot (__NR_SYSCALL_BASE+ 61)
+ #define __NR_ustat (__NR_SYSCALL_BASE+ 62)
+@@ -92,8 +93,8 @@
+ #define __NR_getpgrp (__NR_SYSCALL_BASE+ 65)
+ #define __NR_setsid (__NR_SYSCALL_BASE+ 66)
+ #define __NR_sigaction (__NR_SYSCALL_BASE+ 67)
+-#define __NR_sgetmask (__NR_SYSCALL_BASE+ 68)
+-#define __NR_ssetmask (__NR_SYSCALL_BASE+ 69)
++ /* 68 was sys_sgetmask */
++ /* 69 was sys_ssetmask */
+ #define __NR_setreuid (__NR_SYSCALL_BASE+ 70)
+ #define __NR_setregid (__NR_SYSCALL_BASE+ 71)
+ #define __NR_sigsuspend (__NR_SYSCALL_BASE+ 72)
+@@ -108,7 +109,7 @@
+ #define __NR_setgroups (__NR_SYSCALL_BASE+ 81)
+ #define __NR_select (__NR_SYSCALL_BASE+ 82)
+ #define __NR_symlink (__NR_SYSCALL_BASE+ 83)
+-
++ /* 84 was sys_lstat */
+ #define __NR_readlink (__NR_SYSCALL_BASE+ 85)
+ #define __NR_uselib (__NR_SYSCALL_BASE+ 86)
+ #define __NR_swapon (__NR_SYSCALL_BASE+ 87)
+@@ -122,10 +123,10 @@
+ #define __NR_fchown (__NR_SYSCALL_BASE+ 95)
+ #define __NR_getpriority (__NR_SYSCALL_BASE+ 96)
+ #define __NR_setpriority (__NR_SYSCALL_BASE+ 97)
+-#define __NR_profil (__NR_SYSCALL_BASE+ 98)
++ /* 98 was sys_profil */
+ #define __NR_statfs (__NR_SYSCALL_BASE+ 99)
+ #define __NR_fstatfs (__NR_SYSCALL_BASE+100)
+-#define __NR_ioperm (__NR_SYSCALL_BASE+101)
++ /* 101 was sys_ioperm */
+ #define __NR_socketcall (__NR_SYSCALL_BASE+102)
+ #define __NR_syslog (__NR_SYSCALL_BASE+103)
+ #define __NR_setitimer (__NR_SYSCALL_BASE+104)
+@@ -133,10 +134,10 @@
+ #define __NR_stat (__NR_SYSCALL_BASE+106)
+ #define __NR_lstat (__NR_SYSCALL_BASE+107)
+ #define __NR_fstat (__NR_SYSCALL_BASE+108)
+-
+-
++ /* 109 was sys_uname */
++ /* 110 was sys_iopl */
+ #define __NR_vhangup (__NR_SYSCALL_BASE+111)
+-#define __NR_idle (__NR_SYSCALL_BASE+112)
++ /* 112 was sys_idle */
+ #define __NR_syscall (__NR_SYSCALL_BASE+113) /* syscall to call a syscall! */
+ #define __NR_wait4 (__NR_SYSCALL_BASE+114)
+ #define __NR_swapoff (__NR_SYSCALL_BASE+115)
+@@ -147,7 +148,7 @@
+ #define __NR_clone (__NR_SYSCALL_BASE+120)
+ #define __NR_setdomainname (__NR_SYSCALL_BASE+121)
+ #define __NR_uname (__NR_SYSCALL_BASE+122)
+-#define __NR_modify_ldt (__NR_SYSCALL_BASE+123)
++ /* 123 was sys_modify_ldt */
+ #define __NR_adjtimex (__NR_SYSCALL_BASE+124)
+ #define __NR_mprotect (__NR_SYSCALL_BASE+125)
+ #define __NR_sigprocmask (__NR_SYSCALL_BASE+126)
+@@ -161,7 +162,7 @@
+ #define __NR_bdflush (__NR_SYSCALL_BASE+134)
+ #define __NR_sysfs (__NR_SYSCALL_BASE+135)
+ #define __NR_personality (__NR_SYSCALL_BASE+136)
+-#define __NR_afs_syscall (__NR_SYSCALL_BASE+137) /* Syscall for Andrew File System */
++ /* 137 was sys_afs_syscall */
+ #define __NR_setfsuid (__NR_SYSCALL_BASE+138)
+ #define __NR_setfsgid (__NR_SYSCALL_BASE+139)
+ #define __NR__llseek (__NR_SYSCALL_BASE+140)
+@@ -190,7 +191,7 @@
+ #define __NR_mremap (__NR_SYSCALL_BASE+163)
+ #define __NR_setresuid (__NR_SYSCALL_BASE+164)
+ #define __NR_getresuid (__NR_SYSCALL_BASE+165)
+-#define __NR_vm86 (__NR_SYSCALL_BASE+166)
++ /* 166 was sys_vm86 */
+ #define __NR_query_module (__NR_SYSCALL_BASE+167)
+ #define __NR_poll (__NR_SYSCALL_BASE+168)
+ #define __NR_nfsservctl (__NR_SYSCALL_BASE+169)
+diff -urN kernel-source-2.4.27-8/include/asm-i386/ide.h kernel-source-2.4.27-8-arm-1/include/asm-i386/ide.h
+--- kernel-source-2.4.27-8/include/asm-i386/ide.h 2003-06-13 15:51:38.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-i386/ide.h 2005-02-18 17:48:44.000000000 +0000
+@@ -23,39 +23,15 @@
+ # endif
+ #endif
+
+-static __inline__ int ide_default_irq(ide_ioreg_t base)
+-{
+- switch (base) {
+- case 0x1f0: return 14;
+- case 0x170: return 15;
+- case 0x1e8: return 11;
+- case 0x168: return 10;
+- case 0x1e0: return 8;
+- case 0x160: return 12;
+- default:
+- return 0;
+- }
+-}
+-
+-static __inline__ ide_ioreg_t ide_default_io_base(int index)
+-{
+- switch (index) {
+- case 0: return 0x1f0;
+- case 1: return 0x170;
+- case 2: return 0x1e8;
+- case 3: return 0x168;
+- case 4: return 0x1e0;
+- case 5: return 0x160;
+- default:
+- return 0;
+- }
+-}
++#define ide_default_io_base(i) ((ide_ioreg_t)0)
++#define ide_default_irq(b) (0)
+
+ static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
+ {
+ ide_ioreg_t reg = data_port;
+ int i;
+
++ memset(hw, 0, sizeof(*hw));
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hw->io_ports[i] = reg;
+ reg += 1;
+@@ -63,7 +39,7 @@
+ if (ctrl_port) {
+ hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+ } else {
+- hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206;
++ hw->io_ports[IDE_CONTROL_OFFSET] = data_port + 0x206;
+ }
+ if (irq != NULL)
+ *irq = 0;
+@@ -74,14 +50,25 @@
+ {
+ #ifndef CONFIG_BLK_DEV_IDEPCI
+ hw_regs_t hw;
+- int index;
+
+- for(index = 0; index < MAX_HWIFS; index++) {
+- memset(&hw, 0, sizeof hw);
+- ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL);
+- hw.irq = ide_default_irq(ide_default_io_base(index));
++ ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL);
++ hw.irq = 14;
++ ide_register_hw(&hw, NULL);
++ ide_init_hwif_ports(&hw, 0x170, 0x376, NULL);
++ hw.irq = 15;
++ ide_register_hw(&hw, NULL);
++ ide_init_hwif_ports(&hw, 0x1e8, 0x3ee, NULL);
++ hw.irq = 11;
++ ide_register_hw(&hw, NULL);
++ ide_init_hwif_ports(&hw, 0x168, 0x36e, NULL);
++ hw.irq = 10;
++ ide_register_hw(&hw, NULL);
++ ide_init_hwif_ports(&hw, 0x1e0, 0x3e6, NULL);
++ hw.irq = 8;
++ ide_register_hw(&hw, NULL);
++ ide_init_hwif_ports(&hw, 0x160, 0x366, NULL);
++ hw.irq = 12;
+ ide_register_hw(&hw, NULL);
+- }
+ #endif /* CONFIG_BLK_DEV_IDEPCI */
+ }
+
+diff -urN kernel-source-2.4.27-8/include/asm-i386/param.h kernel-source-2.4.27-8-arm-1/include/asm-i386/param.h
+--- kernel-source-2.4.27-8/include/asm-i386/param.h 2000-10-27 19:04:43.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-i386/param.h 2005-02-18 17:48:44.000000000 +0000
+@@ -3,6 +3,16 @@
+
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#if HZ == 100
++/* X86 is defined to provide userspace with a world where HZ=100
++ We have to do this, (x*const)/const2 isnt optimised out because its not
++ a null operation as it might overflow.. */
++#define hz_to_std(a) (a)
++#else
++#define hz_to_std(a) ((a)*(100/HZ)+((a)*(100%HZ))/HZ)
++#endif
++#endif
+ #endif
+
+ #define EXEC_PAGESIZE 4096
+diff -urN kernel-source-2.4.27-8/include/asm-i386/pgtable.h kernel-source-2.4.27-8-arm-1/include/asm-i386/pgtable.h
+--- kernel-source-2.4.27-8/include/asm-i386/pgtable.h 2002-11-28 23:53:15.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-i386/pgtable.h 2005-02-18 17:48:44.000000000 +0000
+@@ -361,7 +361,6 @@
+ #endif /* !__ASSEMBLY__ */
+
+ /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
+-#define PageSkip(page) (0)
+ #define kern_addr_valid(addr) (1)
+
+ #define io_remap_page_range remap_page_range
+diff -urN kernel-source-2.4.27-8/include/asm-ia64/param.h kernel-source-2.4.27-8-arm-1/include/asm-ia64/param.h
+--- kernel-source-2.4.27-8/include/asm-ia64/param.h 2004-04-14 14:05:40.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-ia64/param.h 2005-02-18 17:48:44.000000000 +0000
+@@ -4,12 +4,14 @@
+ /*
+ * Fundamental kernel parameters.
+ *
+- * Based on <asm-i386/param.h>.
+- *
+- * Modified 1998, 1999, 2002-2003
+- * David Mosberger-Tang <davidm at hpl.hp.com>, Hewlett-Packard Co
++ * Copyright (C) 1998, 1999, 2002-2003 Hewlett-Packard Co
++ * David Mosberger-Tang <davidm at hpl.hp.com>
+ */
+
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
++
+ #define EXEC_PAGESIZE 65536
+
+ #ifndef NGROUPS
+diff -urN kernel-source-2.4.27-8/include/asm-m68k/param.h kernel-source-2.4.27-8-arm-1/include/asm-m68k/param.h
+--- kernel-source-2.4.27-8/include/asm-m68k/param.h 2001-01-04 21:00:55.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-m68k/param.h 2005-02-18 17:48:45.000000000 +0000
+@@ -3,6 +3,9 @@
+
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+
+ #define EXEC_PAGESIZE 8192
+diff -urN kernel-source-2.4.27-8/include/asm-ppc/param.h kernel-source-2.4.27-8-arm-1/include/asm-ppc/param.h
+--- kernel-source-2.4.27-8/include/asm-ppc/param.h 2003-06-13 15:51:38.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-ppc/param.h 2005-02-18 17:48:45.000000000 +0000
+@@ -3,6 +3,9 @@
+
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+
+ #define EXEC_PAGESIZE 4096
+diff -urN kernel-source-2.4.27-8/include/asm-s390/param.h kernel-source-2.4.27-8-arm-1/include/asm-s390/param.h
+--- kernel-source-2.4.27-8/include/asm-s390/param.h 2001-02-13 22:13:44.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-s390/param.h 2005-02-18 17:48:45.000000000 +0000
+@@ -11,6 +11,9 @@
+
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+
+ #define EXEC_PAGESIZE 4096
+diff -urN kernel-source-2.4.27-8/include/asm-sh/param.h kernel-source-2.4.27-8-arm-1/include/asm-sh/param.h
+--- kernel-source-2.4.27-8/include/asm-sh/param.h 2001-01-04 21:19:13.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-sh/param.h 2005-02-18 17:48:45.000000000 +0000
+@@ -3,6 +3,9 @@
+
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+
+ #define EXEC_PAGESIZE 4096
+diff -urN kernel-source-2.4.27-8/include/asm-sparc/param.h kernel-source-2.4.27-8-arm-1/include/asm-sparc/param.h
+--- kernel-source-2.4.27-8/include/asm-sparc/param.h 2000-10-30 22:34:12.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-sparc/param.h 2005-02-18 17:48:45.000000000 +0000
+@@ -4,6 +4,9 @@
+
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+
+ #define EXEC_PAGESIZE 8192 /* Thanks for sun4's we carry baggage... */
+diff -urN kernel-source-2.4.27-8/include/asm-sparc64/ide.h kernel-source-2.4.27-8-arm-1/include/asm-sparc64/ide.h
+--- kernel-source-2.4.27-8/include/asm-sparc64/ide.h 2003-06-13 15:51:38.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/asm-sparc64/ide.h 2005-02-18 17:48:45.000000000 +0000
+@@ -25,21 +25,12 @@
+ # endif
+ #endif
+
+-static __inline__ int ide_default_irq(ide_ioreg_t base)
+-{
+- return 0;
+-}
+-
+-static __inline__ ide_ioreg_t ide_default_io_base(int index)
+-{
+- return 0;
+-}
+-
+ static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
+ {
+ ide_ioreg_t reg = data_port;
+ int i;
+
++ memset(&hw, 0, sizeof(hw));
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hw->io_ports[i] = reg;
+ reg += 1;
+@@ -60,16 +51,6 @@
+ */
+ static __inline__ void ide_init_default_hwifs(void)
+ {
+-#ifndef CONFIG_BLK_DEV_IDEPCI
+- hw_regs_t hw;
+- int index;
+-
+- for (index = 0; index < MAX_HWIFS; index++) {
+- ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL);
+- hw.irq = ide_default_irq(ide_default_io_base(index));
+- ide_register_hw(&hw, NULL);
+- }
+-#endif /* CONFIG_BLK_DEV_IDEPCI */
+ }
+
+ #undef SUPPORT_SLOW_DATA_PORTS
+diff -urN kernel-source-2.4.27-8/include/asm-sparc64/param.h kernel-source-2.4.27-8-arm-1/include/asm-sparc64/param.h
+--- kernel-source-2.4.27-8/include/asm-sparc64/param.h 2000-10-30 22:34:12.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/asm-sparc64/param.h 2005-02-18 17:48:45.000000000 +0000
+@@ -4,6 +4,9 @@
+
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+
+ #define EXEC_PAGESIZE 8192 /* Thanks for sun4's we carry baggage... */
+diff -urN kernel-source-2.4.27-8/include/linux/console_struct.h kernel-source-2.4.27-8-arm-1/include/linux/console_struct.h
+--- kernel-source-2.4.27-8/include/linux/console_struct.h 2003-06-13 15:51:38.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/console_struct.h 2005-02-18 17:48:45.000000000 +0000
+@@ -7,6 +7,8 @@
+ * Fields marked with [#] must be set by the low-level driver.
+ * Fields marked with [!] can be changed by the low-level driver
+ * to achieve effects such as fast scrolling by changing the origin.
++ *
++ * 11/07/1998 RMK Changed vc_state to be a function pointer
+ */
+
+ #ifndef _LINUX_CONSOLE_STRUCT_H_
+@@ -34,7 +36,11 @@
+ unsigned short vc_s_complement_mask; /* Saved mouse pointer mask */
+ unsigned int vc_x, vc_y; /* Cursor position */
+ unsigned int vc_top, vc_bottom; /* Scrolling region */
++#ifdef CONSOLE_WIP
++ int (*vc_state)(int currcons, struct tty_struct *tty, unsigned int c);
++#else
+ unsigned int vc_state; /* Escape sequence parser state */
++#endif
+ unsigned int vc_npar,vc_par[NPAR]; /* Parameters of current escape sequence */
+ unsigned long vc_origin; /* [!] Start of real screen */
+ unsigned long vc_scr_end; /* [!] End of real screen */
+diff -urN kernel-source-2.4.27-8/include/linux/cpufreq.h kernel-source-2.4.27-8-arm-1/include/linux/cpufreq.h
+--- kernel-source-2.4.27-8/include/linux/cpufreq.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/cpufreq.h 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,94 @@
++/*
++ * linux/include/linux/cpufreq.h
++ *
++ * Copyright (C) 2001 Russell King
++ *
++ * $Id: cpufreq.h,v 1.5.2.1 2002/05/03 13:26:27 rmk 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.
++ */
++#ifndef _LINUX_CPUFREQ_H
++#define _LINUX_CPUFREQ_H
++
++#include <linux/config.h>
++#include <linux/notifier.h>
++
++#ifndef CONFIG_SMP
++#define cpufreq_current(cpu) ((void)(cpu), __cpufreq_cur)
++#define cpufreq_max(cpu) ((void)(cpu), __cpufreq_max)
++#define cpufreq_min(cpu) ((void)(cpu), __cpufreq_min)
++#else
++/*
++ * Should be something like:
++ *
++ * typedef struct {
++ * u_int current;
++ * u_int max;
++ * u_int min;
++ * } __cacheline_aligned cpufreq_info_t;
++ *
++ * static cpufreq_info_t cpufreq_info;
++ *
++ * #define cpufreq_current(cpu) (cpufreq_info[cpu].current)
++ * #define cpufreq_max(cpu) (cpufreq_info[cpu].max)
++ * #define cpufreq_min(cpu) (cpufreq_info[cpu].min)
++ *
++ * Maybe we should find some other per-cpu structure to
++ * bury this in?
++ */
++#error fill in SMP version
++#endif
++
++struct cpufreq_info {
++ unsigned int old_freq;
++ unsigned int new_freq;
++};
++
++/*
++ * The max and min frequency rates that the registered device
++ * can tolerate. Never set any element this structure directly -
++ * always use cpu_updateminmax.
++ */
++struct cpufreq_minmax {
++ unsigned int min_freq;
++ unsigned int max_freq;
++ unsigned int cur_freq;
++ unsigned int new_freq;
++};
++
++static inline
++void cpufreq_updateminmax(void *arg, unsigned int min, unsigned int max)
++{
++ struct cpufreq_minmax *minmax = arg;
++
++ if (minmax->min_freq < min)
++ minmax->min_freq = min;
++ if (minmax->max_freq > max)
++ minmax->max_freq = max;
++}
++
++#define CPUFREQ_MINMAX (0)
++#define CPUFREQ_PRECHANGE (1)
++#define CPUFREQ_POSTCHANGE (2)
++
++int cpufreq_register_notifier(struct notifier_block *nb);
++int cpufreq_unregister_notifier(struct notifier_block *nb);
++
++int cpufreq_setmax(void);
++int cpufreq_restore(void);
++int cpufreq_set(unsigned int khz);
++unsigned int cpufreq_get(int cpu);
++
++/*
++ * These two functions are only available at init time.
++ */
++void cpufreq_init(unsigned int khz,
++ unsigned int min_freq,
++ unsigned int max_freq);
++
++void cpufreq_setfunctions(unsigned int (*validate)(unsigned int),
++ void (*setspeed)(unsigned int));
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/linux/i2c-id.h kernel-source-2.4.27-8-arm-1/include/linux/i2c-id.h
+--- kernel-source-2.4.27-8/include/linux/i2c-id.h 2004-02-18 13:36:32.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/linux/i2c-id.h 2005-02-18 17:48:45.000000000 +0000
+@@ -20,7 +20,7 @@
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+ /* ------------------------------------------------------------------------- */
+
+-/* $Id: i2c-id.h,v 1.35 2001/08/12 17:22:20 mds Exp $ */
++/* $Id: i2c-id.h,v 1.52 2002/07/10 13:28:44 abz Exp $ */
+
+ #ifndef I2C_ID_H
+ #define I2C_ID_H
+@@ -90,7 +90,12 @@
+ #define I2C_DRIVERID_DRP3510 43 /* ADR decoder (Astra Radio) */
+ #define I2C_DRIVERID_SP5055 44 /* Satellite tuner */
+ #define I2C_DRIVERID_STV0030 45 /* Multipurpose switch */
++#define I2C_DRIVERID_SAA7108 46 /* video decoder, image scaler */
++#define I2C_DRIVERID_DS1307 47 /* DS1307 real time clock */
+ #define I2C_DRIVERID_ADV7175 48 /* ADV 7175/7176 video encoder */
++#define I2C_DRIVERID_ZR36067 49 /* Zoran 36067 video encoder */
++#define I2C_DRIVERID_ZR36120 50 /* Zoran 36120 video encoder */
++#define I2C_DRIVERID_24LC32A 51 /* Microchip 24LC32A 32k EEPROM */
+ #define I2C_DRIVERID_MAX1617 56 /* temp sensor */
+ #define I2C_DRIVERID_SAA7191 57 /* video decoder */
+ #define I2C_DRIVERID_INDYCAM 58 /* SGI IndyCam */
+@@ -102,6 +107,8 @@
+
+ #define I2C_DRIVERID_I2CDEV 900
+ #define I2C_DRIVERID_I2CPROC 901
++#define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */
++#define I2C_DRIVERID_ALERT 903 /* SMBus Alert Responder Client */
+
+ /* IDs -- Use DRIVERIDs 1000-1999 for sensors.
+ These were originally in sensors.h in the lm_sensors package */
+@@ -131,6 +138,12 @@
+ #define I2C_DRIVERID_ADM1024 1025
+ #define I2C_DRIVERID_IT87 1026
+ #define I2C_DRIVERID_CH700X 1027 /* single driver for CH7003-7009 digital pc to tv encoders */
++#define I2C_DRIVERID_FSCPOS 1028
++#define I2C_DRIVERID_FSCSCY 1029
++#define I2C_DRIVERID_PCF8591 1030
++#define I2C_DRIVERID_SMSC47M1 1031
++#define I2C_DRIVERID_VT1211 1032
++#define I2C_DRIVERID_LM92 1033
+
+ /*
+ * ---- Adapter types ----------------------------------------------------
+@@ -147,7 +160,8 @@
+ #define I2C_ALGO_ISA 0x050000 /* lm_sensors ISA pseudo-adapter */
+ #define I2C_ALGO_SAA7146 0x060000 /* SAA 7146 video decoder bus */
+ #define I2C_ALGO_ACB 0x070000 /* ACCESS.bus algorithm */
+-
++#define I2C_ALGO_IIC 0x080000 /* ITE IIC bus */
++#define I2C_ALGO_SAA7134 0x090000
+ #define I2C_ALGO_EC 0x100000 /* ACPI embedded controller */
+
+ #define I2C_ALGO_MPC8XX 0x110000 /* MPC8xx PowerPC I2C algorithm */
+@@ -156,6 +170,8 @@
+
+ #define I2C_ALGO_SGI 0x130000 /* SGI algorithm */
+
++#define I2C_ALGO_OCP 0x120000 /* IBM or otherwise On-chip I2C algorithm */
++
+ #define I2C_ALGO_EXP 0x800000 /* experimental */
+
+ #define I2C_ALGO_MASK 0xff0000 /* Mask for algorithms */
+@@ -182,9 +198,14 @@
+ #define I2C_HW_B_I810 0x0a /* Intel I810 */
+ #define I2C_HW_B_VOO 0x0b /* 3dfx Voodoo 3 / Banshee */
+ #define I2C_HW_B_PPORT 0x0c /* Primitive parallel port adapter */
++#define I2C_HW_B_SAVG 0x0d /* Savage 4 */
+ #define I2C_HW_B_RIVA 0x10 /* Riva based graphics cards */
+ #define I2C_HW_B_IOC 0x11 /* IOC bit-wiggling */
+ #define I2C_HW_B_TSUNA 0x12 /* DEC Tsunami chipset */
++#define I2C_HW_B_FRODO 0x13 /* 2d3D, Inc. SA-1110 Development Board */
++#define I2C_HW_B_OMAHA 0x14 /* Omaha I2C interface */
++#define I2C_HW_B_GUIDE 0x15 /* Guide bit-basher */
++#define I2C_HW_B_S3C2410 0x16 /* Samsung S3C2410 bitwise mode */
+
+ /* --- PCF 8584 based algorithms */
+ #define I2C_HW_P_LP 0x00 /* Parallel port interface */
+@@ -197,6 +218,12 @@
+ /* --- MPC8xx PowerPC adapters */
+ #define I2C_HW_MPC8XX_EPON 0x00 /* Eponymous MPC8xx I2C adapter */
+
++/* --- ITE based algorithms */
++#define I2C_HW_I_IIC 0x00 /* controller on the ITE */
++
++/* --- PowerPC on-chip adapters */
++#define I2C_HW_OCP 0x00 /* IBM on-chip I2C adapter */
++
+ /* --- Broadcom SiByte adapters */
+ #define I2C_HW_SIBYTE 0x00
+
+diff -urN kernel-source-2.4.27-8/include/linux/ide.h kernel-source-2.4.27-8-arm-1/include/linux/ide.h
+--- kernel-source-2.4.27-8/include/linux/ide.h 2005-01-19 09:57:49.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/linux/ide.h 2005-02-18 17:48:45.000000000 +0000
+@@ -287,7 +287,9 @@
+ * Check for an interrupt and acknowledge the interrupt status
+ */
+ struct hwif_s;
++struct ide_drive_s;
+ typedef int (ide_ack_intr_t)(struct hwif_s *);
++typedef void (ide_xfer_data_t)(struct ide_drive_s *, void *, unsigned int);
+
+ #ifndef NO_DMA
+ #define NO_DMA 255
+@@ -311,8 +313,12 @@
+ typedef struct hw_regs_s {
+ ide_ioreg_t io_ports[IDE_NR_PORTS]; /* task file registers */
+ int irq; /* our irq number */
+- int dma; /* our dma entry */
++ int dma; /* our dma number */
+ ide_ack_intr_t *ack_intr; /* acknowledge interrupt */
++ ide_xfer_data_t *ide_output_data; /* write data to i/face */
++ ide_xfer_data_t *ide_input_data; /* read data from i/face */
++ ide_xfer_data_t *atapi_output_bytes; /* write bytes to i/face */
++ ide_xfer_data_t *atapi_input_bytes; /* read bytes from i/face */
+ void *priv; /* interface specific data */
+ hwif_chipset_t chipset;
+ sata_ioreg_t sata_scr[SATA_NR_PORTS];
+diff -urN kernel-source-2.4.27-8/include/linux/l3/algo-bit.h kernel-source-2.4.27-8-arm-1/include/linux/l3/algo-bit.h
+--- kernel-source-2.4.27-8/include/linux/l3/algo-bit.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/l3/algo-bit.h 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,39 @@
++/*
++ * linux/include/linux/l3/algo-bit.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.
++ *
++ * L3 Bus bit-banging algorithm. Derived from i2c-algo-bit.h by
++ * Simon G. Vogl.
++ */
++#ifndef L3_ALGO_BIT_H
++#define L3_ALGO_BIT_H 1
++
++#include <linux/l3/l3.h>
++
++struct l3_algo_bit_data {
++ void (*setdat) (void *data, int state);
++ void (*setclk) (void *data, int state);
++ void (*setmode)(void *data, int state);
++ void (*setdir) (void *data, int in); /* set data direction */
++ int (*getdat) (void *data);
++
++ void *data;
++
++ /* bus timings (us) */
++ int data_hold;
++ int data_setup;
++ int clock_high;
++ int mode_hold;
++ int mode_setup;
++ int mode;
++};
++
++int l3_bit_add_bus(struct l3_adapter *);
++int l3_bit_del_bus(struct l3_adapter *);
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/linux/l3/l3.h kernel-source-2.4.27-8-arm-1/include/linux/l3/l3.h
+--- kernel-source-2.4.27-8/include/linux/l3/l3.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/l3/l3.h 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,208 @@
++/*
++ * linux/include/linux/l3/l3.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.
++ *
++ * Derived from i2c.h by Simon G. Vogl
++ */
++#ifndef L3_H
++#define L3_H
++
++struct l3_msg {
++ unsigned char addr; /* slave address */
++ unsigned char flags;
++#define L3_M_RD 0x01
++#define L3_M_NOADDR 0x02
++ unsigned short len; /* msg length */
++ unsigned char *buf; /* pointer to msg data */
++};
++
++#ifdef __KERNEL__
++
++#include <linux/types.h>
++#include <linux/list.h>
++
++struct l3_client;
++
++struct l3_ops {
++ int (*open)(struct l3_client *);
++ int (*command)(struct l3_client *, int cmd, void *arg);
++ void (*close)(struct l3_client *);
++};
++
++/*
++ * A driver is capable of handling one or more physical devices present on
++ * L3 adapters. This information is used to inform the driver of adapter
++ * events.
++ */
++struct l3_driver {
++ /*
++ * This name is used to uniquely identify the driver.
++ * It should be the same as the module name.
++ */
++ char name[32];
++
++ /*
++ * Notifies the driver that a new client wishes to use its
++ * services. Note that the module use count will be increased
++ * prior to this function being called. In addition, the
++ * clients driver and adapter fields will have been setup.
++ */
++ int (*attach_client)(struct l3_client *);
++
++ /*
++ * Notifies the driver that the client has finished with its
++ * services, and any memory that it allocated for this client
++ * should be cleaned up. In addition the chip should be
++ * shut down.
++ */
++ void (*detach_client)(struct l3_client *);
++
++ /*
++ * Possible operations on the driver.
++ */
++ struct l3_ops *ops;
++
++ /*
++ * Module structure, if any.
++ */
++ struct module *owner;
++
++ /*
++ * drivers list
++ */
++ struct list_head drivers;
++};
++
++struct l3_adapter;
++
++struct l3_algorithm {
++ /* textual description */
++ char name[32];
++
++ /* perform bus transactions */
++ int (*xfer)(struct l3_adapter *, struct l3_msg msgs[], int num);
++};
++
++struct semaphore;
++
++/*
++ * l3_adapter is the structure used to identify a physical L3 bus along
++ * with the access algorithms necessary to access it.
++ */
++struct l3_adapter {
++ /*
++ * This name is used to uniquely identify the adapter.
++ * It should be the same as the module name.
++ */
++ char name[32];
++
++ /*
++ * the algorithm to access the bus
++ */
++ struct l3_algorithm *algo;
++
++ /*
++ * Algorithm specific data
++ */
++ void *algo_data;
++
++ /*
++ * This may be NULL, or should point to the module struct
++ */
++ struct module *owner;
++
++ /*
++ * private data for the adapter
++ */
++ void *data;
++
++ /*
++ * Our lock. Unlike the i2c layer, we allow this to be used for
++ * other stuff, like the i2c layer lock. Some people implement
++ * i2c stuff using the same signals as the l3 bus.
++ */
++ struct semaphore *lock;
++
++ /*
++ * List of attached clients.
++ */
++ struct list_head clients;
++
++ /*
++ * List of all adapters.
++ */
++ struct list_head adapters;
++};
++
++/*
++ * l3_client identifies a single device (i.e. chip) that is connected to an
++ * L3 bus. The behaviour is defined by the routines of the driver. This
++ * function is mainly used for lookup & other admin. functions.
++ */
++struct l3_client {
++ struct l3_adapter *adapter; /* the adapter we sit on */
++ struct l3_driver *driver; /* and our access routines */
++ void *driver_data; /* private driver data */
++ struct list_head __adap;
++};
++
++
++extern int l3_add_adapter(struct l3_adapter *);
++extern int l3_del_adapter(struct l3_adapter *);
++
++extern int l3_add_driver(struct l3_driver *);
++extern int l3_del_driver(struct l3_driver *);
++
++extern int l3_attach_client(struct l3_client *, const char *, const char *);
++extern int l3_detach_client(struct l3_client *);
++
++extern int l3_transfer(struct l3_adapter *, struct l3_msg msgs[], int);
++extern int l3_write(struct l3_client *, int, const char *, int);
++extern int l3_read(struct l3_client *, int, char *, int);
++
++/**
++ * l3_command - send a command to a L3 device driver
++ * @client: registered client structure
++ * @cmd: device driver command
++ * @arg: device driver arguments
++ *
++ * Ask the L3 device driver to perform some function. Further information
++ * should be sought from the device driver in question.
++ *
++ * Returns negative error code on failure.
++ */
++static inline int l3_command(struct l3_client *clnt, int cmd, void *arg)
++{
++ struct l3_ops *ops = clnt->driver->ops;
++ int ret = -EINVAL;
++
++ if (ops && ops->command)
++ ret = ops->command(clnt, cmd, arg);
++
++ return ret;
++}
++
++static inline int l3_open(struct l3_client *clnt)
++{
++ struct l3_ops *ops = clnt->driver->ops;
++ int ret = 0;
++
++ if (ops && ops->open)
++ ret = ops->open(clnt);
++ return ret;
++}
++
++static inline void l3_close(struct l3_client *clnt)
++{
++ struct l3_ops *ops = clnt->driver->ops;
++ if (ops && ops->close)
++ ops->close(clnt);
++}
++#endif
++
++#endif /* L3_H */
+diff -urN kernel-source-2.4.27-8/include/linux/l3/uda1341.h kernel-source-2.4.27-8-arm-1/include/linux/l3/uda1341.h
+--- kernel-source-2.4.27-8/include/linux/l3/uda1341.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/l3/uda1341.h 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,50 @@
++/*
++ * linux/include/linux/l3/uda1341.h
++ *
++ * Philips UDA1341 mixer device driver
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico at cam.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ */
++
++#define UDA1341_NAME "uda1341"
++
++struct uda1341_cfg {
++ unsigned int fs:16;
++ unsigned int format:3;
++};
++
++#define FMT_I2S 0
++#define FMT_LSB16 1
++#define FMT_LSB18 2
++#define FMT_LSB20 3
++#define FMT_MSB 4
++#define FMT_LSB16MSB 5
++#define FMT_LSB18MSB 6
++#define FMT_LSB20MSB 7
++
++#define L3_UDA1341_CONFIGURE 0x13410001
++
++struct l3_gain {
++ unsigned int left:8;
++ unsigned int right:8;
++ unsigned int unused:8;
++ unsigned int channel:8;
++};
++
++#define L3_SET_VOLUME 0x13410002
++#define L3_SET_TREBLE 0x13410003
++#define L3_SET_BASS 0x13410004
++#define L3_SET_GAIN 0x13410005
++
++struct l3_agc {
++ unsigned int level:8;
++ unsigned int enable:1;
++ unsigned int attack:7;
++ unsigned int decay:8;
++ unsigned int channel:8;
++};
++
++#define L3_INPUT_AGC 0x13410006
+diff -urN kernel-source-2.4.27-8/include/linux/mm.h kernel-source-2.4.27-8-arm-1/include/linux/mm.h
+--- kernel-source-2.4.27-8/include/linux/mm.h 2005-01-19 09:57:58.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/linux/mm.h 2005-02-18 17:48:45.000000000 +0000
+@@ -701,6 +701,12 @@
+
+ extern struct page * vmalloc_to_page(void *addr);
+
++#ifndef __arm__
++#define memc_update_addr(x,y,z)
++#define memc_update_mm(x)
++#define memc_clear(x,y)
++#endif
++
+ #endif /* __KERNEL__ */
+
+ #endif
+diff -urN kernel-source-2.4.27-8/include/linux/mm.h.orig kernel-source-2.4.27-8-arm-1/include/linux/mm.h.orig
+--- kernel-source-2.4.27-8/include/linux/mm.h.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/mm.h.orig 2005-01-19 09:57:58.000000000 +0000
+@@ -0,0 +1,706 @@
++#ifndef _LINUX_MM_H
++#define _LINUX_MM_H
++
++#include <linux/sched.h>
++#include <linux/errno.h>
++
++#ifdef __KERNEL__
++
++#include <linux/config.h>
++#include <linux/string.h>
++#include <linux/list.h>
++#include <linux/mmzone.h>
++#include <linux/swap.h>
++#include <linux/rbtree.h>
++
++extern unsigned long max_mapnr;
++extern unsigned long num_physpages;
++extern unsigned long num_mappedpages;
++extern void * high_memory;
++extern int page_cluster;
++/* The inactive_clean lists are per zone. */
++extern struct list_head active_list;
++extern struct list_head inactive_list;
++
++#include <asm/page.h>
++#include <asm/pgtable.h>
++#include <asm/atomic.h>
++
++/*
++ * Linux kernel virtual memory manager primitives.
++ * The idea being to have a "virtual" mm in the same way
++ * we have a virtual fs - giving a cleaner interface to the
++ * mm details, and allowing different kinds of memory mappings
++ * (from shared memory to executable loading to arbitrary
++ * mmap() functions).
++ */
++
++/*
++ * This struct defines a memory VMM memory area. There is one of these
++ * per VM-area/task. A VM area is any part of the process virtual memory
++ * space that has a special rule for the page-fault handlers (ie a shared
++ * library, the executable area etc).
++ */
++struct vm_area_struct {
++ struct mm_struct * vm_mm; /* The address space we belong to. */
++ unsigned long vm_start; /* Our start address within vm_mm. */
++ unsigned long vm_end; /* The first byte after our end address
++ within vm_mm. */
++
++ /* linked list of VM areas per task, sorted by address */
++ struct vm_area_struct *vm_next;
++
++ pgprot_t vm_page_prot; /* Access permissions of this VMA. */
++ unsigned long vm_flags; /* Flags, listed below. */
++
++ rb_node_t vm_rb;
++
++ /*
++ * For areas with an address space and backing store,
++ * one of the address_space->i_mmap{,shared} lists,
++ * for shm areas, the list of attaches, otherwise unused.
++ */
++ struct vm_area_struct *vm_next_share;
++ struct vm_area_struct **vm_pprev_share;
++
++ /* Function pointers to deal with this struct. */
++ struct vm_operations_struct * vm_ops;
++
++ /* Information about our backing store: */
++ unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE
++ units, *not* PAGE_CACHE_SIZE */
++ struct file * vm_file; /* File we map to (can be NULL). */
++ unsigned long vm_raend; /* XXX: put full readahead info here. */
++ void * vm_private_data; /* was vm_pte (shared mem) */
++};
++
++/*
++ * vm_flags..
++ */
++#define VM_READ 0x00000001 /* currently active flags */
++#define VM_WRITE 0x00000002
++#define VM_EXEC 0x00000004
++#define VM_SHARED 0x00000008
++
++#define VM_MAYREAD 0x00000010 /* limits for mprotect() etc */
++#define VM_MAYWRITE 0x00000020
++#define VM_MAYEXEC 0x00000040
++#define VM_MAYSHARE 0x00000080
++
++#define VM_GROWSDOWN 0x00000100 /* general info on the segment */
++#define VM_GROWSUP 0x00000200
++#define VM_SHM 0x00000400 /* shared memory area, don't swap out */
++#define VM_DENYWRITE 0x00000800 /* ETXTBSY on write attempts.. */
++
++#define VM_EXECUTABLE 0x00001000
++#define VM_LOCKED 0x00002000
++#define VM_IO 0x00004000 /* Memory mapped I/O or similar */
++
++ /* Used by sys_madvise() */
++#define VM_SEQ_READ 0x00008000 /* App will access data sequentially */
++#define VM_RAND_READ 0x00010000 /* App will not benefit from clustered reads */
++
++#define VM_DONTCOPY 0x00020000 /* Do not copy this vma on fork */
++#define VM_DONTEXPAND 0x00040000 /* Cannot expand with mremap() */
++#define VM_RESERVED 0x00080000 /* Don't unmap it from swap_out */
++
++#ifndef VM_STACK_FLAGS
++#define VM_STACK_FLAGS 0x00000177
++#endif
++
++#define VM_READHINTMASK (VM_SEQ_READ | VM_RAND_READ)
++#define VM_ClearReadHint(v) (v)->vm_flags &= ~VM_READHINTMASK
++#define VM_NormalReadHint(v) (!((v)->vm_flags & VM_READHINTMASK))
++#define VM_SequentialReadHint(v) ((v)->vm_flags & VM_SEQ_READ)
++#define VM_RandomReadHint(v) ((v)->vm_flags & VM_RAND_READ)
++
++/* read ahead limits */
++extern int vm_min_readahead;
++extern int vm_max_readahead;
++
++/*
++ * mapping from the currently active vm_flags protection bits (the
++ * low four bits) to a page protection mask..
++ */
++extern pgprot_t protection_map[16];
++
++
++/*
++ * These are the virtual MM functions - opening of an area, closing and
++ * unmapping it (needed to keep files on disk up-to-date etc), pointer
++ * to the functions called when a no-page or a wp-page exception occurs.
++ */
++struct vm_operations_struct {
++ void (*open)(struct vm_area_struct * area);
++ void (*close)(struct vm_area_struct * area);
++ struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int unused);
++};
++
++/*
++ * Each physical page in the system has a struct page associated with
++ * it to keep track of whatever it is we are using the page for at the
++ * moment. Note that we have no way to track which tasks are using
++ * a page.
++ *
++ * Try to keep the most commonly accessed fields in single cache lines
++ * here (16 bytes or greater). This ordering should be particularly
++ * beneficial on 32-bit processors.
++ *
++ * The first line is data used in page cache lookup, the second line
++ * is used for linear searches (eg. clock algorithm scans).
++ *
++ * TODO: make this structure smaller, it could be as small as 32 bytes.
++ */
++typedef struct page {
++ struct list_head list; /* ->mapping has some page lists. */
++ struct address_space *mapping; /* The inode (or ...) we belong to. */
++ unsigned long index; /* Our offset within mapping. */
++ struct page *next_hash; /* Next page sharing our hash bucket in
++ the pagecache hash table. */
++ atomic_t count; /* Usage count, see below. */
++ unsigned long flags; /* atomic flags, some possibly
++ updated asynchronously */
++ struct list_head lru; /* Pageout list, eg. active_list;
++ protected by pagemap_lru_lock !! */
++ struct page **pprev_hash; /* Complement to *next_hash. */
++ struct buffer_head * buffers; /* Buffer maps us to a disk block. */
++
++ /*
++ * On machines where all RAM is mapped into kernel address space,
++ * we can simply calculate the virtual address. On machines with
++ * highmem some memory is mapped into kernel virtual memory
++ * dynamically, so we need a place to store that address.
++ * Note that this field could be 16 bits on x86 ... ;)
++ *
++ * Architectures with slow multiplication can define
++ * WANT_PAGE_VIRTUAL in asm/page.h
++ */
++#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
++ void *virtual; /* Kernel virtual address (NULL if
++ not kmapped, ie. highmem) */
++#endif /* CONFIG_HIGMEM || WANT_PAGE_VIRTUAL */
++} mem_map_t;
++
++/*
++ * Methods to modify the page usage count.
++ *
++ * What counts for a page usage:
++ * - cache mapping (page->mapping)
++ * - disk mapping (page->buffers)
++ * - page mapped in a task's page tables, each mapping
++ * is counted separately
++ *
++ * Also, many kernel routines increase the page count before a critical
++ * routine so they can be sure the page doesn't go away from under them.
++ */
++#define get_page(p) atomic_inc(&(p)->count)
++#define put_page(p) __free_page(p)
++#define put_page_testzero(p) atomic_dec_and_test(&(p)->count)
++#define page_count(p) atomic_read(&(p)->count)
++#define set_page_count(p,v) atomic_set(&(p)->count, v)
++
++/*
++ * Various page->flags bits:
++ *
++ * PG_reserved is set for special pages, which can never be swapped
++ * out. Some of them might not even exist (eg empty_bad_page)...
++ *
++ * Multiple processes may "see" the same page. E.g. for untouched
++ * mappings of /dev/null, all processes see the same page full of
++ * zeroes, and text pages of executables and shared libraries have
++ * only one copy in memory, at most, normally.
++ *
++ * For the non-reserved pages, page->count denotes a reference count.
++ * page->count == 0 means the page is free.
++ * page->count == 1 means the page is used for exactly one purpose
++ * (e.g. a private data page of one process).
++ *
++ * A page may be used for kmalloc() or anyone else who does a
++ * __get_free_page(). In this case the page->count is at least 1, and
++ * all other fields are unused but should be 0 or NULL. The
++ * management of this page is the responsibility of the one who uses
++ * it.
++ *
++ * The other pages (we may call them "process pages") are completely
++ * managed by the Linux memory manager: I/O, buffers, swapping etc.
++ * The following discussion applies only to them.
++ *
++ * A page may belong to an inode's memory mapping. In this case,
++ * page->mapping is the pointer to the inode, and page->index is the
++ * file offset of the page, in units of PAGE_CACHE_SIZE.
++ *
++ * A page may have buffers allocated to it. In this case,
++ * page->buffers is a circular list of these buffer heads. Else,
++ * page->buffers == NULL.
++ *
++ * For pages belonging to inodes, the page->count is the number of
++ * attaches, plus 1 if buffers are allocated to the page, plus one
++ * for the page cache itself.
++ *
++ * All pages belonging to an inode are in these doubly linked lists:
++ * mapping->clean_pages, mapping->dirty_pages and mapping->locked_pages;
++ * using the page->list list_head. These fields are also used for
++ * freelist managemet (when page->count==0).
++ *
++ * There is also a hash table mapping (mapping,index) to the page
++ * in memory if present. The lists for this hash table use the fields
++ * page->next_hash and page->pprev_hash.
++ *
++ * All process pages can do I/O:
++ * - inode pages may need to be read from disk,
++ * - inode pages which have been modified and are MAP_SHARED may need
++ * to be written to disk,
++ * - private pages which have been modified may need to be swapped out
++ * to swap space and (later) to be read back into memory.
++ * During disk I/O, PG_locked is used. This bit is set before I/O
++ * and reset when I/O completes. page_waitqueue(page) is a wait queue of all
++ * tasks waiting for the I/O on this page to complete.
++ * PG_uptodate tells whether the page's contents is valid.
++ * When a read completes, the page becomes uptodate, unless a disk I/O
++ * error happened.
++ *
++ * For choosing which pages to swap out, inode pages carry a
++ * PG_referenced bit, which is set any time the system accesses
++ * that page through the (mapping,index) hash table. This referenced
++ * bit, together with the referenced bit in the page tables, is used
++ * to manipulate page->age and move the page across the active,
++ * inactive_dirty and inactive_clean lists.
++ *
++ * Note that the referenced bit, the page->lru list_head and the
++ * active, inactive_dirty and inactive_clean lists are protected by
++ * the pagemap_lru_lock, and *NOT* by the usual PG_locked bit!
++ *
++ * PG_skip is used on sparc/sparc64 architectures to "skip" certain
++ * parts of the address space.
++ *
++ * PG_error is set to indicate that an I/O error occurred on this page.
++ *
++ * PG_arch_1 is an architecture specific page state bit. The generic
++ * code guarantees that this bit is cleared for a page when it first
++ * is entered into the page cache.
++ *
++ * PG_highmem pages are not permanently mapped into the kernel virtual
++ * address space, they need to be kmapped separately for doing IO on
++ * the pages. The struct page (these bits with information) are always
++ * mapped into kernel address space...
++ */
++#define PG_locked 0 /* Page is locked. Don't touch. */
++#define PG_error 1
++#define PG_referenced 2
++#define PG_uptodate 3
++#define PG_dirty 4
++#define PG_unused 5
++#define PG_lru 6
++#define PG_active 7
++#define PG_slab 8
++#define PG_skip 10
++#define PG_highmem 11
++#define PG_checked 12 /* kill me in 2.5.<early>. */
++#define PG_arch_1 13
++#define PG_reserved 14
++#define PG_launder 15 /* written out by VM pressure.. */
++#define PG_fs_1 16 /* Filesystem specific */
++
++#ifndef arch_set_page_uptodate
++#define arch_set_page_uptodate(page)
++#endif
++
++/* Make it prettier to test the above... */
++#define UnlockPage(page) unlock_page(page)
++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags)
++#define SetPageUptodate(page) \
++ do { \
++ arch_set_page_uptodate(page); \
++ set_bit(PG_uptodate, &(page)->flags); \
++ } while (0)
++#define ClearPageUptodate(page) clear_bit(PG_uptodate, &(page)->flags)
++#define PageDirty(page) test_bit(PG_dirty, &(page)->flags)
++#define SetPageDirty(page) set_bit(PG_dirty, &(page)->flags)
++#define ClearPageDirty(page) clear_bit(PG_dirty, &(page)->flags)
++#define PageLocked(page) test_bit(PG_locked, &(page)->flags)
++#define LockPage(page) set_bit(PG_locked, &(page)->flags)
++#define TryLockPage(page) test_and_set_bit(PG_locked, &(page)->flags)
++#define PageChecked(page) test_bit(PG_checked, &(page)->flags)
++#define SetPageChecked(page) set_bit(PG_checked, &(page)->flags)
++#define ClearPageChecked(page) clear_bit(PG_checked, &(page)->flags)
++#define PageLaunder(page) test_bit(PG_launder, &(page)->flags)
++#define SetPageLaunder(page) set_bit(PG_launder, &(page)->flags)
++#define ClearPageLaunder(page) clear_bit(PG_launder, &(page)->flags)
++#define ClearPageArch1(page) clear_bit(PG_arch_1, &(page)->flags)
++
++/*
++ * The zone field is never updated after free_area_init_core()
++ * sets it, so none of the operations on it need to be atomic.
++ */
++#define NODE_SHIFT 4
++#define ZONE_SHIFT (BITS_PER_LONG - 8)
++
++struct zone_struct;
++extern struct zone_struct *zone_table[];
++
++static inline zone_t *page_zone(struct page *page)
++{
++ return zone_table[page->flags >> ZONE_SHIFT];
++}
++
++static inline void set_page_zone(struct page *page, unsigned long zone_num)
++{
++ page->flags &= ~(~0UL << ZONE_SHIFT);
++ page->flags |= zone_num << ZONE_SHIFT;
++}
++
++/*
++ * In order to avoid #ifdefs within C code itself, we define
++ * set_page_address to a noop for non-highmem machines, where
++ * the field isn't useful.
++ * The same is true for page_address() in arch-dependent code.
++ */
++#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
++
++#define set_page_address(page, address) \
++ do { \
++ (page)->virtual = (address); \
++ } while(0)
++
++#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
++#define set_page_address(page, address) do { } while(0)
++#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
++
++/*
++ * Permanent address of a page. Obviously must never be
++ * called on a highmem page.
++ */
++#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
++
++#define page_address(page) ((page)->virtual)
++
++#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
++
++#define page_address(page) \
++ __va( (((page) - page_zone(page)->zone_mem_map) << PAGE_SHIFT) \
++ + page_zone(page)->zone_start_paddr)
++
++#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
++
++extern void FASTCALL(set_page_dirty(struct page *));
++
++/*
++ * The first mb is necessary to safely close the critical section opened by the
++ * TryLockPage(), the second mb is necessary to enforce ordering between
++ * the clear_bit and the read of the waitqueue (to avoid SMP races with a
++ * parallel wait_on_page).
++ */
++#define PageError(page) test_bit(PG_error, &(page)->flags)
++#define SetPageError(page) set_bit(PG_error, &(page)->flags)
++#define ClearPageError(page) clear_bit(PG_error, &(page)->flags)
++#define PageReferenced(page) test_bit(PG_referenced, &(page)->flags)
++#define SetPageReferenced(page) set_bit(PG_referenced, &(page)->flags)
++#define ClearPageReferenced(page) clear_bit(PG_referenced, &(page)->flags)
++#define PageTestandClearReferenced(page) test_and_clear_bit(PG_referenced, &(page)->flags)
++#define PageSlab(page) test_bit(PG_slab, &(page)->flags)
++#define PageSetSlab(page) set_bit(PG_slab, &(page)->flags)
++#define PageClearSlab(page) clear_bit(PG_slab, &(page)->flags)
++#define PageReserved(page) test_bit(PG_reserved, &(page)->flags)
++
++#define PageActive(page) test_bit(PG_active, &(page)->flags)
++#define SetPageActive(page) set_bit(PG_active, &(page)->flags)
++#define ClearPageActive(page) clear_bit(PG_active, &(page)->flags)
++
++#define PageLRU(page) test_bit(PG_lru, &(page)->flags)
++#define TestSetPageLRU(page) test_and_set_bit(PG_lru, &(page)->flags)
++#define TestClearPageLRU(page) test_and_clear_bit(PG_lru, &(page)->flags)
++
++#ifdef CONFIG_HIGHMEM
++#define PageHighMem(page) test_bit(PG_highmem, &(page)->flags)
++#else
++#define PageHighMem(page) 0 /* needed to optimize away at compile time */
++#endif
++
++#define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags)
++#define ClearPageReserved(page) clear_bit(PG_reserved, &(page)->flags)
++
++/*
++ * Error return values for the *_nopage functions
++ */
++#define NOPAGE_SIGBUS (NULL)
++#define NOPAGE_OOM ((struct page *) (-1))
++
++/* The array of struct pages */
++extern mem_map_t * mem_map;
++
++/*
++ * There is only one page-allocator function, and two main namespaces to
++ * it. The alloc_page*() variants return 'struct page *' and as such
++ * can allocate highmem pages, the *get*page*() variants return
++ * virtual kernel addresses to the allocated page(s).
++ */
++extern struct page * FASTCALL(_alloc_pages(unsigned int gfp_mask, unsigned int order));
++extern struct page * FASTCALL(__alloc_pages(unsigned int gfp_mask, unsigned int order, zonelist_t *zonelist));
++extern struct page * alloc_pages_node(int nid, unsigned int gfp_mask, unsigned int order);
++
++static inline struct page * alloc_pages(unsigned int gfp_mask, unsigned int order)
++{
++ /*
++ * Gets optimized away by the compiler.
++ */
++ if (order >= MAX_ORDER)
++ return NULL;
++ return _alloc_pages(gfp_mask, order);
++}
++
++#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
++
++extern unsigned long FASTCALL(__get_free_pages(unsigned int gfp_mask, unsigned int order));
++extern unsigned long FASTCALL(get_zeroed_page(unsigned int gfp_mask));
++
++#define __get_free_page(gfp_mask) \
++ __get_free_pages((gfp_mask),0)
++
++#define __get_dma_pages(gfp_mask, order) \
++ __get_free_pages((gfp_mask) | GFP_DMA,(order))
++
++/*
++ * The old interface name will be removed in 2.5:
++ */
++#define get_free_page get_zeroed_page
++
++/*
++ * There is only one 'core' page-freeing function.
++ */
++extern void FASTCALL(__free_pages(struct page *page, unsigned int order));
++extern void FASTCALL(free_pages(unsigned long addr, unsigned int order));
++
++#define __free_page(page) __free_pages((page), 0)
++#define free_page(addr) free_pages((addr),0)
++
++extern void show_free_areas(void);
++extern void show_free_areas_node(pg_data_t *pgdat);
++
++extern void clear_page_tables(struct mm_struct *, unsigned long, int);
++
++extern int fail_writepage(struct page *);
++struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int unused);
++struct file *shmem_file_setup(char * name, loff_t size);
++extern void shmem_lock(struct file * file, int lock);
++extern int shmem_zero_setup(struct vm_area_struct *);
++
++extern void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size);
++extern int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma);
++extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, pgprot_t prot);
++extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t prot);
++
++extern int vmtruncate(struct inode * inode, loff_t offset);
++extern pmd_t *FASTCALL(__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address));
++extern pte_t *FASTCALL(pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address));
++extern int handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address, int write_access);
++extern int make_pages_present(unsigned long addr, unsigned long end);
++extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
++extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char *dst, int len);
++extern int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long dst, int len);
++extern int ptrace_attach(struct task_struct *tsk);
++extern int ptrace_detach(struct task_struct *, unsigned int);
++extern void ptrace_disable(struct task_struct *);
++extern int ptrace_check_attach(struct task_struct *task, int kill);
++
++int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start,
++ int len, int write, int force, struct page **pages, struct vm_area_struct **vmas);
++
++/*
++ * On a two-level page table, this ends up being trivial. Thus the
++ * inlining and the symmetry break with pte_alloc() that does all
++ * of this out-of-line.
++ */
++static inline pmd_t *pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
++{
++ if (pgd_none(*pgd))
++ return __pmd_alloc(mm, pgd, address);
++ return pmd_offset(pgd, address);
++}
++
++extern int pgt_cache_water[2];
++extern int check_pgt_cache(void);
++
++extern void free_area_init(unsigned long * zones_size);
++extern void free_area_init_node(int nid, pg_data_t *pgdat, struct page *pmap,
++ unsigned long * zones_size, unsigned long zone_start_paddr,
++ unsigned long *zholes_size);
++extern void mem_init(void);
++extern void show_mem(void);
++extern void si_meminfo(struct sysinfo * val);
++extern void swapin_readahead(swp_entry_t);
++
++extern struct address_space swapper_space;
++#define PageSwapCache(page) ((page)->mapping == &swapper_space)
++
++static inline int is_page_cache_freeable(struct page * page)
++{
++ return page_count(page) - !!page->buffers == 1;
++}
++
++extern int FASTCALL(can_share_swap_page(struct page *));
++extern int FASTCALL(remove_exclusive_swap_page(struct page *));
++
++extern void __free_pte(pte_t);
++
++/* mmap.c */
++extern void lock_vma_mappings(struct vm_area_struct *);
++extern void unlock_vma_mappings(struct vm_area_struct *);
++extern void insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
++extern void __insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
++extern void build_mmap_rb(struct mm_struct *);
++extern void exit_mmap(struct mm_struct *);
++
++extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++
++extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
++ unsigned long len, unsigned long prot,
++ unsigned long flag, unsigned long pgoff);
++
++static inline unsigned long do_mmap(struct file *file, unsigned long addr,
++ unsigned long len, unsigned long prot,
++ unsigned long flag, unsigned long offset)
++{
++ unsigned long ret = -EINVAL;
++ if ((offset + PAGE_ALIGN(len)) < offset)
++ goto out;
++ if (!(offset & ~PAGE_MASK))
++ ret = do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT);
++out:
++ return ret;
++}
++
++extern int do_munmap(struct mm_struct *, unsigned long, size_t);
++
++extern unsigned long do_brk(unsigned long, unsigned long);
++extern unsigned long do_brk_locked(unsigned long, unsigned long);
++
++static inline void __vma_unlink(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev)
++{
++ prev->vm_next = vma->vm_next;
++ rb_erase(&vma->vm_rb, &mm->mm_rb);
++ if (mm->mmap_cache == vma)
++ mm->mmap_cache = prev;
++}
++
++static inline int can_vma_merge(struct vm_area_struct * vma, unsigned long vm_flags)
++{
++ if (!vma->vm_file && vma->vm_flags == vm_flags)
++ return 1;
++ else
++ return 0;
++}
++
++struct zone_t;
++/* filemap.c */
++extern void remove_inode_page(struct page *);
++extern unsigned long page_unuse(struct page *);
++extern void truncate_inode_pages(struct address_space *, loff_t);
++
++/* generic vm_area_ops exported for stackable file systems */
++extern int filemap_sync(struct vm_area_struct *, unsigned long, size_t, unsigned int);
++extern struct page *filemap_nopage(struct vm_area_struct *, unsigned long, int);
++
++/*
++ * GFP bitmasks..
++ */
++/* Zone modifiers in GFP_ZONEMASK (see linux/mmzone.h - low four bits) */
++#define __GFP_DMA 0x01
++#define __GFP_HIGHMEM 0x02
++
++/* Action modifiers - doesn't change the zoning */
++#define __GFP_WAIT 0x10 /* Can wait and reschedule? */
++#define __GFP_HIGH 0x20 /* Should access emergency pools? */
++#define __GFP_IO 0x40 /* Can start low memory physical IO? */
++#define __GFP_HIGHIO 0x80 /* Can start high mem physical IO? */
++#define __GFP_FS 0x100 /* Can call down to low-level FS? */
++
++#define GFP_NOHIGHIO (__GFP_HIGH | __GFP_WAIT | __GFP_IO)
++#define GFP_NOIO (__GFP_HIGH | __GFP_WAIT)
++#define GFP_NOFS (__GFP_HIGH | __GFP_WAIT | __GFP_IO | __GFP_HIGHIO)
++#define GFP_ATOMIC (__GFP_HIGH)
++#define GFP_USER ( __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS)
++#define GFP_HIGHUSER ( __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS | __GFP_HIGHMEM)
++#define GFP_KERNEL (__GFP_HIGH | __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS)
++#define GFP_NFS (__GFP_HIGH | __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS)
++#define GFP_KSWAPD ( __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS)
++
++/* Flag - indicates that the buffer will be suitable for DMA. Ignored on some
++ platforms, used as appropriate on others */
++
++#define GFP_DMA __GFP_DMA
++
++static inline unsigned int pf_gfp_mask(unsigned int gfp_mask)
++{
++ /* avoid all memory balancing I/O methods if this task cannot block on I/O */
++ if (current->flags & PF_NOIO)
++ gfp_mask &= ~(__GFP_IO | __GFP_HIGHIO | __GFP_FS);
++
++ return gfp_mask;
++}
++
++/* vma is the first one with address < vma->vm_end,
++ * and even address < vma->vm_start. Have to extend vma. */
++static inline int expand_stack(struct vm_area_struct * vma, unsigned long address)
++{
++ unsigned long grow;
++
++ /*
++ * vma->vm_start/vm_end cannot change under us because the caller
++ * is required to hold the mmap_sem in read mode. We need the
++ * page_table_lock lock to serialize against concurrent expand_stacks.
++ */
++ address &= PAGE_MASK;
++ spin_lock(&vma->vm_mm->page_table_lock);
++
++ /* already expanded while we were spinning? */
++ if (vma->vm_start <= address) {
++ spin_unlock(&vma->vm_mm->page_table_lock);
++ return 0;
++ }
++
++ grow = (vma->vm_start - address) >> PAGE_SHIFT;
++ if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur ||
++ ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur) {
++ spin_unlock(&vma->vm_mm->page_table_lock);
++ return -ENOMEM;
++ }
++
++ if ((vma->vm_flags & VM_LOCKED) &&
++ ((vma->vm_mm->locked_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_MEMLOCK].rlim_cur) {
++ spin_unlock(&vma->vm_mm->page_table_lock);
++ return -ENOMEM;
++ }
++
++
++ vma->vm_start = address;
++ vma->vm_pgoff -= grow;
++ vma->vm_mm->total_vm += grow;
++ if (vma->vm_flags & VM_LOCKED)
++ vma->vm_mm->locked_vm += grow;
++ spin_unlock(&vma->vm_mm->page_table_lock);
++ return 0;
++}
++
++/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
++extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
++extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr,
++ struct vm_area_struct **pprev);
++
++/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
++ NULL if none. Assume start_addr < end_addr. */
++static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
++{
++ struct vm_area_struct * vma = find_vma(mm,start_addr);
++
++ if (vma && end_addr <= vma->vm_start)
++ vma = NULL;
++ return vma;
++}
++
++extern struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr);
++
++extern struct page * vmalloc_to_page(void *addr);
++
++#endif /* __KERNEL__ */
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/linux/mtd/cfi.h kernel-source-2.4.27-8-arm-1/include/linux/mtd/cfi.h
+--- kernel-source-2.4.27-8/include/linux/mtd/cfi.h 2003-06-13 15:51:38.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/mtd/cfi.h 2005-02-18 17:48:45.000000000 +0000
+@@ -287,8 +287,10 @@
+ #define P_ID_AMD_STD 2
+ #define P_ID_INTEL_STD 3
+ #define P_ID_AMD_EXT 4
++#define P_ID_ST_ADV 32
+ #define P_ID_MITSUBISHI_STD 256
+ #define P_ID_MITSUBISHI_EXT 257
++#define P_ID_SST_PAGE 258
+ #define P_ID_RESERVED 65535
+
+
+diff -urN kernel-source-2.4.27-8/include/linux/pci.h kernel-source-2.4.27-8-arm-1/include/linux/pci.h
+--- kernel-source-2.4.27-8/include/linux/pci.h 2004-08-08 00:26:06.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/pci.h 2005-02-18 17:48:45.000000000 +0000
+@@ -413,6 +413,7 @@
+
+ char name[90]; /* device name */
+ char slot_name[8]; /* slot name */
++ u32 saved_state[16]; /* for saving the config space before suspend */
+ int active; /* ISAPnP: device is active */
+ int ro; /* ISAPnP: read only */
+ unsigned short regs; /* ISAPnP: supported registers */
+@@ -499,6 +500,8 @@
+
+ struct pbus_set_ranges_data
+ {
++ int found_vga:1;
++ int prefetch_valid:1;
+ unsigned long io_start, io_end;
+ unsigned long mem_start, mem_end;
+ unsigned long prefetch_start, prefetch_end;
+@@ -641,6 +644,8 @@
+ int pci_restore_state(struct pci_dev *dev, u32 *buffer);
+ int pci_set_power_state(struct pci_dev *dev, int state);
+ int pci_enable_wake(struct pci_dev *dev, u32 state, int enable);
++int pci_generic_suspend_save(struct pci_dev *pdev, u32 state);
++int pci_generic_resume_restore(struct pci_dev *pdev);
+
+ /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */
+
+diff -urN kernel-source-2.4.27-8/include/linux/pld/pld_epxa.h kernel-source-2.4.27-8-arm-1/include/linux/pld/pld_epxa.h
+--- kernel-source-2.4.27-8/include/linux/pld/pld_epxa.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/pld/pld_epxa.h 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,35 @@
++#ifndef __LINUX_EPXAPLD_H
++#define __LINUX_EPXAPLD_H
++
++/*
++ * linux/drivers/char/pld/epxapld.h
++ *
++ * Pld driver for Altera EPXA Excalibur devices
++ *
++ * 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: $
++ *
++ */
++#define PLD_IOC_MAGIC 'p'
++#if !defined(KERNEL) || defined(CONFIG_PLD_HOTSWAP)
++#define PLD_IOC_ADD_PLD_DEV _IOW(PLD_IOC_MAGIC, 0xa0, struct pldhs_dev_desc)
++#define PLD_IOC_REMOVE_PLD_DEVS _IO(PLD_IOC_MAGIC, 0xa1)
++#define PLD_IOC_SET_INT_MODE _IOW(PLD_IOC_MAGIC, 0xa2, int)
++#endif
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/linux/pld/pld_hotswap.h kernel-source-2.4.27-8-arm-1/include/linux/pld/pld_hotswap.h
+--- kernel-source-2.4.27-8/include/linux/pld/pld_hotswap.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/pld/pld_hotswap.h 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,87 @@
++#ifndef __LINUX_PLD_HOTSWAP_H
++#define __LINUX_PLD_HOTSWAP_H
++/*
++ * linux/drivers/char/pld/pld_hotswap.h
++ *
++ * Pld driver for Altera EPXA Excalibur devices
++ *
++ * 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: $
++ *
++ */
++
++/*
++ * The pld_hotswap ops contain the basic operation required for adding
++ * and removing devices from the system.
++ */
++
++struct pldhs_dev_info{
++ unsigned int size;
++ unsigned short vendor_id;
++ unsigned short product_id;
++ unsigned char fsize;
++ unsigned char nsize;
++ unsigned short pssize;
++ unsigned short cmdsize;
++ unsigned char irq;
++ unsigned char version;
++ unsigned int base_addr;
++ unsigned int reg_size;
++};
++
++struct pldhs_dev_desc{
++ struct pldhs_dev_info* info;
++ char* name;
++ void* data;
++};
++
++#include <linux/pld/pld_epxa.h>
++
++
++#ifdef __KERNEL__
++struct pld_hotswap_ops{
++ struct list_head list;
++ char* name;
++ int (*add_device)(struct pldhs_dev_info *dev_info, void* dev_ps_data);
++ int (*remove_devices)(void);
++ int (*proc_read)(char* buf,char** start,off_t offset,int count,int *eof,void *data);
++};
++
++/*
++ * These functions are called by the device drivers to register functions
++ * which should be called when devices are added and removed
++ */
++extern int pldhs_register_driver(struct pld_hotswap_ops *drv);
++extern int pldhs_unregister_driver(char *driver);
++
++/*
++ * These functions are called by the pld loader to add and remove
++ * device instances from the system. The call the functions regsistered
++ * the the particular deriver for this purpose
++ */
++extern int pldhs_add_device(struct pldhs_dev_info* dev_info,char *drv_name, void* dev_ps_data);
++extern int pldhs_remove_devices(void);
++
++/* Macro for formatting data to appear in the proc/pld file */
++#define PLDHS_READ_PROC_DATA(buf,name,index,base,irq,ps_string) \
++ sprintf(buf,":%s:%d Base Address, %#lx, IRQ, %d#\n%s\n",\
++ name,index,base,irq,ps_string)
++
++#endif
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/linux/serial.h kernel-source-2.4.27-8-arm-1/include/linux/serial.h
+--- kernel-source-2.4.27-8/include/linux/serial.h 2002-08-03 01:39:45.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/serial.h 2005-02-18 17:48:45.000000000 +0000
+@@ -48,7 +48,7 @@
+ unsigned char *iomem_base;
+ unsigned short iomem_reg_shift;
+ unsigned int port_high;
+- int reserved[1];
++ unsigned long iomap_base; /* cookie passed into ioremap */
+ };
+
+ /*
+diff -urN kernel-source-2.4.27-8/include/linux/serialP.h kernel-source-2.4.27-8-arm-1/include/linux/serialP.h
+--- kernel-source-2.4.27-8/include/linux/serialP.h 2002-08-03 01:39:45.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/serialP.h 2005-02-18 17:48:45.000000000 +0000
+@@ -157,8 +157,8 @@
+ struct pci_dev *dev;
+ };
+
+-extern int pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable);
+-extern int pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable);
++//extern int pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable);
++//extern int pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable);
+
+ #ifndef PCI_ANY_ID
+ #define PCI_ANY_ID (~0)
+diff -urN kernel-source-2.4.27-8/include/linux/serial_core.h kernel-source-2.4.27-8-arm-1/include/linux/serial_core.h
+--- kernel-source-2.4.27-8/include/linux/serial_core.h 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/serial_core.h 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,442 @@
++/*
++ * linux/drivers/char/serial_core.h
++ *
++ * 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_core.h,v 1.9.2.3 2001/11/26 22:32:45 rmk Exp $
++ */
++
++/*
++ * The type definitions. These are from Ted Ts'o's serial.h
++ */
++#define PORT_UNKNOWN 0
++#define PORT_8250 1
++#define PORT_16450 2
++#define PORT_16550 3
++#define PORT_16550A 4
++#define PORT_CIRRUS 5
++#define PORT_16650 6
++#define PORT_16650V2 7
++#define PORT_16750 8
++#define PORT_STARTECH 9
++#define PORT_16C950 10
++#define PORT_16654 11
++#define PORT_16850 12
++#define PORT_RSA 13
++#define PORT_NS16550A 14
++#define PORT_MAX_8250 14 /* max port ID */
++
++/*
++ * ARM specific type numbers. These are not currently guaranteed
++ * to be implemented, and will change in the future. These are
++ * separate so any additions to the old serial.c that occur before
++ * we are merged can be easily merged here.
++ */
++#define PORT_AMBA 32
++#define PORT_CLPS711X 33
++#define PORT_SA1100 34
++#define PORT_UART00 35
++#define PORT_21285 37
++#define PORT_S3C2410 40
++
++/* Sparc type numbers. */
++#define PORT_SUNZILOG 38
++#define PORT_SUNSAB 39
++
++/* NEC v850. */
++#define PORT_NB85E_UART 40
++
++/* NEC PC-9800 */
++#define PORT_8251_PC98 41
++#define PORT_19K_PC98 42
++#define PORT_FIFO_PC98 43
++#define PORT_VFAST_PC98 44
++#define PORT_PC9861 45
++#define PORT_PC9801_101 46
++
++/* DZ */
++#define PORT_DZ 47
++
++#define PORT_OMAHA 48
++#define PORT_AT91RM9200 49
++
++
++#ifdef __KERNEL__
++
++#include <linux/config.h>
++#include <linux/interrupt.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/spinlock.h>
++
++struct uart_port;
++struct uart_info;
++struct serial_struct;
++
++/*
++ * This structure describes all the operations that can be
++ * done on the physical hardware.
++ */
++struct uart_ops {
++ unsigned int (*tx_empty)(struct uart_port *);
++ void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
++ unsigned int (*get_mctrl)(struct uart_port *);
++ void (*stop_tx)(struct uart_port *, unsigned int tty_stop);
++ void (*start_tx)(struct uart_port *, unsigned int tty_start);
++ void (*send_xchar)(struct uart_port *, char ch);
++ void (*stop_rx)(struct uart_port *);
++ void (*enable_ms)(struct uart_port *);
++ void (*break_ctl)(struct uart_port *, int ctl);
++ int (*startup)(struct uart_port *);
++ void (*shutdown)(struct uart_port *);
++ void (*change_speed)(struct uart_port *, unsigned int cflag, unsigned int iflag, unsigned int quot);
++ void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate);
++ int (*set_wake)(struct uart_port *, unsigned int state);
++
++ /*
++ * Return a string describing the type of the port
++ */
++ const char *(*type)(struct uart_port *);
++
++ /*
++ * Release IO and memory resources used by the port.
++ * This includes iounmap if necessary.
++ */
++ void (*release_port)(struct uart_port *);
++
++ /*
++ * Request IO and memory resources used by the port.
++ * This includes iomapping the port if necessary.
++ */
++ int (*request_port)(struct uart_port *);
++ void (*config_port)(struct uart_port *, int);
++ int (*verify_port)(struct uart_port *, struct serial_struct *);
++ int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
++};
++
++#define UART_CONFIG_TYPE (1 << 0)
++#define UART_CONFIG_IRQ (1 << 1)
++
++struct uart_icount {
++ __u32 cts;
++ __u32 dsr;
++ __u32 rng;
++ __u32 dcd;
++ __u32 rx;
++ __u32 tx;
++ __u32 frame;
++ __u32 overrun;
++ __u32 parity;
++ __u32 brk;
++ __u32 buf_overrun;
++};
++
++struct uart_port {
++ spinlock_t lock; /* port lock */
++ unsigned int iobase; /* in/out[bwl] */
++ char *membase; /* read/write[bwl] */
++ unsigned char fifosize; /* tx fifo size */
++ unsigned char x_char; /* xon/xoff char */
++ unsigned char regshift; /* reg offset shift */
++ unsigned char iotype; /* io access style */
++
++#define UPIO_PORT (0)
++#define UPIO_HUB6 (1)
++#define UPIO_MEM (2)
++
++ unsigned int read_status_mask; /* driver specific */
++ unsigned int ignore_status_mask; /* driver specific */
++
++ struct uart_info *info; /* pointer to parent info */
++ struct uart_icount icount; /* statistics */
++
++ struct console *cons; /* struct console, if any */
++ unsigned long sysrq; /* sysrq timeout */
++
++ unsigned int flags;
++
++#define UPF_HUP_NOTIFY ASYNC_HUP_NOTIFY
++#define UPF_FOURPORT ASYNC_FOURPORT
++#define UPF_SAK ASYNC_SAK
++#define UPF_SPLIT_TERMIOS ASYNC_SPLIT_TERMIOS
++#define UPF_SPD_MASK (0x1030)
++#define UPF_SPD_HI (0x0010)
++#define UPF_SPD_VHI (0x0020)
++#define UPF_SPD_CUST (0x0030)
++#define UPF_SPD_SHI (0x1000)
++#define UPF_SPD_WARP (0x1010)
++#define UPF_SKIP_TEST ASYNC_SKIP_TEST
++#define UPF_AUTO_IRQ ASYNC_AUTO_IRQ
++#define UPF_SESSION_LOCKOUT ASYNC_SESSION_LOCKOUT
++#define UPF_PGRP_LOCKOUT ASYNCPGRP_LOCKOUT
++#define UPF_CALLOUT_NOHUP ASYNC_CALLOUT_NOHUP
++#define UPF_HARDPPS_CD ASYNC_HARDPPS_CD
++ /* 12 */
++#define UPF_LOW_LATENCY ASYNC_LOW_LATENCY
++#define UPF_BUGGY_UART ASYNC_BUGGY_UART
++#define UPF_AUTOPROBE ASYNC_AUTOPROBE
++#define UPF_MAGIC_MULTIPLIER (1 << 16)
++#define UPF_BOOT_ONLYMCA ASYNC_BOOT_ONLYMCA
++#define UPF_CONS_FLOW ASYNC_CONS_FLOW
++#define UPF_SHARE_IRQ ASYNC_SHARE_IRQ
++#define UPF_BOOT_AUTOCONF ASYNC_BOOT_AUTOCONF
++#define UPF_RESOURCES (1 << 30)
++#define UPF_IOREMAP (1 << 31)
++
++#define UPF_CHANGE_MASK (0x17fff)
++#define UPF_USR_MASK (UPF_SPD_MASK|UPF_LOW_LATENCY)
++
++ unsigned int mctrl; /* current modem ctrl settings */
++ unsigned int timeout; /* character-based timeout */
++ unsigned int type; /* port type */
++ struct uart_ops *ops;
++ unsigned int uartclk; /* base uart clock */
++ unsigned int custom_divisor;
++ unsigned int irq; /* irq number */
++ unsigned int line; /* port index */
++ unsigned long mapbase; /* for ioremap */
++ unsigned char hub6; /* this should be in the 8250 driver */
++ unsigned char unused[7];
++};
++
++/*
++ * This is the state information which is persistent across opens.
++ * The low level driver must not to touch any elements contained
++ * within.
++ */
++struct uart_state {
++ unsigned int close_delay;
++ unsigned int closing_wait;
++
++#define USF_CLOSING_WAIT_INF (0)
++#define USF_CLOSING_WAIT_NONE (65535)
++
++ struct termios normal_termios;
++ struct termios callout_termios;
++
++ int count;
++ struct uart_info *info;
++ struct uart_port *port;
++
++ struct semaphore sem;
++#ifdef CONFIG_PM
++ struct pm_dev *pm;
++#endif
++};
++
++#define UART_XMIT_SIZE 1024
++/*
++ * This is the state information which is only valid when the port
++ * is open; it may be freed by the core driver once the device has
++ * been closed. Either the low level driver or the core can modify
++ * stuff here.
++ */
++struct uart_info {
++ struct tty_struct *tty;
++ struct circ_buf xmit;
++ unsigned int flags;
++
++/*
++ * These are the flags that specific to info->flags, and reflect our
++ * internal state. They can not be accessed via port->flags. Low
++ * level drivers must not change these, but may query them instead.
++ */
++#define UIF_CHECK_CD ASYNC_CHECK_CD
++#define UIF_CTS_FLOW ASYNC_CTS_FLOW
++#define UIF_CALLOUT_ACTIVE ASYNC_CALLOUT_ACTIVE
++#define UIF_NORMAL_ACTIVE ASYNC_NORMAL_ACTIVE
++#define UIF_INITIALIZED ASYNC_INITIALIZED
++
++ unsigned char *tmpbuf;
++ struct semaphore tmpbuf_sem;
++
++ unsigned int driver_priv;
++ int blocked_open;
++ pid_t session;
++ pid_t pgrp;
++
++ struct tasklet_struct tlet;
++
++ wait_queue_head_t open_wait;
++ wait_queue_head_t delta_msr_wait;
++};
++
++/* number of characters left in xmit buffer before we ask for more */
++#define WAKEUP_CHARS 256
++
++struct module;
++struct tty_driver;
++
++struct uart_driver {
++ struct module *owner;
++ int normal_major;
++ const char *normal_name;
++ struct tty_driver *normal_driver;
++ int callout_major;
++ const char *callout_name;
++ struct tty_driver *callout_driver;
++ struct tty_struct **table;
++ struct termios **termios;
++ struct termios **termios_locked;
++ int minor;
++ int nr;
++ struct console *cons;
++
++ /*
++ * these are private; the low level driver should not
++ * touch these; they should be initialised to NULL
++ */
++ struct uart_state *state;
++};
++
++void uart_write_wakeup(struct uart_port *port);
++struct uart_port *uart_get_console(struct uart_port *ports, int nr,
++ struct console *c);
++void uart_parse_options(char *options, int *baud, int *parity, int *bits,
++ int *flow);
++int uart_set_options(struct uart_port *port, struct console *co, int baud,
++ int parity, int bits, int flow);
++
++/*
++ * Port/driver registration/removal
++ */
++int uart_register_driver(struct uart_driver *uart);
++void uart_unregister_driver(struct uart_driver *uart);
++void uart_unregister_port(struct uart_driver *reg, int line);
++int uart_register_port(struct uart_driver *reg, struct uart_port *port);
++int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
++int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
++
++#define uart_circ_empty(circ) ((circ)->head == (circ)->tail)
++#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0)
++
++#define uart_circ_chars_pending(circ) \
++ (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++#define uart_circ_chars_free(circ) \
++ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++#define uart_tx_stopped(port) \
++ ((port)->info->tty->stopped || (port)->info->tty->hw_stopped)
++
++/*
++ * The following are helper functions for the low level drivers.
++ */
++#ifdef SUPPORT_SYSRQ
++static inline int
++uart_handle_sysrq_char(struct uart_port *port, unsigned int ch,
++ struct pt_regs *regs)
++{
++ if (port->sysrq) {
++ if (ch && time_before(jiffies, port->sysrq)) {
++ handle_sysrq(ch, regs, NULL, NULL);
++ port->sysrq = 0;
++ return 1;
++ }
++ port->sysrq = 0;
++ }
++ return 0;
++}
++#else
++#define uart_handle_sysrq_char(port,ch,regs) (0)
++#endif
++
++/*
++ * We do the SysRQ and SAK checking like this...
++ */
++static inline int uart_handle_break(struct uart_port *port)
++{
++ struct uart_info *info = port->info;
++#ifdef SUPPORT_SYSRQ
++ if (port->cons && port->cons->index == port->line) {
++ if (!port->sysrq) {
++ port->sysrq = jiffies + HZ*5;
++ return 1;
++ }
++ port->sysrq = 0;
++ }
++#endif
++ if (port->flags & UPF_SAK)
++ do_SAK(info->tty);
++ return 0;
++}
++
++/**
++ * uart_handle_dcd_change - handle a change of carrier detect state
++ * @port: uart_port structure for the open port
++ * @status: new carrier detect status, nonzero if active
++ */
++static inline void
++uart_handle_dcd_change(struct uart_port *port, unsigned int status)
++{
++ struct uart_info *info = port->info;
++
++ port->icount.dcd++;
++
++#ifdef CONFIG_HARD_PPS
++ if ((port->flags & UPF_HARDPPS_CD) && status)
++ hardpps();
++#endif
++
++ if (info->flags & UIF_CHECK_CD) {
++ if (status)
++ wake_up_interruptible(&info->open_wait);
++ else if (!((info->flags & UIF_CALLOUT_ACTIVE) &&
++ (port->flags & UPF_CALLOUT_NOHUP))) {
++ if (info->tty)
++ tty_hangup(info->tty);
++ }
++ }
++}
++
++/**
++ * uart_handle_cts_change - handle a change of clear-to-send state
++ * @port: uart_port structure for the open port
++ * @status: new clear to send status, nonzero if active
++ */
++static inline void
++uart_handle_cts_change(struct uart_port *port, unsigned int status)
++{
++ struct uart_info *info = port->info;
++ struct tty_struct *tty = info->tty;
++
++ port->icount.cts++;
++
++ if (info->flags & UIF_CTS_FLOW) {
++ if (tty->hw_stopped) {
++ if (status) {
++ tty->hw_stopped = 0;
++ port->ops->start_tx(port, 0);
++ uart_write_wakeup(port);
++ }
++ } else {
++ if (!status) {
++ tty->hw_stopped = 1;
++ port->ops->stop_tx(port, 0);
++ }
++ }
++ }
++}
++
++/*
++ * UART_ENABLE_MS - determine if port should enable modem status irqs
++ */
++#define UART_ENABLE_MS(port,cflag) ((port)->flags & UPF_HARDPPS_CD || \
++ (cflag) & CRTSCTS || \
++ !((cflag) & CLOCAL))
++
++#endif
+diff -urN kernel-source-2.4.27-8/include/linux/serial_reg.h kernel-source-2.4.27-8-arm-1/include/linux/serial_reg.h
+--- kernel-source-2.4.27-8/include/linux/serial_reg.h 2001-05-02 00:05:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/include/linux/serial_reg.h 2005-02-18 17:48:45.000000000 +0000
+@@ -121,6 +121,7 @@
+ /*
+ * These are the definitions for the Modem Control Register
+ */
++#define UART_MCR_AFE 0x20 /* Enable auto-RTS/CTS (TI16C750) */
+ #define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
+ #define UART_MCR_OUT2 0x08 /* Out2 complement */
+ #define UART_MCR_OUT1 0x04 /* Out1 complement */
+diff -urN kernel-source-2.4.27-8/include/linux/zlib.h kernel-source-2.4.27-8-arm-1/include/linux/zlib.h
+--- kernel-source-2.4.27-8/include/linux/zlib.h 2002-11-28 23:53:15.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/include/linux/zlib.h 2005-02-18 17:48:45.000000000 +0000
+@@ -31,7 +31,7 @@
+ #ifndef _ZLIB_H
+ #define _ZLIB_H
+
+-#include <linux/zconf.h>
++#include "zconf.h"
+
+ #ifdef __cplusplus
+ extern "C" {
+diff -urN kernel-source-2.4.27-8/init/do_mounts.c kernel-source-2.4.27-8-arm-1/init/do_mounts.c
+--- kernel-source-2.4.27-8/init/do_mounts.c 2005-01-19 09:57:43.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/init/do_mounts.c 2005-02-18 17:48:45.000000000 +0000
+@@ -888,7 +888,7 @@
+ */
+ void prepare_namespace(void)
+ {
+- int is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
++ int is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR || MAJOR(ROOT_DEV) == 31;
+ #ifdef CONFIG_ALL_PPC
+ extern void arch_discover_root(void);
+ arch_discover_root();
+@@ -951,6 +951,7 @@
+ static unsigned inptr; /* index of next byte to be processed in inbuf */
+ static unsigned outcnt; /* bytes in output buffer */
+ static int exit_code;
++static int unzip_error;
+ static long bytes_out;
+ static int crd_infd, crd_outfd;
+
+@@ -998,13 +999,17 @@
+ /* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
++ * Returning -1 does not guarantee that gunzip() will ever return.
+ */
+ static int __init fill_inbuf(void)
+ {
+ if (exit_code) return -1;
+
+ insize = read(crd_infd, inbuf, INBUFSIZ);
+- if (insize == 0) return -1;
++ if (insize == 0) {
++ error("RAMDISK: ran out of compressed data\n");
++ return -1;
++ }
+
+ inptr = 1;
+
+@@ -1018,10 +1023,15 @@
+ static void __init flush_window(void)
+ {
+ ulg c = crc; /* temporary variable */
+- unsigned n;
++ unsigned n, written;
+ uch *in, ch;
+
+- write(crd_outfd, window, outcnt);
++ written = write(crd_outfd, window, outcnt);
++ if (written != outcnt && unzip_error == 0) {
++ printk(KERN_ERR "RAMDISK: incomplete write (%d != %d), "
++ "only wrote %ld\n", written, outcnt, bytes_out);
++ unzip_error = 1;
++ }
+ in = window;
+ for (n = 0; n < outcnt; n++) {
+ ch = *in++;
+@@ -1036,6 +1046,7 @@
+ {
+ printk(KERN_ERR "%s", x);
+ exit_code = 1;
++ unzip_error = 1;
+ }
+
+ static int __init crd_load(int in_fd, int out_fd)
+@@ -1064,6 +1075,8 @@
+ }
+ makecrc();
+ result = gunzip();
++ if (unzip_error)
++ result = 1;
+ kfree(inbuf);
+ kfree(window);
+ return result;
+diff -urN kernel-source-2.4.27-8/kernel/Makefile kernel-source-2.4.27-8-arm-1/kernel/Makefile
+--- kernel-source-2.4.27-8/kernel/Makefile 2005-01-19 09:57:51.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/kernel/Makefile 2005-02-18 17:48:45.000000000 +0000
+@@ -9,16 +9,18 @@
+
+ O_TARGET := kernel.o
+
+-export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o fork.o
++export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o fork.o cpufreq.o
+
+-obj-y = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
++obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
+ module.o exit.o itimer.o info.o time.o softirq.o resource.o \
+ sysctl.o acct.o capability.o ptrace.o timer.o user.o \
+ signal.o sys.o kmod.o context.o
+
++obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
+ obj-$(CONFIG_UID16) += uid16.o
+ obj-$(CONFIG_MODULES) += ksyms.o
+ obj-$(CONFIG_PM) += pm.o
++obj-$(CONFIG_CPU_FREQ) += cpufreq.o
+
+ ifneq ($(CONFIG_IA64),y)
+ # According to Alan Modra <alan at linuxcare.com.au>, the -fno-omit-frame-pointer is
+diff -urN kernel-source-2.4.27-8/kernel/cpufreq.c kernel-source-2.4.27-8-arm-1/kernel/cpufreq.c
+--- kernel-source-2.4.27-8/kernel/cpufreq.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/kernel/cpufreq.c 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,536 @@
++/*
++ * linux/kernel/cpufreq.c
++ *
++ * Copyright (C) 2001 Russell King
++ *
++ * $Id: cpufreq.c,v 1.17.2.2 2002/05/03 13:26:27 rmk 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.
++ *
++ * CPU speed changing core functionality. We provide the following
++ * services to the system:
++ * - notifier lists to inform other code of the freq change both
++ * before and after the freq change.
++ * - the ability to change the freq speed
++ *
++ * ** You'll need to add CTL_CPU = 10 to include/linux/sysctl.h if
++ * it's not already present.
++ *
++ * When this appears in the kernel, the sysctl enums will move to
++ * include/linux/sysctl.h
++ */
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/notifier.h>
++#include <linux/cpufreq.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/smp.h>
++#include <linux/fs.h>
++#include <linux/sysctl.h>
++
++#include <asm/semaphore.h>
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <linux/interrupt.h> /* requires system.h */
++
++/*
++ * This list is for kernel code that needs to handle
++ * changes to devices when the CPU clock speed changes.
++ */
++static struct notifier_block *cpufreq_notifier_list;
++static DECLARE_MUTEX_LOCKED(cpufreq_sem);
++static unsigned long cpufreq_ref_loops;
++static unsigned int cpufreq_ref_freq;
++static int cpufreq_initialised;
++static unsigned int (*cpufreq_validatespeed)(unsigned int);
++static void (*cpufreq_setspeed)(unsigned int);
++
++#ifndef CONFIG_SMP
++static unsigned int __cpufreq_max;
++static unsigned int __cpufreq_min;
++static unsigned int __cpufreq_cur;
++#endif
++
++static unsigned long scale(unsigned long ref, u_int div, u_int mult)
++{
++ unsigned long new_jiffy_l, new_jiffy_h;
++
++ /*
++ * Recalculate loops_per_jiffy. We do it this way to
++ * avoid math overflow on 32-bit machines. Maybe we
++ * should make this architecture dependent? If you have
++ * a better way of doing this, please replace!
++ *
++ * new = old * mult / div
++ */
++ new_jiffy_h = ref / div;
++ new_jiffy_l = (ref % div) / 100;
++ new_jiffy_h *= mult;
++ new_jiffy_l = new_jiffy_l * mult / div;
++
++ return new_jiffy_h + new_jiffy_l * 100;
++}
++
++/*
++ * cpufreq_max command line parameter. Use:
++ * cpufreq=59000-221000
++ * to set the CPU frequency to 59 to 221MHz.
++ */
++static int __init cpufreq_setup(char *str)
++{
++ unsigned int min, max;
++ int i;
++
++ min = 0;
++ max = simple_strtoul(str, &str, 0);
++ if (*str == '-') {
++ min = max;
++ max = simple_strtoul(str + 1, NULL, 0);
++ }
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ cpufreq_max(i) = max;
++ cpufreq_min(i) = min;
++ }
++
++ return 1;
++}
++
++__setup("cpufreq=", cpufreq_setup);
++
++/**
++ * cpufreq_register_notifier - register a driver with cpufreq
++ * @nb: notifier function to register
++ *
++ * Add a driver to the list of drivers that which to be notified about
++ * CPU clock rate changes. The driver will be called three times on
++ * clock change.
++ *
++ * This function may sleep, and has the same return conditions as
++ * notifier_chain_register.
++ */
++int cpufreq_register_notifier(struct notifier_block *nb)
++{
++ int ret;
++
++ down(&cpufreq_sem);
++ ret = notifier_chain_register(&cpufreq_notifier_list, nb);
++ up(&cpufreq_sem);
++
++ return ret;
++}
++
++EXPORT_SYMBOL(cpufreq_register_notifier);
++
++/**
++ * cpufreq_unregister_notifier - unregister a driver with cpufreq
++ * @nb: notifier block to be unregistered
++ *
++ * Remove a driver from the CPU frequency notifier lists.
++ *
++ * This function may sleep, and has the same return conditions as
++ * notifier_chain_unregister.
++ */
++int cpufreq_unregister_notifier(struct notifier_block *nb)
++{
++ int ret;
++
++ down(&cpufreq_sem);
++ ret = notifier_chain_unregister(&cpufreq_notifier_list, nb);
++ up(&cpufreq_sem);
++
++ return ret;
++}
++
++EXPORT_SYMBOL(cpufreq_unregister_notifier);
++
++/*
++ * This notifier alters the system "loops_per_jiffy" for the clock
++ * speed change. We ignore CPUFREQ_MINMAX here.
++ */
++static void adjust_jiffies(unsigned long val, struct cpufreq_info *ci)
++{
++ if ((val == CPUFREQ_PRECHANGE && ci->old_freq < ci->new_freq) ||
++ (val == CPUFREQ_POSTCHANGE && ci->old_freq > ci->new_freq))
++ loops_per_jiffy = scale(cpufreq_ref_loops, cpufreq_ref_freq,
++ ci->new_freq);
++}
++
++#ifdef CONFIG_PM
++/**
++ * cpufreq_restore - restore the CPU clock frequency after resume
++ *
++ * Restore the CPU clock frequency so that our idea of the current
++ * frequency reflects the actual hardware.
++ */
++int cpufreq_restore(void)
++{
++ unsigned long old_cpus;
++ int cpu = smp_processor_id();
++ int ret;
++
++ if (!cpufreq_initialised)
++ panic("cpufreq_restore() called before initialisation!");
++ if (in_interrupt())
++ panic("cpufreq_restore() called from interrupt context!");
++
++ /*
++ * Bind to the current CPU.
++ */
++ old_cpus = current->cpus_allowed;
++ current->cpus_allowed = 1UL << cpu_logical_map(cpu);
++
++ down(&cpufreq_sem);
++
++ ret = -ENXIO;
++ if (cpufreq_setspeed) {
++ cpufreq_setspeed(cpufreq_current(cpu));
++ ret = 0;
++ }
++
++ up(&cpufreq_sem);
++
++ current->cpus_allowed = old_cpus;
++
++ return ret;
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_restore);
++#endif
++
++/**
++ * cpu_setfreq - change the CPU clock frequency.
++ * @freq: frequency (in kHz) at which we should run.
++ *
++ * Set the CPU clock frequency, informing all registered users of
++ * the change. We bound the frequency according to the cpufreq_max
++ * command line parameter, and the parameters the registered users
++ * will allow.
++ *
++ * This function must be called from process context, and on the
++ * cpu that we wish to change the frequency of.
++ *
++ * We return 0 if successful. (we are currently always successful).
++ */
++int cpufreq_set(unsigned int freq)
++{
++ unsigned long old_cpus;
++ struct cpufreq_info clkinfo;
++ struct cpufreq_minmax minmax;
++ int cpu = smp_processor_id();
++ int ret;
++
++ if (!cpufreq_initialised)
++ panic("cpufreq_set() called before initialisation!");
++ if (in_interrupt())
++ panic("cpufreq_set() called from interrupt context!");
++
++ /*
++ * Bind to the current CPU.
++ */
++ old_cpus = current->cpus_allowed;
++ current->cpus_allowed = 1UL << cpu_logical_map(cpu);
++
++ down(&cpufreq_sem);
++ ret = -ENXIO;
++ if (!cpufreq_setspeed || !cpufreq_validatespeed)
++ goto out;
++
++ /*
++ * Don't allow the CPU to be clocked over the limit.
++ */
++ minmax.min_freq = cpufreq_min(cpu);
++ minmax.max_freq = cpufreq_max(cpu);
++ minmax.cur_freq = cpufreq_current(cpu);
++ minmax.new_freq = freq;
++
++ /*
++ * Find out what the registered devices will currently tolerate,
++ * and limit the requested clock rate to these values. Drivers
++ * must not rely on the 'new_freq' value - it is only a guide.
++ */
++ notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_MINMAX, &minmax);
++ if (freq < minmax.min_freq)
++ freq = minmax.min_freq;
++ if (freq > minmax.max_freq)
++ freq = minmax.max_freq;
++
++ /*
++ * Ask the CPU specific code to validate the speed. If the speed
++ * is not acceptable, make it acceptable. Current policy is to
++ * round the frequency down to the value the processor actually
++ * supports.
++ */
++ freq = cpufreq_validatespeed(freq);
++
++ if (cpufreq_current(cpu) != freq) {
++ clkinfo.old_freq = cpufreq_current(cpu);
++ clkinfo.new_freq = freq;
++
++ notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_PRECHANGE,
++ &clkinfo);
++
++ adjust_jiffies(CPUFREQ_PRECHANGE, &clkinfo);
++
++ /*
++ * Actually set the CPU frequency.
++ */
++ cpufreq_setspeed(freq);
++ cpufreq_current(cpu) = freq;
++ adjust_jiffies(CPUFREQ_POSTCHANGE, &clkinfo);
++
++ notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_POSTCHANGE,
++ &clkinfo);
++ }
++
++ ret = 0;
++
++ out:
++ up(&cpufreq_sem);
++
++ current->cpus_allowed = old_cpus;
++
++ return ret;
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_set);
++
++/**
++ * cpufreq_setmax - set the CPU to maximum frequency
++ *
++ * Sets the CPU this function is executed on to maximum frequency.
++ */
++int cpufreq_setmax(void)
++{
++ return cpufreq_set(cpufreq_max(smp_processor_id()));
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_setmax);
++
++/**
++ * cpufreq_get - return the current CPU clock frequency in 1kHz
++ * @cpu: cpu number to obtain frequency for
++ *
++ * Returns the specified CPUs frequency in kHz.
++ */
++unsigned int cpufreq_get(int cpu)
++{
++ if (!cpufreq_initialised)
++ panic("cpufreq_get() called before initialisation!");
++ return cpufreq_current(cpu);
++}
++
++EXPORT_SYMBOL(cpufreq_get);
++
++#ifdef CONFIG_SYSCTL
++
++static int
++cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
++ void *buffer, size_t *lenp)
++{
++ char buf[16], *p;
++ int cpu = 0, len, left = *lenp;
++
++ if (!left || (filp->f_pos && !write)) {
++ *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);
++ } 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 = 0;
++
++ 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);
++ }
++ return 1;
++}
++
++enum {
++ CPU_NR_FREQ_MAX = 1,
++ CPU_NR_FREQ_MIN = 2,
++ CPU_NR_FREQ = 3
++};
++
++static ctl_table ctl_cpu_vars[4] = {
++ {
++ ctl_name: CPU_NR_FREQ_MAX,
++ procname: "speed-max",
++ data: &cpufreq_max(0),
++ maxlen: sizeof(cpufreq_max(0)),
++ mode: 0444,
++ proc_handler: proc_dointvec,
++ },
++ {
++ ctl_name: CPU_NR_FREQ_MIN,
++ procname: "speed-min",
++ data: &cpufreq_min(0),
++ maxlen: sizeof(cpufreq_min(0)),
++ mode: 0444,
++ proc_handler: proc_dointvec,
++ },
++ {
++ ctl_name: CPU_NR_FREQ,
++ procname: "speed",
++ mode: 0644,
++ proc_handler: cpufreq_procctl,
++ strategy: cpufreq_sysctl,
++ },
++ {
++ ctl_name: 0,
++ }
++};
++
++enum {
++ CPU_NR = 1,
++};
++
++static ctl_table ctl_cpu_nr[2] = {
++ {
++ ctl_name: CPU_NR,
++ procname: "0",
++ mode: 0555,
++ child: ctl_cpu_vars,
++ },
++ {
++ ctl_name: 0,
++ }
++};
++
++static ctl_table ctl_cpu[2] = {
++ {
++ ctl_name: CTL_CPU,
++ procname: "cpu",
++ mode: 0555,
++ child: ctl_cpu_nr,
++ },
++ {
++ ctl_name: 0,
++ }
++};
++
++static inline void cpufreq_sysctl_init(void)
++{
++ register_sysctl_table(ctl_cpu, 0);
++}
++
++#else
++#define cpufreq_sysctl_init()
++#endif
++
++/**
++ * cpufreq_setfunctions - Set CPU clock functions
++ * @validate: pointer to validation function
++ * @setspeed: pointer to setspeed function
++ */
++void
++cpufreq_setfunctions(unsigned int (*validate)(unsigned int),
++ void (*setspeed)(unsigned int))
++{
++ down(&cpufreq_sem);
++ cpufreq_validatespeed = validate;
++ cpufreq_setspeed = setspeed;
++ up(&cpufreq_sem);
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_setfunctions);
++
++/**
++ * cpufreq_init - Initialise the cpufreq core
++ * @freq: current CPU clock speed.
++ * @min_freq: minimum CPU clock speed.
++ * @max_freq: maximum CPU clock speed.
++ *
++ * Initialise the cpufreq core. If the cpufreq_max command line
++ * parameter has not been specified, we set the maximum clock rate
++ * to the current CPU clock rate.
++ */
++void cpufreq_init(unsigned int freq,
++ unsigned int min_freq,
++ unsigned int max_freq)
++{
++ /*
++ * If the user doesn't tell us their maximum frequency,
++ * or if it is invalid, use the values determined
++ * by the cpufreq-arch-specific initialization functions.
++ * The validatespeed code is responsible for limiting
++ * this further.
++ */
++ if (max_freq && ((cpufreq_max(0) == 0) || (cpufreq_max(0) > max_freq)))
++ cpufreq_max(0) = max_freq;
++ if (min_freq && ((cpufreq_min(0) == 0) || (cpufreq_min(0) < min_freq)))
++ cpufreq_min(0) = min_freq;
++
++ if (cpufreq_max(0) == 0)
++ cpufreq_max(0) = freq;
++
++ printk(KERN_INFO "CPU clock: %d.%03d MHz (%d.%03d-%d.%03d MHz)\n",
++ freq / 1000, freq % 1000,
++ cpufreq_min(0) / 1000, cpufreq_min(0) % 1000,
++ cpufreq_max(0) / 1000, cpufreq_max(0) % 1000);
++
++ cpufreq_ref_loops = loops_per_jiffy;
++ cpufreq_ref_freq = freq;
++ cpufreq_current(smp_processor_id()) = freq;
++
++ cpufreq_initialised = 1;
++ up(&cpufreq_sem);
++
++ cpufreq_sysctl_init();
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_init);
+diff -urN kernel-source-2.4.27-8/kernel/exit.c kernel-source-2.4.27-8-arm-1/kernel/exit.c
+--- kernel-source-2.4.27-8/kernel/exit.c 2002-11-28 23:53:15.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/kernel/exit.c 2005-02-18 17:48:45.000000000 +0000
+@@ -587,7 +587,7 @@
+ return retval;
+ }
+
+-#if !defined(__alpha__) && !defined(__ia64__)
++#if !defined(__alpha__) && !defined(__ia64__) && !defined(__arm__)
+
+ /*
+ * sys_waitpid() remains for compatibility. waitpid() should be
+diff -urN kernel-source-2.4.27-8/kernel/fork.c kernel-source-2.4.27-8-arm-1/kernel/fork.c
+--- kernel-source-2.4.27-8/kernel/fork.c 2005-01-19 09:57:51.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/kernel/fork.c 2005-02-18 17:48:45.000000000 +0000
+@@ -75,7 +75,11 @@
+ * value: the thread structures can take up at most half
+ * of memory.
+ */
++#if THREAD_SIZE > PAGE_SIZE
+ max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8;
++#else
++ max_threads = (mempages * PAGE_SIZE) / (8 * THREAD_SIZE);
++#endif
+
+ init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
+ init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
+@@ -220,6 +224,7 @@
+
+ fail_nomem:
+ flush_tlb_mm(current->mm);
++ memc_update_mm(mm);
+ return retval;
+ }
+
+diff -urN kernel-source-2.4.27-8/kernel/ksyms.c kernel-source-2.4.27-8-arm-1/kernel/ksyms.c
+--- kernel-source-2.4.27-8/kernel/ksyms.c 2005-01-19 09:57:58.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/kernel/ksyms.c 2005-02-18 17:48:45.000000000 +0000
+@@ -359,6 +359,7 @@
+
+ /* tty routines */
+ EXPORT_SYMBOL(tty_hangup);
++EXPORT_SYMBOL(tty_vhangup);
+ EXPORT_SYMBOL(tty_wait_until_sent);
+ EXPORT_SYMBOL(tty_check_change);
+ EXPORT_SYMBOL(tty_hung_up_p);
+@@ -453,9 +454,11 @@
+ EXPORT_SYMBOL(kiobuf_wait_for_io);
+
+ /* dma handling */
++#ifdef CONFIG_GENERIC_ISA_DMA
+ EXPORT_SYMBOL(request_dma);
+ EXPORT_SYMBOL(free_dma);
+ EXPORT_SYMBOL(dma_spin_lock);
++#endif
+ #ifdef HAVE_DISABLE_HLT
+ EXPORT_SYMBOL(disable_hlt);
+ EXPORT_SYMBOL(enable_hlt);
+diff -urN kernel-source-2.4.27-8/kernel/ksyms.c.orig kernel-source-2.4.27-8-arm-1/kernel/ksyms.c.orig
+--- kernel-source-2.4.27-8/kernel/ksyms.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/kernel/ksyms.c.orig 2005-01-19 09:57:58.000000000 +0000
+@@ -0,0 +1,638 @@
++/*
++ * Herein lies all the functions/variables that are "exported" for linkage
++ * with dynamically loaded kernel modules.
++ * Jon.
++ *
++ * - Stacked module support and unified symbol table added (June 1994)
++ * - External symbol table support added (December 1994)
++ * - Versions on symbols added (December 1994)
++ * by Bjorn Ekwall <bj0rn at blox.se>
++ */
++
++#include <linux/config.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/blkdev.h>
++#include <linux/cdrom.h>
++#include <linux/kernel_stat.h>
++#include <linux/vmalloc.h>
++#include <linux/sys.h>
++#include <linux/utsname.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/serial.h>
++#include <linux/locks.h>
++#include <linux/delay.h>
++#include <linux/random.h>
++#include <linux/reboot.h>
++#include <linux/pagemap.h>
++#include <linux/sysctl.h>
++#include <linux/hdreg.h>
++#include <linux/skbuff.h>
++#include <linux/genhd.h>
++#include <linux/blkpg.h>
++#include <linux/swap.h>
++#include <linux/cache_def.h>
++#include <linux/ctype.h>
++#include <linux/file.h>
++#include <linux/iobuf.h>
++#include <linux/console.h>
++#include <linux/poll.h>
++#include <linux/mmzone.h>
++#include <linux/mm.h>
++#include <linux/capability.h>
++#include <linux/highuid.h>
++#include <linux/brlock.h>
++#include <linux/fs.h>
++#include <linux/tty.h>
++#include <linux/in6.h>
++#include <linux/completion.h>
++#include <linux/seq_file.h>
++#include <linux/dnotify.h>
++#include <linux/crc32.h>
++#include <linux/firmware.h>
++#include <asm/checksum.h>
++
++#if defined(CONFIG_PROC_FS)
++#include <linux/proc_fs.h>
++#endif
++#ifdef CONFIG_KMOD
++#include <linux/kmod.h>
++#endif
++#ifdef CONFIG_BIGPHYS_AREA
++#include <linux/bigphysarea.h>
++#endif
++
++extern void set_device_ro(kdev_t dev,int flag);
++
++extern void *sys_call_table;
++
++extern struct timezone sys_tz;
++extern int request_dma(unsigned int dmanr, char * deviceID);
++extern void free_dma(unsigned int dmanr);
++extern spinlock_t dma_spin_lock;
++extern int panic_timeout;
++
++#ifdef CONFIG_MODVERSIONS
++const struct module_symbol __export_Using_Versions
++__attribute__((section("__ksymtab"))) = {
++ 1 /* Version version */, "Using_Versions"
++};
++#endif
++
++
++EXPORT_SYMBOL(inter_module_register);
++EXPORT_SYMBOL(inter_module_unregister);
++EXPORT_SYMBOL(inter_module_get);
++EXPORT_SYMBOL(inter_module_get_request);
++EXPORT_SYMBOL(inter_module_put);
++EXPORT_SYMBOL(try_inc_mod_count);
++
++/* process memory management */
++EXPORT_SYMBOL(do_mmap_pgoff);
++EXPORT_SYMBOL(do_munmap);
++EXPORT_SYMBOL(do_brk);
++EXPORT_SYMBOL(do_brk_locked);
++EXPORT_SYMBOL(exit_mm);
++EXPORT_SYMBOL(exit_files);
++EXPORT_SYMBOL(exit_fs);
++EXPORT_SYMBOL(exit_sighand);
++
++/* internal kernel memory management */
++EXPORT_SYMBOL(_alloc_pages);
++EXPORT_SYMBOL(__alloc_pages);
++EXPORT_SYMBOL(alloc_pages_node);
++EXPORT_SYMBOL(__get_free_pages);
++EXPORT_SYMBOL(get_zeroed_page);
++EXPORT_SYMBOL(__free_pages);
++EXPORT_SYMBOL(free_pages);
++EXPORT_SYMBOL(num_physpages);
++EXPORT_SYMBOL(kmem_find_general_cachep);
++EXPORT_SYMBOL(kmem_cache_create);
++EXPORT_SYMBOL(kmem_cache_destroy);
++EXPORT_SYMBOL(kmem_cache_shrink);
++EXPORT_SYMBOL(kmem_cache_alloc);
++EXPORT_SYMBOL(kmem_cache_free);
++EXPORT_SYMBOL(kmem_cache_size);
++EXPORT_SYMBOL(kmalloc);
++EXPORT_SYMBOL(kfree);
++EXPORT_SYMBOL(vfree);
++EXPORT_SYMBOL(__vmalloc);
++EXPORT_SYMBOL(vmap);
++EXPORT_SYMBOL(vmalloc_to_page);
++EXPORT_SYMBOL(vcalloc);
++EXPORT_SYMBOL(mem_map);
++EXPORT_SYMBOL(remap_page_range);
++EXPORT_SYMBOL(max_mapnr);
++EXPORT_SYMBOL(high_memory);
++EXPORT_SYMBOL(vmtruncate);
++EXPORT_SYMBOL(find_vma);
++EXPORT_SYMBOL(get_unmapped_area);
++EXPORT_SYMBOL(init_mm);
++#ifdef CONFIG_HIGHMEM
++EXPORT_SYMBOL(kmap_high);
++EXPORT_SYMBOL(kunmap_high);
++EXPORT_SYMBOL(highmem_start_page);
++EXPORT_SYMBOL(create_bounce);
++EXPORT_SYMBOL(kmap_prot);
++EXPORT_SYMBOL(kmap_pte);
++#endif
++#ifdef CONFIG_BIGPHYS_AREA
++EXPORT_SYMBOL(bigphysarea_alloc);
++EXPORT_SYMBOL(bigphysarea_free);
++EXPORT_SYMBOL(bigphysarea_alloc_pages);
++EXPORT_SYMBOL(bigphysarea_free_pages);
++#endif
++
++/* filesystem internal functions */
++EXPORT_SYMBOL(def_blk_fops);
++EXPORT_SYMBOL(update_atime);
++EXPORT_SYMBOL(get_fs_type);
++EXPORT_SYMBOL(get_super);
++EXPORT_SYMBOL(drop_super);
++EXPORT_SYMBOL(getname);
++EXPORT_SYMBOL(names_cachep);
++EXPORT_SYMBOL(fput);
++EXPORT_SYMBOL(fget);
++EXPORT_SYMBOL(igrab);
++EXPORT_SYMBOL(iunique);
++EXPORT_SYMBOL(ilookup);
++EXPORT_SYMBOL(iget4_locked);
++EXPORT_SYMBOL(unlock_new_inode);
++EXPORT_SYMBOL(iput);
++EXPORT_SYMBOL(inode_init_once);
++EXPORT_SYMBOL(__inode_init_once);
++EXPORT_SYMBOL(force_delete);
++EXPORT_SYMBOL(follow_up);
++EXPORT_SYMBOL(follow_down);
++EXPORT_SYMBOL(lookup_mnt);
++EXPORT_SYMBOL(path_init);
++EXPORT_SYMBOL(path_walk);
++EXPORT_SYMBOL(path_lookup);
++EXPORT_SYMBOL(path_release);
++EXPORT_SYMBOL(__user_walk);
++EXPORT_SYMBOL(lookup_one_len);
++EXPORT_SYMBOL(lookup_hash);
++EXPORT_SYMBOL(sys_close);
++EXPORT_SYMBOL(dcache_lock);
++EXPORT_SYMBOL(d_alloc_root);
++EXPORT_SYMBOL(d_delete);
++EXPORT_SYMBOL(dget_locked);
++EXPORT_SYMBOL(d_validate);
++EXPORT_SYMBOL(d_rehash);
++EXPORT_SYMBOL(d_invalidate); /* May be it will be better in dcache.h? */
++EXPORT_SYMBOL(d_move);
++EXPORT_SYMBOL(d_instantiate);
++EXPORT_SYMBOL(d_alloc);
++EXPORT_SYMBOL(d_lookup);
++EXPORT_SYMBOL(__d_path);
++EXPORT_SYMBOL(mark_buffer_dirty);
++EXPORT_SYMBOL(set_buffer_async_io); /* for reiserfs_writepage */
++EXPORT_SYMBOL(end_buffer_io_async);
++EXPORT_SYMBOL(__mark_buffer_dirty);
++EXPORT_SYMBOL(__mark_inode_dirty);
++EXPORT_SYMBOL(fd_install);
++EXPORT_SYMBOL(get_empty_filp);
++EXPORT_SYMBOL(init_private_file);
++EXPORT_SYMBOL(filp_open);
++EXPORT_SYMBOL(filp_close);
++EXPORT_SYMBOL(put_filp);
++EXPORT_SYMBOL(files_lock);
++EXPORT_SYMBOL(check_disk_change);
++EXPORT_SYMBOL(__invalidate_buffers);
++EXPORT_SYMBOL(invalidate_bdev);
++EXPORT_SYMBOL(invalidate_inodes);
++EXPORT_SYMBOL(invalidate_device);
++EXPORT_SYMBOL(invalidate_inode_pages);
++EXPORT_SYMBOL(truncate_inode_pages);
++EXPORT_SYMBOL(fsync_dev);
++EXPORT_SYMBOL(fsync_dev_lockfs);
++EXPORT_SYMBOL(unlockfs);
++EXPORT_SYMBOL(fsync_no_super);
++EXPORT_SYMBOL(permission);
++EXPORT_SYMBOL(vfs_permission);
++EXPORT_SYMBOL(inode_setattr);
++EXPORT_SYMBOL(inode_change_ok);
++EXPORT_SYMBOL(write_inode_now);
++EXPORT_SYMBOL(notify_change);
++EXPORT_SYMBOL(set_blocksize);
++EXPORT_SYMBOL(sb_set_blocksize);
++EXPORT_SYMBOL(sb_min_blocksize);
++EXPORT_SYMBOL(getblk);
++EXPORT_SYMBOL(cdget);
++EXPORT_SYMBOL(cdput);
++EXPORT_SYMBOL(bdget);
++EXPORT_SYMBOL(bdput);
++EXPORT_SYMBOL(bread);
++EXPORT_SYMBOL(__brelse);
++EXPORT_SYMBOL(__bforget);
++EXPORT_SYMBOL(ll_rw_block);
++EXPORT_SYMBOL(submit_bh);
++EXPORT_SYMBOL(unlock_buffer);
++EXPORT_SYMBOL(__wait_on_buffer);
++EXPORT_SYMBOL(___wait_on_page);
++EXPORT_SYMBOL(generic_direct_IO);
++EXPORT_SYMBOL(discard_bh_page);
++EXPORT_SYMBOL(block_write_full_page);
++EXPORT_SYMBOL(block_read_full_page);
++EXPORT_SYMBOL(block_prepare_write);
++EXPORT_SYMBOL(block_sync_page);
++EXPORT_SYMBOL(generic_cont_expand);
++EXPORT_SYMBOL(cont_prepare_write);
++EXPORT_SYMBOL(generic_commit_write);
++EXPORT_SYMBOL(block_truncate_page);
++EXPORT_SYMBOL(generic_block_bmap);
++EXPORT_SYMBOL(generic_file_read);
++EXPORT_SYMBOL(do_generic_file_read);
++EXPORT_SYMBOL(do_generic_file_write);
++EXPORT_SYMBOL(do_generic_direct_read);
++EXPORT_SYMBOL(do_generic_direct_write);
++EXPORT_SYMBOL(generic_file_write);
++EXPORT_SYMBOL(generic_file_mmap);
++EXPORT_SYMBOL(generic_ro_fops);
++EXPORT_SYMBOL(generic_buffer_fdatasync);
++EXPORT_SYMBOL(page_hash_bits);
++EXPORT_SYMBOL(page_hash_table);
++EXPORT_SYMBOL(file_lock_list);
++EXPORT_SYMBOL(locks_init_lock);
++EXPORT_SYMBOL(locks_copy_lock);
++EXPORT_SYMBOL(posix_lock_file);
++EXPORT_SYMBOL(posix_test_lock);
++EXPORT_SYMBOL(posix_block_lock);
++EXPORT_SYMBOL(posix_unblock_lock);
++EXPORT_SYMBOL(posix_locks_deadlock);
++EXPORT_SYMBOL(locks_mandatory_area);
++EXPORT_SYMBOL(dput);
++EXPORT_SYMBOL(have_submounts);
++EXPORT_SYMBOL(d_find_alias);
++EXPORT_SYMBOL(d_prune_aliases);
++EXPORT_SYMBOL(prune_dcache);
++EXPORT_SYMBOL(shrink_dcache_sb);
++EXPORT_SYMBOL(shrink_dcache_parent);
++EXPORT_SYMBOL(find_inode_number);
++EXPORT_SYMBOL(is_subdir);
++EXPORT_SYMBOL(get_unused_fd);
++EXPORT_SYMBOL(put_unused_fd);
++EXPORT_SYMBOL(vfs_create);
++EXPORT_SYMBOL(vfs_mkdir);
++EXPORT_SYMBOL(vfs_mknod);
++EXPORT_SYMBOL(vfs_symlink);
++EXPORT_SYMBOL(vfs_link);
++EXPORT_SYMBOL(vfs_rmdir);
++EXPORT_SYMBOL(vfs_unlink);
++EXPORT_SYMBOL(vfs_rename);
++EXPORT_SYMBOL(vfs_statfs);
++EXPORT_SYMBOL(generic_read_dir);
++EXPORT_SYMBOL(generic_file_llseek);
++EXPORT_SYMBOL(no_llseek);
++EXPORT_SYMBOL(__pollwait);
++EXPORT_SYMBOL(poll_freewait);
++EXPORT_SYMBOL(ROOT_DEV);
++EXPORT_SYMBOL(__find_get_page);
++EXPORT_SYMBOL(__find_lock_page);
++EXPORT_SYMBOL(find_trylock_page);
++EXPORT_SYMBOL(find_or_create_page);
++EXPORT_SYMBOL(grab_cache_page_nowait);
++EXPORT_SYMBOL(read_cache_page);
++EXPORT_SYMBOL(set_page_dirty);
++EXPORT_SYMBOL(mark_page_accessed);
++EXPORT_SYMBOL(vfs_readlink);
++EXPORT_SYMBOL(vfs_follow_link);
++EXPORT_SYMBOL(page_readlink);
++EXPORT_SYMBOL(page_follow_link);
++EXPORT_SYMBOL(page_symlink_inode_operations);
++EXPORT_SYMBOL(block_symlink);
++EXPORT_SYMBOL(vfs_readdir);
++EXPORT_SYMBOL(__get_lease);
++EXPORT_SYMBOL(lease_get_mtime);
++EXPORT_SYMBOL(lock_may_read);
++EXPORT_SYMBOL(lock_may_write);
++EXPORT_SYMBOL(dcache_dir_open);
++EXPORT_SYMBOL(dcache_dir_close);
++EXPORT_SYMBOL(dcache_dir_lseek);
++EXPORT_SYMBOL(dcache_dir_fsync);
++EXPORT_SYMBOL(dcache_readdir);
++EXPORT_SYMBOL(dcache_dir_ops);
++
++/* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */
++EXPORT_SYMBOL(default_llseek);
++EXPORT_SYMBOL(dentry_open);
++EXPORT_SYMBOL(filemap_nopage);
++EXPORT_SYMBOL(filemap_sync);
++EXPORT_SYMBOL(filemap_fdatawrite);
++EXPORT_SYMBOL(filemap_fdatasync);
++EXPORT_SYMBOL(filemap_fdatawait);
++EXPORT_SYMBOL(lock_page);
++EXPORT_SYMBOL(unlock_page);
++EXPORT_SYMBOL(wakeup_page_waiters);
++
++/* device registration */
++EXPORT_SYMBOL(register_chrdev);
++EXPORT_SYMBOL(unregister_chrdev);
++EXPORT_SYMBOL(register_blkdev);
++EXPORT_SYMBOL(unregister_blkdev);
++EXPORT_SYMBOL(tty_register_driver);
++EXPORT_SYMBOL(tty_unregister_driver);
++EXPORT_SYMBOL(tty_std_termios);
++
++/* block device driver support */
++EXPORT_SYMBOL(blksize_size);
++EXPORT_SYMBOL(hardsect_size);
++EXPORT_SYMBOL(blk_size);
++EXPORT_SYMBOL(blk_dev);
++EXPORT_SYMBOL(is_read_only);
++EXPORT_SYMBOL(set_device_ro);
++EXPORT_SYMBOL(bmap);
++EXPORT_SYMBOL(sync_dev);
++EXPORT_SYMBOL(devfs_register_partitions);
++EXPORT_SYMBOL(blkdev_open);
++EXPORT_SYMBOL(blkdev_get);
++EXPORT_SYMBOL(blkdev_put);
++EXPORT_SYMBOL(ioctl_by_bdev);
++EXPORT_SYMBOL(grok_partitions);
++EXPORT_SYMBOL(register_disk);
++EXPORT_SYMBOL(tq_disk);
++EXPORT_SYMBOL(init_buffer);
++EXPORT_SYMBOL(refile_buffer);
++EXPORT_SYMBOL(max_sectors);
++EXPORT_SYMBOL(max_readahead);
++
++/* tty routines */
++EXPORT_SYMBOL(tty_hangup);
++EXPORT_SYMBOL(tty_wait_until_sent);
++EXPORT_SYMBOL(tty_check_change);
++EXPORT_SYMBOL(tty_hung_up_p);
++EXPORT_SYMBOL(tty_flip_buffer_push);
++EXPORT_SYMBOL(tty_get_baud_rate);
++EXPORT_SYMBOL(do_SAK);
++
++/* filesystem registration */
++EXPORT_SYMBOL(register_filesystem);
++EXPORT_SYMBOL(unregister_filesystem);
++EXPORT_SYMBOL(kern_mount);
++EXPORT_SYMBOL(__mntput);
++EXPORT_SYMBOL(may_umount);
++
++/* executable format registration */
++EXPORT_SYMBOL(register_binfmt);
++EXPORT_SYMBOL(unregister_binfmt);
++EXPORT_SYMBOL(search_binary_handler);
++EXPORT_SYMBOL(prepare_binprm);
++EXPORT_SYMBOL(compute_creds);
++EXPORT_SYMBOL(remove_arg_zero);
++EXPORT_SYMBOL(set_binfmt);
++
++/* sysctl table registration */
++EXPORT_SYMBOL(register_sysctl_table);
++EXPORT_SYMBOL(unregister_sysctl_table);
++EXPORT_SYMBOL(sysctl_string);
++EXPORT_SYMBOL(sysctl_intvec);
++EXPORT_SYMBOL(sysctl_jiffies);
++EXPORT_SYMBOL(proc_dostring);
++EXPORT_SYMBOL(proc_dointvec);
++EXPORT_SYMBOL(proc_dointvec_jiffies);
++EXPORT_SYMBOL(proc_dointvec_minmax);
++EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
++EXPORT_SYMBOL(proc_doulongvec_minmax);
++
++/* interrupt handling */
++EXPORT_SYMBOL(add_timer);
++EXPORT_SYMBOL(del_timer);
++EXPORT_SYMBOL(request_irq);
++EXPORT_SYMBOL(free_irq);
++#if !defined(CONFIG_IA64) /* irq_stat is part of struct cpuinfo_ia64 */
++EXPORT_SYMBOL(irq_stat);
++#endif
++
++/* waitqueue handling */
++EXPORT_SYMBOL(add_wait_queue);
++EXPORT_SYMBOL(add_wait_queue_exclusive);
++EXPORT_SYMBOL(remove_wait_queue);
++
++/* completion handling */
++EXPORT_SYMBOL(wait_for_completion);
++EXPORT_SYMBOL(complete);
++
++/* The notion of irq probe/assignment is foreign to S/390 */
++
++#if !defined(CONFIG_ARCH_S390)
++EXPORT_SYMBOL(probe_irq_on);
++EXPORT_SYMBOL(probe_irq_off);
++#endif
++
++#ifdef CONFIG_SMP
++EXPORT_SYMBOL(del_timer_sync);
++#endif
++EXPORT_SYMBOL(mod_timer);
++EXPORT_SYMBOL(tq_timer);
++EXPORT_SYMBOL(tq_immediate);
++
++#ifdef CONFIG_SMP
++/* Various random spinlocks we want to export */
++EXPORT_SYMBOL(tqueue_lock);
++
++/* Big-Reader lock implementation */
++EXPORT_SYMBOL(__brlock_array);
++#ifndef __BRLOCK_USE_ATOMICS
++EXPORT_SYMBOL(__br_write_locks);
++#endif
++EXPORT_SYMBOL(__br_write_lock);
++EXPORT_SYMBOL(__br_write_unlock);
++#endif
++
++/* Kiobufs */
++EXPORT_SYMBOL(alloc_kiovec);
++EXPORT_SYMBOL(free_kiovec);
++EXPORT_SYMBOL(expand_kiobuf);
++
++EXPORT_SYMBOL(map_user_kiobuf);
++EXPORT_SYMBOL(unmap_kiobuf);
++EXPORT_SYMBOL(lock_kiovec);
++EXPORT_SYMBOL(unlock_kiovec);
++EXPORT_SYMBOL(brw_kiovec);
++EXPORT_SYMBOL(kiobuf_wait_for_io);
++
++/* dma handling */
++EXPORT_SYMBOL(request_dma);
++EXPORT_SYMBOL(free_dma);
++EXPORT_SYMBOL(dma_spin_lock);
++#ifdef HAVE_DISABLE_HLT
++EXPORT_SYMBOL(disable_hlt);
++EXPORT_SYMBOL(enable_hlt);
++#endif
++
++/* resource handling */
++EXPORT_SYMBOL(request_resource);
++EXPORT_SYMBOL(release_resource);
++EXPORT_SYMBOL(allocate_resource);
++EXPORT_SYMBOL(check_resource);
++EXPORT_SYMBOL(__request_region);
++EXPORT_SYMBOL(__check_region);
++EXPORT_SYMBOL(__release_region);
++EXPORT_SYMBOL(ioport_resource);
++EXPORT_SYMBOL(iomem_resource);
++
++/* process management */
++EXPORT_SYMBOL(complete_and_exit);
++EXPORT_SYMBOL(__wake_up);
++EXPORT_SYMBOL(__wake_up_sync);
++EXPORT_SYMBOL(wake_up_process);
++EXPORT_SYMBOL(sleep_on);
++EXPORT_SYMBOL(sleep_on_timeout);
++EXPORT_SYMBOL(interruptible_sleep_on);
++EXPORT_SYMBOL(interruptible_sleep_on_timeout);
++EXPORT_SYMBOL(schedule);
++EXPORT_SYMBOL(schedule_timeout);
++#if CONFIG_SMP
++EXPORT_SYMBOL(set_cpus_allowed);
++#endif
++EXPORT_SYMBOL(yield);
++EXPORT_SYMBOL(__cond_resched);
++EXPORT_SYMBOL(jiffies);
++EXPORT_SYMBOL(xtime);
++EXPORT_SYMBOL(do_gettimeofday);
++EXPORT_SYMBOL(do_settimeofday);
++
++#if !defined(__ia64__)
++EXPORT_SYMBOL(loops_per_jiffy);
++#endif
++
++EXPORT_SYMBOL(kstat);
++EXPORT_SYMBOL(nr_running);
++
++/* misc */
++EXPORT_SYMBOL(panic);
++EXPORT_SYMBOL(panic_notifier_list);
++EXPORT_SYMBOL(panic_timeout);
++EXPORT_SYMBOL(__out_of_line_bug);
++EXPORT_SYMBOL(sprintf);
++EXPORT_SYMBOL(snprintf);
++EXPORT_SYMBOL(sscanf);
++EXPORT_SYMBOL(vsprintf);
++EXPORT_SYMBOL(vsnprintf);
++EXPORT_SYMBOL(vsscanf);
++EXPORT_SYMBOL(kdevname);
++EXPORT_SYMBOL(bdevname);
++EXPORT_SYMBOL(cdevname);
++EXPORT_SYMBOL(simple_strtol);
++EXPORT_SYMBOL(simple_strtoul);
++EXPORT_SYMBOL(simple_strtoull);
++EXPORT_SYMBOL(system_utsname); /* UTS data */
++EXPORT_SYMBOL(uts_sem); /* UTS semaphore */
++#ifndef __mips__
++EXPORT_SYMBOL(sys_call_table);
++#endif
++EXPORT_SYMBOL(machine_restart);
++EXPORT_SYMBOL(machine_halt);
++EXPORT_SYMBOL(machine_power_off);
++EXPORT_SYMBOL(_ctype);
++EXPORT_SYMBOL(secure_tcp_sequence_number);
++EXPORT_SYMBOL(get_random_bytes);
++EXPORT_SYMBOL(securebits);
++EXPORT_SYMBOL(cap_bset);
++EXPORT_SYMBOL(reparent_to_init);
++EXPORT_SYMBOL(daemonize);
++EXPORT_SYMBOL(csum_partial); /* for networking and md */
++EXPORT_SYMBOL(seq_escape);
++EXPORT_SYMBOL(seq_printf);
++EXPORT_SYMBOL(seq_open);
++EXPORT_SYMBOL(seq_release);
++EXPORT_SYMBOL(seq_read);
++EXPORT_SYMBOL(seq_lseek);
++EXPORT_SYMBOL(single_open);
++EXPORT_SYMBOL(single_release);
++EXPORT_SYMBOL(seq_release_private);
++
++/* Program loader interfaces */
++EXPORT_SYMBOL(setup_arg_pages);
++EXPORT_SYMBOL(copy_strings_kernel);
++EXPORT_SYMBOL(do_execve);
++EXPORT_SYMBOL(flush_old_exec);
++EXPORT_SYMBOL(kernel_read);
++EXPORT_SYMBOL(open_exec);
++
++/* Miscellaneous access points */
++EXPORT_SYMBOL(si_meminfo);
++
++/* Added to make file system as module */
++EXPORT_SYMBOL(sys_tz);
++EXPORT_SYMBOL(file_fsync);
++EXPORT_SYMBOL(fsync_buffers_list);
++EXPORT_SYMBOL(clear_inode);
++EXPORT_SYMBOL(___strtok);
++EXPORT_SYMBOL(init_special_inode);
++EXPORT_SYMBOL(read_ahead);
++EXPORT_SYMBOL(get_hash_table);
++EXPORT_SYMBOL(new_inode);
++EXPORT_SYMBOL(insert_inode_hash);
++EXPORT_SYMBOL(remove_inode_hash);
++EXPORT_SYMBOL(buffer_insert_list);
++EXPORT_SYMBOL(make_bad_inode);
++EXPORT_SYMBOL(is_bad_inode);
++EXPORT_SYMBOL(event);
++EXPORT_SYMBOL(brw_page);
++EXPORT_SYMBOL(__inode_dir_notify);
++
++#ifdef CONFIG_UID16
++EXPORT_SYMBOL(overflowuid);
++EXPORT_SYMBOL(overflowgid);
++#endif
++EXPORT_SYMBOL(fs_overflowuid);
++EXPORT_SYMBOL(fs_overflowgid);
++
++/* all busmice */
++EXPORT_SYMBOL(fasync_helper);
++EXPORT_SYMBOL(kill_fasync);
++
++EXPORT_SYMBOL(disk_name); /* for md.c */
++
++/* binfmt_aout */
++EXPORT_SYMBOL(get_write_access);
++
++/* library functions */
++EXPORT_SYMBOL(strnicmp);
++EXPORT_SYMBOL(strspn);
++EXPORT_SYMBOL(strsep);
++
++#ifdef CONFIG_CRC32
++EXPORT_SYMBOL(crc32_le);
++EXPORT_SYMBOL(crc32_be);
++EXPORT_SYMBOL(bitreverse);
++#endif
++
++#ifdef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
++
++/* software interrupts */
++EXPORT_SYMBOL(tasklet_hi_vec);
++EXPORT_SYMBOL(tasklet_vec);
++EXPORT_SYMBOL(bh_task_vec);
++EXPORT_SYMBOL(init_bh);
++EXPORT_SYMBOL(remove_bh);
++EXPORT_SYMBOL(tasklet_init);
++EXPORT_SYMBOL(tasklet_kill);
++EXPORT_SYMBOL(__run_task_queue);
++EXPORT_SYMBOL(do_softirq);
++EXPORT_SYMBOL(raise_softirq);
++EXPORT_SYMBOL(cpu_raise_softirq);
++EXPORT_SYMBOL(__tasklet_schedule);
++EXPORT_SYMBOL(__tasklet_hi_schedule);
++
++/* init task, for moving kthread roots - ought to export a function ?? */
++
++EXPORT_SYMBOL(init_task_union);
++
++EXPORT_SYMBOL(tasklist_lock);
++EXPORT_SYMBOL(pidhash);
++EXPORT_SYMBOL(unshare_files);
++
++/* debug */
++EXPORT_SYMBOL(dump_stack);
++
++/* To match ksyms with System.map */
++extern const char _end[];
++EXPORT_SYMBOL(_end);
+diff -urN kernel-source-2.4.27-8/kernel/signal.c kernel-source-2.4.27-8-arm-1/kernel/signal.c
+--- kernel-source-2.4.27-8/kernel/signal.c 2004-02-18 13:36:32.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/kernel/signal.c 2005-02-18 17:48:45.000000000 +0000
+@@ -778,8 +778,8 @@
+ info.si_uid = tsk->uid;
+
+ /* FIXME: find out whether or not this is supposed to be c*time. */
+- info.si_utime = tsk->times.tms_utime;
+- info.si_stime = tsk->times.tms_stime;
++ info.si_utime = hz_to_std(tsk->times.tms_utime);
++ info.si_stime = hz_to_std(tsk->times.tms_stime);
+
+ status = tsk->exit_code & 0x7f;
+ why = SI_KERNEL; /* shouldn't happen */
+@@ -1277,7 +1277,7 @@
+ #endif /* __sparc__ */
+ #endif
+
+-#if !defined(__alpha__) && !defined(__ia64__)
++#if !defined(__alpha__) && !defined(__ia64__) && !defined(__arm__)
+ /*
+ * For backwards compatibility. Functionality superseded by sigprocmask.
+ */
+@@ -1305,7 +1305,8 @@
+ }
+ #endif /* !defined(__alpha__) */
+
+-#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__) && \
++ !defined(__arm__)
+ /*
+ * For backwards compatibility. Functionality superseded by sigaction.
+ */
+@@ -1322,4 +1323,4 @@
+
+ return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
+ }
+-#endif /* !alpha && !__ia64__ && !defined(__mips__) */
++#endif /* !alpha && !__ia64__ && !defined(__mips__) && !defined(__arm__) */
+diff -urN kernel-source-2.4.27-8/kernel/sys.c kernel-source-2.4.27-8-arm-1/kernel/sys.c
+--- kernel-source-2.4.27-8/kernel/sys.c 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/kernel/sys.c 2005-02-18 17:48:45.000000000 +0000
+@@ -801,16 +801,23 @@
+
+ asmlinkage long sys_times(struct tms * tbuf)
+ {
++ struct tms temp;
++
+ /*
+ * In the SMP world we might just be unlucky and have one of
+ * the times increment as we use it. Since the value is an
+ * atomically safe type this is just fine. Conceptually its
+ * as if the syscall took an instant longer to occur.
+ */
+- if (tbuf)
+- if (copy_to_user(tbuf, ¤t->times, sizeof(struct tms)))
++ if (tbuf) {
++ temp.tms_utime = hz_to_std(current->times.tms_utime);
++ temp.tms_stime = hz_to_std(current->times.tms_stime);
++ temp.tms_cutime = hz_to_std(current->times.tms_cutime);
++ temp.tms_cstime = hz_to_std(current->times.tms_cstime);
++ if (copy_to_user(tbuf, &temp, sizeof(struct tms)))
+ return -EFAULT;
+- return jiffies;
++ }
++ return hz_to_std(jiffies);
+ }
+
+ /*
+diff -urN kernel-source-2.4.27-8/lib/string.c kernel-source-2.4.27-8-arm-1/lib/string.c
+--- kernel-source-2.4.27-8/lib/string.c 2005-01-19 09:57:58.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/lib/string.c 2005-02-18 17:48:45.000000000 +0000
+@@ -189,10 +189,10 @@
+ */
+ char * strchr(const char * s, int c)
+ {
+- for(; *s != (char) c; ++s)
+- if (*s == '\0')
+- return NULL;
++ for(; *s != '\0'; ++s)
++ if (*s == (char) c)
+ return (char *) s;
++ return NULL;
+ }
+ #endif
+
+diff -urN kernel-source-2.4.27-8/lib/string.c.orig kernel-source-2.4.27-8-arm-1/lib/string.c.orig
+--- kernel-source-2.4.27-8/lib/string.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/lib/string.c.orig 2005-01-19 09:57:58.000000000 +0000
+@@ -0,0 +1,534 @@
++/*
++ * linux/lib/string.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ */
++
++/*
++ * stupid library routines.. The optimized versions should generally be found
++ * as inline code in <asm-xx/string.h>
++ *
++ * These are buggy as well..
++ *
++ * * Fri Jun 25 1999, Ingo Oeser <ioe at informatik.tu-chemnitz.de>
++ * - Added strsep() which will replace strtok() soon (because strsep() is
++ * reentrant and should be faster). Use only strsep() in new code, please.
++ */
++
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++
++#ifndef __HAVE_ARCH_STRNICMP
++/**
++ * strnicmp - Case insensitive, length-limited string comparison
++ * @s1: One string
++ * @s2: The other string
++ * @len: the maximum number of characters to compare
++ */
++int strnicmp(const char *s1, const char *s2, size_t len)
++{
++ /* Yes, Virginia, it had better be unsigned */
++ unsigned char c1, c2;
++
++ c1 = 0; c2 = 0;
++ if (len) {
++ do {
++ c1 = *s1; c2 = *s2;
++ s1++; s2++;
++ if (!c1)
++ break;
++ if (!c2)
++ break;
++ if (c1 == c2)
++ continue;
++ c1 = tolower(c1);
++ c2 = tolower(c2);
++ if (c1 != c2)
++ break;
++ } while (--len);
++ }
++ return (int)c1 - (int)c2;
++}
++#endif
++
++char * ___strtok;
++
++#ifndef __HAVE_ARCH_STRCPY
++/**
++ * strcpy - Copy a %NUL terminated string
++ * @dest: Where to copy the string to
++ * @src: Where to copy the string from
++ */
++char * strcpy(char * dest,const char *src)
++{
++ char *tmp = dest;
++
++ while ((*dest++ = *src++) != '\0')
++ /* nothing */;
++ return tmp;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRNCPY
++/**
++ * strncpy - Copy a length-limited, %NUL-terminated string
++ * @dest: Where to copy the string to
++ * @src: Where to copy the string from
++ * @count: The maximum number of bytes to copy
++ *
++ * The result is not %NUL-terminated if the source exceeds
++ * @count bytes.
++ */
++char * strncpy(char * dest,const char *src,size_t count)
++{
++ char *tmp = dest;
++
++ while (count) {
++ if ((*tmp = *src) != 0) src++;
++ tmp++;
++ count--;
++ }
++ return dest;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRCAT
++/**
++ * strcat - Append one %NUL-terminated string to another
++ * @dest: The string to be appended to
++ * @src: The string to append to it
++ */
++char * strcat(char * dest, const char * src)
++{
++ char *tmp = dest;
++
++ while (*dest)
++ dest++;
++ while ((*dest++ = *src++) != '\0')
++ ;
++
++ return tmp;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRNCAT
++/**
++ * strncat - Append a length-limited, %NUL-terminated string to another
++ * @dest: The string to be appended to
++ * @src: The string to append to it
++ * @count: The maximum numbers of bytes to copy
++ *
++ * Note that in contrast to strncpy, strncat ensures the result is
++ * terminated.
++ */
++char * strncat(char *dest, const char *src, size_t count)
++{
++ char *tmp = dest;
++
++ if (count) {
++ while (*dest)
++ dest++;
++ while ((*dest++ = *src++)) {
++ if (--count == 0) {
++ *dest = '\0';
++ break;
++ }
++ }
++ }
++
++ return tmp;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRCMP
++/**
++ * strcmp - Compare two strings
++ * @cs: One string
++ * @ct: Another string
++ */
++int strcmp(const char * cs,const char * ct)
++{
++ register signed char __res;
++
++ while (1) {
++ if ((__res = *cs - *ct++) != 0 || !*cs++)
++ break;
++ }
++
++ return __res;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRNCMP
++/**
++ * strncmp - Compare two length-limited strings
++ * @cs: One string
++ * @ct: Another string
++ * @count: The maximum number of bytes to compare
++ */
++int strncmp(const char * cs,const char * ct,size_t count)
++{
++ register signed char __res = 0;
++
++ while (count) {
++ if ((__res = *cs - *ct++) != 0 || !*cs++)
++ break;
++ count--;
++ }
++
++ return __res;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRCHR
++/**
++ * strchr - Find the first occurrence of a character in a string
++ * @s: The string to be searched
++ * @c: The character to search for
++ */
++char * strchr(const char * s, int c)
++{
++ for(; *s != (char) c; ++s)
++ if (*s == '\0')
++ return NULL;
++ return (char *) s;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRRCHR
++/**
++ * strrchr - Find the last occurrence of a character in a string
++ * @s: The string to be searched
++ * @c: The character to search for
++ */
++char * strrchr(const char * s, int c)
++{
++ const char *p = s + strlen(s);
++ do {
++ if (*p == (char)c)
++ return (char *)p;
++ } while (--p >= s);
++ return NULL;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRLEN
++/**
++ * strlen - Find the length of a string
++ * @s: The string to be sized
++ */
++size_t strlen(const char * s)
++{
++ const char *sc;
++
++ for (sc = s; *sc != '\0'; ++sc)
++ /* nothing */;
++ return sc - s;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRNLEN
++/**
++ * strnlen - Find the length of a length-limited string
++ * @s: The string to be sized
++ * @count: The maximum number of bytes to search
++ */
++size_t strnlen(const char * s, size_t count)
++{
++ const char *sc;
++
++ for (sc = s; count-- && *sc != '\0'; ++sc)
++ /* nothing */;
++ return sc - s;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRSPN
++/**
++ * strspn - Calculate the length of the initial substring of @s which only
++ * contain letters in @accept
++ * @s: The string to be searched
++ * @accept: The string to search for
++ */
++size_t strspn(const char *s, const char *accept)
++{
++ const char *p;
++ const char *a;
++ size_t count = 0;
++
++ for (p = s; *p != '\0'; ++p) {
++ for (a = accept; *a != '\0'; ++a) {
++ if (*p == *a)
++ break;
++ }
++ if (*a == '\0')
++ return count;
++ ++count;
++ }
++
++ return count;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRPBRK
++/**
++ * strpbrk - Find the first occurrence of a set of characters
++ * @cs: The string to be searched
++ * @ct: The characters to search for
++ */
++char * strpbrk(const char * cs,const char * ct)
++{
++ const char *sc1,*sc2;
++
++ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
++ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
++ if (*sc1 == *sc2)
++ return (char *) sc1;
++ }
++ }
++ return NULL;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRTOK
++/**
++ * strtok - Split a string into tokens
++ * @s: The string to be searched
++ * @ct: The characters to search for
++ *
++ * WARNING: strtok is deprecated, use strsep instead.
++ */
++char * strtok(char * s,const char * ct)
++{
++ char *sbegin, *send;
++
++ sbegin = s ? s : ___strtok;
++ if (!sbegin) {
++ return NULL;
++ }
++ sbegin += strspn(sbegin,ct);
++ if (*sbegin == '\0') {
++ ___strtok = NULL;
++ return( NULL );
++ }
++ send = strpbrk( sbegin, ct);
++ if (send && *send != '\0')
++ *send++ = '\0';
++ ___strtok = send;
++ return (sbegin);
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRSEP
++/**
++ * strsep - Split a string into tokens
++ * @s: The string to be searched
++ * @ct: The characters to search for
++ *
++ * strsep() updates @s to point after the token, ready for the next call.
++ *
++ * It returns empty tokens, too, behaving exactly like the libc function
++ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
++ * Same semantics, slimmer shape. ;)
++ */
++char * strsep(char **s, const char *ct)
++{
++ char *sbegin = *s, *end;
++
++ if (sbegin == NULL)
++ return NULL;
++
++ end = strpbrk(sbegin, ct);
++ if (end)
++ *end++ = '\0';
++ *s = end;
++
++ return sbegin;
++}
++#endif
++
++#ifndef __HAVE_ARCH_MEMSET
++/**
++ * memset - Fill a region of memory with the given value
++ * @s: Pointer to the start of the area.
++ * @c: The byte to fill the area with
++ * @count: The size of the area.
++ *
++ * Do not use memset() to access IO space, use memset_io() instead.
++ */
++void * memset(void * s,int c,size_t count)
++{
++ char *xs = (char *) s;
++
++ while (count--)
++ *xs++ = c;
++
++ return s;
++}
++#endif
++
++#ifndef __HAVE_ARCH_BCOPY
++/**
++ * bcopy - Copy one area of memory to another
++ * @src: Where to copy from
++ * @dest: Where to copy to
++ * @count: The size of the area.
++ *
++ * Note that this is the same as memcpy(), with the arguments reversed.
++ * memcpy() is the standard, bcopy() is a legacy BSD function.
++ *
++ * You should not use this function to access IO space, use memcpy_toio()
++ * or memcpy_fromio() instead.
++ */
++char * bcopy(const char * src, char * dest, int count)
++{
++ char *tmp = dest;
++
++ while (count--)
++ *tmp++ = *src++;
++
++ return dest;
++}
++#endif
++
++#ifndef __HAVE_ARCH_MEMCPY
++/**
++ * memcpy - Copy one area of memory to another
++ * @dest: Where to copy to
++ * @src: Where to copy from
++ * @count: The size of the area.
++ *
++ * You should not use this function to access IO space, use memcpy_toio()
++ * or memcpy_fromio() instead.
++ */
++void * memcpy(void * dest,const void *src,size_t count)
++{
++ char *tmp = (char *) dest, *s = (char *) src;
++
++ while (count--)
++ *tmp++ = *s++;
++
++ return dest;
++}
++#endif
++
++#ifndef __HAVE_ARCH_MEMMOVE
++/**
++ * memmove - Copy one area of memory to another
++ * @dest: Where to copy to
++ * @src: Where to copy from
++ * @count: The size of the area.
++ *
++ * Unlike memcpy(), memmove() copes with overlapping areas.
++ */
++void * memmove(void * dest,const void *src,size_t count)
++{
++ char *tmp, *s;
++
++ if (dest <= src) {
++ tmp = (char *) dest;
++ s = (char *) src;
++ while (count--)
++ *tmp++ = *s++;
++ }
++ else {
++ tmp = (char *) dest + count;
++ s = (char *) src + count;
++ while (count--)
++ *--tmp = *--s;
++ }
++
++ return dest;
++}
++#endif
++
++#ifndef __HAVE_ARCH_MEMCMP
++/**
++ * memcmp - Compare two areas of memory
++ * @cs: One area of memory
++ * @ct: Another area of memory
++ * @count: The size of the area.
++ */
++int memcmp(const void * cs,const void * ct,size_t count)
++{
++ const unsigned char *su1, *su2;
++ int res = 0;
++
++ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
++ if ((res = *su1 - *su2) != 0)
++ break;
++ return res;
++}
++#endif
++
++#ifndef __HAVE_ARCH_MEMSCAN
++/**
++ * memscan - Find a character in an area of memory.
++ * @addr: The memory area
++ * @c: The byte to search for
++ * @size: The size of the area.
++ *
++ * returns the address of the first occurrence of @c, or 1 byte past
++ * the area if @c is not found
++ */
++void * memscan(void * addr, int c, size_t size)
++{
++ unsigned char * p = (unsigned char *) addr;
++
++ while (size) {
++ if (*p == c)
++ return (void *) p;
++ p++;
++ size--;
++ }
++ return (void *) p;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRSTR
++/**
++ * strstr - Find the first substring in a %NUL terminated string
++ * @s1: The string to be searched
++ * @s2: The string to search for
++ */
++char * strstr(const char * s1,const char * s2)
++{
++ int l1, l2;
++
++ l2 = strlen(s2);
++ if (!l2)
++ return (char *) s1;
++ l1 = strlen(s1);
++ while (l1 >= l2) {
++ l1--;
++ if (!memcmp(s1,s2,l2))
++ return (char *) s1;
++ s1++;
++ }
++ return NULL;
++}
++#endif
++
++#ifndef __HAVE_ARCH_MEMCHR
++/**
++ * memchr - Find a character in an area of memory.
++ * @s: The memory area
++ * @c: The byte to search for
++ * @n: The size of the area.
++ *
++ * returns the address of the first occurrence of @c, or %NULL
++ * if @c is not found
++ */
++void *memchr(const void *s, int c, size_t n)
++{
++ const unsigned char *p = s;
++ while (n-- != 0) {
++ if ((unsigned char)c == *p++) {
++ return (void *)(p-1);
++ }
++ }
++ return NULL;
++}
++
++#endif
+diff -urN kernel-source-2.4.27-8/mm/Makefile kernel-source-2.4.27-8-arm-1/mm/Makefile
+--- kernel-source-2.4.27-8/mm/Makefile 2005-01-19 09:57:47.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/mm/Makefile 2005-02-18 17:48:45.000000000 +0000
+@@ -20,4 +20,5 @@
+
+ obj-$(CONFIG_BIGPHYS_AREA) += bigphysarea.o
+
++obj-y += debug.o
+ include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27-8/mm/debug.c kernel-source-2.4.27-8-arm-1/mm/debug.c
+--- kernel-source-2.4.27-8/mm/debug.c 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/mm/debug.c 2005-02-18 17:48:45.000000000 +0000
+@@ -0,0 +1,159 @@
++/*
++ * linux/mm/debug.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.
++ *
++ * Dump out page information on SysRQ-G, and provide show_page_info()
++ *
++ * You are advised to use a serial console with this patch - it
++ * saturates a 38400baud link for 1 minute on a 32MB machine.
++ */
++#include <linux/kernel.h>
++#include <linux/mm.h>
++#include <linux/pagemap.h>
++#include <linux/sysrq.h>
++#include <linux/init.h>
++
++/*
++ * We print out the following information for each page in the system:
++ * address: use count, age, mapping, [RSsr] rD [acd]
++ *
++ * The flags are:
++ * R - reserved
++ * S - in swapcache
++ * s - slab page
++ *
++ * r - referenced
++ * D - dirty
++ *
++ * a - active page
++ */
++static void page_detail(struct page *page)
++{
++ if (!page)
++ return;
++
++ printk("%p: %2d %p [%c%c%c] %c%c [%c]\n",
++ page_address(page),
++ page_count(page),
++ page->mapping,
++
++ PageReserved(page) ? 'R' : '-',
++ PageSwapCache(page) ? 'S' : '-',
++ PageSlab(page) ? 's' : '-',
++
++ PageReferenced(page) ? 'r' : '-',
++ PageDirty(page) ? 'D' : '-',
++
++ PageActive(page) ? 'a' : '-');
++}
++
++/*
++ * This version collects statistics
++ */
++static int anon_pages, slab_pages, resvd_pages, unused_pages;
++
++static void page_statistics(struct page *page)
++{
++ if (page) {
++ if (PageReserved(page))
++ resvd_pages++;
++ else if (PageSlab(page))
++ slab_pages++;
++ else if (!page_count(page))
++ unused_pages ++;
++ else if (!page->mapping)
++ anon_pages ++;
++ return;
++ }
++
++ printk(" anon: %d, slab: %d, reserved: %d, free: %d\n",
++ anon_pages, slab_pages, resvd_pages, unused_pages);
++}
++
++static void show_zone_info(zone_t *zone, void (*fn)(struct page *))
++{
++ int i;
++
++ printk(" total %ld, free %ld\n",
++ zone->size, zone->free_pages);
++
++ anon_pages = 0;
++ slab_pages = 0;
++ resvd_pages = 0;
++ unused_pages = 0;
++
++ for (i = 0; i < zone->size; i++) {
++ struct page *page = zone->zone_mem_map + i;
++
++ fn(page);
++ }
++
++ fn(NULL);
++}
++
++static void show_node_info(pg_data_t *pg, void (*fn)(struct page *))
++{
++ int type;
++
++ for (type = 0; type < MAX_NR_ZONES; type++) {
++ zone_t *zone = pg->node_zones + type;
++
++ if (zone->size == 0)
++ continue;
++
++ printk("----- Zone %d ------\n", type);
++
++ show_zone_info(zone, fn);
++ }
++}
++
++static void __show_page_info(void (*fn)(struct page *))
++{
++ pg_data_t *pg;
++ int pgdat = 0;
++
++ for (pg = pgdat_list; pg; pg = pg->node_next) {
++
++ printk("===== Node %d =====\n", pgdat++);
++
++ show_node_info(pg, fn);
++ }
++}
++
++void show_page_info(void)
++{
++ __show_page_info(page_detail);
++}
++
++static void
++show_pg_info(int key, struct pt_regs *regs, struct kbd_struct *kd,
++ struct tty_struct *tty)
++{
++ void (*fn)(struct page *);
++ show_mem();
++ if (key == 'g')
++ fn = page_detail;
++ else
++ fn = page_statistics;
++ __show_page_info(fn);
++}
++
++static struct sysrq_key_op page_info_op = {
++ handler: show_pg_info,
++ help_msg: "paGeinfo",
++ action_msg: "Page Info",
++};
++
++static int __init debug_mm_init(void)
++{
++ register_sysrq_key('g', &page_info_op);
++ register_sysrq_key('h', &page_info_op);
++ return 0;
++}
++
++__initcall(debug_mm_init);
+diff -urN kernel-source-2.4.27-8/mm/filemap.c kernel-source-2.4.27-8-arm-1/mm/filemap.c
+--- kernel-source-2.4.27-8/mm/filemap.c 2005-01-19 09:57:47.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/mm/filemap.c 2005-02-18 17:48:45.000000000 +0000
+@@ -2211,12 +2211,16 @@
+ pte_t pte = *ptep;
+
+ if (pte_present(pte)) {
+- struct page *page = pte_page(pte);
+- if (VALID_PAGE(page) && !PageReserved(page) && ptep_test_and_clear_dirty(ptep)) {
++ unsigned long pfn = pte_pfn(pte);
++
++ if (pfn_valid(pfn)) {
++ struct page *page = pfn_to_page(pfn);
++ if (!PageReserved(page) && ptep_test_and_clear_dirty(ptep)) {
+ flush_tlb_page(vma, address);
+ set_page_dirty(page);
+ }
+ }
++ }
+ return 0;
+ }
+
+diff -urN kernel-source-2.4.27-8/mm/memory.c kernel-source-2.4.27-8-arm-1/mm/memory.c
+--- kernel-source-2.4.27-8/mm/memory.c 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/mm/memory.c 2005-02-18 17:48:45.000000000 +0000
+@@ -77,8 +77,13 @@
+ */
+ void __free_pte(pte_t pte)
+ {
+- struct page *page = pte_page(pte);
+- if ((!VALID_PAGE(page)) || PageReserved(page))
++ unsigned long pfn = pte_pfn(pte);
++ struct page *page;
++
++ if (!pfn_valid(pfn))
++ return;
++ page = pfn_to_page(pfn);
++ if (PageReserved(page))
+ return;
+ if (pte_dirty(pte))
+ set_page_dirty(page);
+@@ -232,6 +237,7 @@
+ do {
+ pte_t pte = *src_pte;
+ struct page *ptepage;
++ unsigned long pfn;
+
+ /* copy_one_pte */
+
+@@ -241,9 +247,11 @@
+ swap_duplicate(pte_to_swp_entry(pte));
+ goto cont_copy_pte_range;
+ }
+- ptepage = pte_page(pte);
+- if ((!VALID_PAGE(ptepage)) ||
+- PageReserved(ptepage))
++ pfn = pte_pfn(pte);
++ if (!pfn_valid(pfn))
++ goto cont_copy_pte_range;
++ ptepage = pfn_to_page(pfn);
++ if (PageReserved(ptepage))
+ goto cont_copy_pte_range;
+
+ /* If it's a COW mapping, write protect it both in the parent and the child */
+@@ -314,9 +322,13 @@
+ if (pte_none(pte))
+ continue;
+ if (pte_present(pte)) {
+- struct page *page = pte_page(pte);
+- if (VALID_PAGE(page) && !PageReserved(page))
++ unsigned long pfn = pte_pfn(pte);
++
++ if (pfn_valid(pfn)) {
++ struct page *page = pfn_to_page(pfn);
++ if (!PageReserved(page))
+ freed ++;
++ }
+ /* This will eventually call __free_pte on the pte. */
+ tlb_remove_page(tlb, ptep, address + offset);
+ } else {
+@@ -354,17 +366,37 @@
+ return freed;
+ }
+
++void unmap_page_range(mmu_gather_t *tlb, struct mm_struct *mm, unsigned long address, unsigned long end)
++{
++ int freed = 0;
++ pgd_t * dir;
++
++ if (address >= end)
++ BUG();
++ dir = pgd_offset(mm, address);
++ do {
++ freed += zap_pmd_range(tlb, dir, address, end - address);
++ address = (address + PGDIR_SIZE) & PGDIR_MASK;
++ dir++;
++ } while (address && (address < end));
++
++ /*
++ * Update rss for the mm_struct (not necessarily current->mm)
++ * Notice that rss is an unsigned long.
++ */
++ if (mm->rss > freed)
++ mm->rss -= freed;
++ else
++ mm->rss = 0;
++}
++
+ /*
+ * remove user pages in a given range.
+ */
+ void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
+ {
+- mmu_gather_t *tlb;
+- pgd_t * dir;
+ unsigned long start = address, end = address + size;
+- int freed = 0;
+-
+- dir = pgd_offset(mm, address);
++ mmu_gather_t *tlb;
+
+ /*
+ * This is a long-lived spinlock. That's fine.
+@@ -377,25 +409,10 @@
+ BUG();
+ spin_lock(&mm->page_table_lock);
+ flush_cache_range(mm, address, end);
+- tlb = tlb_gather_mmu(mm);
+-
+- do {
+- freed += zap_pmd_range(tlb, dir, address, end - address);
+- address = (address + PGDIR_SIZE) & PGDIR_MASK;
+- dir++;
+- } while (address && (address < end));
+
+- /* this will flush any remaining tlb entries */
++ tlb = tlb_gather_mmu(mm);
++ unmap_page_range(tlb, mm, address, end);
+ tlb_finish_mmu(tlb, start, end);
+-
+- /*
+- * Update rss for the mm_struct (not necessarily current->mm)
+- * Notice that rss is an unsigned long.
+- */
+- if (mm->rss > freed)
+- mm->rss -= freed;
+- else
+- mm->rss = 0;
+ spin_unlock(&mm->page_table_lock);
+ }
+
+@@ -407,6 +424,7 @@
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
++ unsigned long pfn;
+
+ pgd = pgd_offset(mm, address);
+ if (pgd_none(*pgd) || pgd_bad(*pgd))
+@@ -423,8 +441,11 @@
+ pte = *ptep;
+ if (pte_present(pte)) {
+ if (!write ||
+- (pte_write(pte) && pte_dirty(pte)))
+- return pte_page(pte);
++ (pte_write(pte) && pte_dirty(pte))) {
++ pfn = pte_pfn(pte);
++ if (pfn_valid(pfn))
++ return pfn_to_page(pfn);
++ }
+ }
+
+ out:
+@@ -439,7 +460,8 @@
+
+ static inline struct page * get_page_map(struct page *page)
+ {
+- if (!VALID_PAGE(page))
++ unsigned long pfn = page_to_pfn(page);
++ if (!pfn_valid(pfn))
+ return 0;
+ return page;
+ }
+@@ -826,22 +848,22 @@
+ unsigned long phys_addr, pgprot_t prot)
+ {
+ unsigned long end;
++ unsigned long pfn;
+
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end > PMD_SIZE)
+ end = PMD_SIZE;
++ pfn = phys_addr >> PAGE_SHIFT;
+ do {
+- struct page *page;
+ pte_t oldpage;
+ oldpage = ptep_get_and_clear(pte);
+
+- page = virt_to_page(__va(phys_addr));
+- if ((!VALID_PAGE(page)) || PageReserved(page))
+- set_pte(pte, mk_pte_phys(phys_addr, prot));
++ if (!pfn_valid(pfn) || PageReserved(pfn_to_page(pfn)))
++ set_pte(pte, pfn_pte(pfn, prot));
+ forget_pte(oldpage);
+ address += PAGE_SIZE;
+- phys_addr += PAGE_SIZE;
++ pfn++;
+ pte++;
+ } while (address && (address < end));
+ }
+@@ -949,10 +971,11 @@
+ unsigned long address, pte_t *page_table, pte_t pte)
+ {
+ struct page *old_page, *new_page;
++ unsigned long pfn = pte_pfn(pte);
+
+- old_page = pte_page(pte);
+- if (!VALID_PAGE(old_page))
++ if (!pfn_valid(pfn))
+ goto bad_wp_page;
++ old_page = pte_page(pte);
+
+ if (!TryLockPage(old_page)) {
+ int reuse = can_share_swap_page(old_page);
+@@ -996,7 +1019,7 @@
+
+ bad_wp_page:
+ spin_unlock(&mm->page_table_lock);
+- printk("do_wp_page: bogus page at address %08lx (page 0x%lx)\n",address,(unsigned long)old_page);
++ printk("do_wp_page: bogus page at address %08lx\n", address);
+ return -1;
+ no_mem:
+ page_cache_release(old_page);
+diff -urN kernel-source-2.4.27-8/mm/mmap.c kernel-source-2.4.27-8-arm-1/mm/mmap.c
+--- kernel-source-2.4.27-8/mm/mmap.c 2005-01-19 09:57:58.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/mm/mmap.c 2005-02-18 17:48:45.000000000 +0000
+@@ -18,6 +18,9 @@
+
+ #include <asm/uaccess.h>
+ #include <asm/pgalloc.h>
++#include <asm/tlb.h>
++
++extern void unmap_page_range(mmu_gather_t *tlb, struct mm_struct *mm, unsigned long start, unsigned long end);
+
+ /*
+ * WARNING: the debugging will use recursive algorithms so never enable this
+@@ -915,6 +918,8 @@
+ * old method of shifting the VA >> by PGDIR_SHIFT doesn't work.
+ */
+ start_index = pgd_index(first);
++ if (start_index < FIRST_USER_PGD_NR)
++ start_index = FIRST_USER_PGD_NR;
+ end_index = pgd_index(last);
+ if (end_index > start_index) {
+ clear_page_tables(mm, start_index, end_index - start_index);
+@@ -1150,45 +1155,58 @@
+ /* Release all mmaps. */
+ void exit_mmap(struct mm_struct * mm)
+ {
++ mmu_gather_t *tlb;
+ struct vm_area_struct * mpnt;
+
+ release_segments(mm);
+ spin_lock(&mm->page_table_lock);
++
++ tlb = tlb_gather_mmu(mm);
++
++ flush_cache_mm(mm);
++ mpnt = mm->mmap;
++ while (mpnt) {
++ unsigned long start = mpnt->vm_start;
++ unsigned long end = mpnt->vm_end;
++
++ mm->map_count--;
++ remove_shared_vm_struct(mpnt);
++ unmap_page_range(tlb, mm, start, end);
++ mpnt = mpnt->vm_next;
++ }
++
++ /* This is just debugging */
++ if (mm->map_count)
++ BUG();
++
++ tlb_finish_mmu(tlb, 0, TASK_SIZE);
++
+ mpnt = mm->mmap;
+ mm->mmap = mm->mmap_cache = NULL;
+ mm->mm_rb = RB_ROOT;
+ mm->rss = 0;
+- spin_unlock(&mm->page_table_lock);
+ mm->total_vm = 0;
+ mm->locked_vm = 0;
+
+- flush_cache_mm(mm);
++ spin_unlock(&mm->page_table_lock);
++
++ clear_page_tables(mm, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);
++
++ /*
++ * Walk the list again, actually closing and freeing it
++ * without holding any MM locks.
++ */
+ while (mpnt) {
+ struct vm_area_struct * next = mpnt->vm_next;
+- unsigned long start = mpnt->vm_start;
+- unsigned long end = mpnt->vm_end;
+- unsigned long size = end - start;
+-
+ if (mpnt->vm_ops) {
+ if (mpnt->vm_ops->close)
+ mpnt->vm_ops->close(mpnt);
+ }
+- mm->map_count--;
+- remove_shared_vm_struct(mpnt);
+- zap_page_range(mm, start, size);
+ if (mpnt->vm_file)
+ fput(mpnt->vm_file);
+ kmem_cache_free(vm_area_cachep, mpnt);
+ mpnt = next;
+ }
+-
+- /* This is just debugging */
+- if (mm->map_count)
+- BUG();
+-
+- clear_page_tables(mm, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);
+-
+- flush_tlb_mm(mm);
+ }
+
+ /* Insert vm structure into process list sorted by address
+diff -urN kernel-source-2.4.27-8/mm/mmap.c.orig kernel-source-2.4.27-8-arm-1/mm/mmap.c.orig
+--- kernel-source-2.4.27-8/mm/mmap.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/mm/mmap.c.orig 2005-01-19 09:57:58.000000000 +0000
+@@ -0,0 +1,1221 @@
++/*
++ * linux/mm/mmap.c
++ *
++ * Written by obz.
++ */
++#include <linux/slab.h>
++#include <linux/shm.h>
++#include <linux/mman.h>
++#include <linux/pagemap.h>
++#include <linux/swap.h>
++#include <linux/swapctl.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <linux/file.h>
++#include <linux/fs.h>
++#include <linux/personality.h>
++#include <linux/mount.h>
++
++#include <asm/uaccess.h>
++#include <asm/pgalloc.h>
++
++/*
++ * WARNING: the debugging will use recursive algorithms so never enable this
++ * unless you know what you are doing.
++ */
++#undef DEBUG_MM_RB
++
++/* description of effects of mapping type and prot in current implementation.
++ * this is due to the limited x86 page protection hardware. The expected
++ * behavior is in parens:
++ *
++ * map_type prot
++ * PROT_NONE PROT_READ PROT_WRITE PROT_EXEC
++ * MAP_SHARED r: (no) no r: (yes) yes r: (no) yes r: (no) yes
++ * w: (no) no w: (no) no w: (yes) yes w: (no) no
++ * x: (no) no x: (no) yes x: (no) yes x: (yes) yes
++ *
++ * MAP_PRIVATE r: (no) no r: (yes) yes r: (no) yes r: (no) yes
++ * w: (no) no w: (no) no w: (copy) copy w: (no) no
++ * x: (no) no x: (no) yes x: (no) yes x: (yes) yes
++ *
++ */
++pgprot_t protection_map[16] = {
++ __P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
++ __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
++};
++
++int sysctl_overcommit_memory;
++int max_map_count = DEFAULT_MAX_MAP_COUNT;
++
++/* Check that a process has enough memory to allocate a
++ * new virtual mapping.
++ */
++int vm_enough_memory(long pages)
++{
++ /* Stupid algorithm to decide if we have enough memory: while
++ * simple, it hopefully works in most obvious cases.. Easy to
++ * fool it, but this should catch most mistakes.
++ */
++ /* 23/11/98 NJC: Somewhat less stupid version of algorithm,
++ * which tries to do "TheRightThing". Instead of using half of
++ * (buffers+cache), use the minimum values. Allow an extra 2%
++ * of num_physpages for safety margin.
++ */
++
++ unsigned long free;
++
++ /* Sometimes we want to use more memory than we have. */
++ if (sysctl_overcommit_memory)
++ return 1;
++
++ /* The page cache contains buffer pages these days.. */
++ free = page_cache_size;
++ free += nr_free_pages();
++ free += nr_swap_pages;
++
++ /*
++ * This double-counts: the nrpages are both in the page-cache
++ * and in the swapper space. At the same time, this compensates
++ * for the swap-space over-allocation (ie "nr_swap_pages" being
++ * too small.
++ */
++ free += swapper_space.nrpages;
++
++ /*
++ * The code below doesn't account for free space in the inode
++ * and dentry slab cache, slab cache fragmentation, inodes and
++ * dentries which will become freeable under VM load, etc.
++ * Lets just hope all these (complex) factors balance out...
++ */
++ free += (dentry_stat.nr_unused * sizeof(struct dentry)) >> PAGE_SHIFT;
++ free += (inodes_stat.nr_unused * sizeof(struct inode)) >> PAGE_SHIFT;
++
++ return free > pages;
++}
++
++/* Remove one vm structure from the inode's i_mapping address space. */
++static inline void __remove_shared_vm_struct(struct vm_area_struct *vma)
++{
++ struct file * file = vma->vm_file;
++
++ if (file) {
++ struct inode *inode = file->f_dentry->d_inode;
++ if (vma->vm_flags & VM_DENYWRITE)
++ atomic_inc(&inode->i_writecount);
++ if(vma->vm_next_share)
++ vma->vm_next_share->vm_pprev_share = vma->vm_pprev_share;
++ *vma->vm_pprev_share = vma->vm_next_share;
++ }
++}
++
++static inline void remove_shared_vm_struct(struct vm_area_struct *vma)
++{
++ lock_vma_mappings(vma);
++ __remove_shared_vm_struct(vma);
++ unlock_vma_mappings(vma);
++}
++
++void lock_vma_mappings(struct vm_area_struct *vma)
++{
++ struct address_space *mapping;
++
++ mapping = NULL;
++ if (vma->vm_file)
++ mapping = vma->vm_file->f_dentry->d_inode->i_mapping;
++ if (mapping)
++ spin_lock(&mapping->i_shared_lock);
++}
++
++void unlock_vma_mappings(struct vm_area_struct *vma)
++{
++ struct address_space *mapping;
++
++ mapping = NULL;
++ if (vma->vm_file)
++ mapping = vma->vm_file->f_dentry->d_inode->i_mapping;
++ if (mapping)
++ spin_unlock(&mapping->i_shared_lock);
++}
++
++/*
++ * sys_brk() for the most part doesn't need the global kernel
++ * lock, except when an application is doing something nasty
++ * like trying to un-brk an area that has already been mapped
++ * to a regular file. in this case, the unmapping will need
++ * to invoke file system routines that need the global lock.
++ */
++asmlinkage unsigned long sys_brk(unsigned long brk)
++{
++ unsigned long rlim, retval;
++ unsigned long newbrk, oldbrk;
++ struct mm_struct *mm = current->mm;
++
++ down_write(&mm->mmap_sem);
++
++ if (brk < mm->end_code)
++ goto out;
++ newbrk = PAGE_ALIGN(brk);
++ oldbrk = PAGE_ALIGN(mm->brk);
++ if (oldbrk == newbrk)
++ goto set_brk;
++
++ /* Always allow shrinking brk. */
++ if (brk <= mm->brk) {
++ if (!do_munmap(mm, newbrk, oldbrk-newbrk))
++ goto set_brk;
++ goto out;
++ }
++
++ /* Check against rlimit.. */
++ rlim = current->rlim[RLIMIT_DATA].rlim_cur;
++ if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
++ goto out;
++
++ /* Check against existing mmap mappings. */
++ if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
++ goto out;
++
++ /* Check if we have enough memory.. */
++ if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT))
++ goto out;
++
++ /* Ok, looks good - let it rip. */
++ if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
++ goto out;
++set_brk:
++ mm->brk = brk;
++out:
++ retval = mm->brk;
++ up_write(&mm->mmap_sem);
++ return retval;
++}
++
++/* Combine the mmap "prot" and "flags" argument into one "vm_flags" used
++ * internally. Essentially, translate the "PROT_xxx" and "MAP_xxx" bits
++ * into "VM_xxx".
++ */
++static inline unsigned long calc_vm_flags(unsigned long prot, unsigned long flags)
++{
++#define _trans(x,bit1,bit2) \
++((bit1==bit2)?(x&bit1):(x&bit1)?bit2:0)
++
++ unsigned long prot_bits, flag_bits;
++ prot_bits =
++ _trans(prot, PROT_READ, VM_READ) |
++ _trans(prot, PROT_WRITE, VM_WRITE) |
++ _trans(prot, PROT_EXEC, VM_EXEC);
++ flag_bits =
++ _trans(flags, MAP_GROWSDOWN, VM_GROWSDOWN) |
++ _trans(flags, MAP_DENYWRITE, VM_DENYWRITE) |
++ _trans(flags, MAP_EXECUTABLE, VM_EXECUTABLE);
++ return prot_bits | flag_bits;
++#undef _trans
++}
++
++#ifdef DEBUG_MM_RB
++static int browse_rb(rb_node_t * rb_node) {
++ int i = 0;
++ if (rb_node) {
++ i++;
++ i += browse_rb(rb_node->rb_left);
++ i += browse_rb(rb_node->rb_right);
++ }
++ return i;
++}
++
++static void validate_mm(struct mm_struct * mm) {
++ int bug = 0;
++ int i = 0;
++ struct vm_area_struct * tmp = mm->mmap;
++ while (tmp) {
++ tmp = tmp->vm_next;
++ i++;
++ }
++ if (i != mm->map_count)
++ printk("map_count %d vm_next %d\n", mm->map_count, i), bug = 1;
++ i = browse_rb(mm->mm_rb.rb_node);
++ if (i != mm->map_count)
++ printk("map_count %d rb %d\n", mm->map_count, i), bug = 1;
++ if (bug)
++ BUG();
++}
++#else
++#define validate_mm(mm) do { } while (0)
++#endif
++
++static struct vm_area_struct * find_vma_prepare(struct mm_struct * mm, unsigned long addr,
++ struct vm_area_struct ** pprev,
++ rb_node_t *** rb_link, rb_node_t ** rb_parent)
++{
++ struct vm_area_struct * vma;
++ rb_node_t ** __rb_link, * __rb_parent, * rb_prev;
++
++ __rb_link = &mm->mm_rb.rb_node;
++ rb_prev = __rb_parent = NULL;
++ vma = NULL;
++
++ while (*__rb_link) {
++ struct vm_area_struct *vma_tmp;
++
++ __rb_parent = *__rb_link;
++ vma_tmp = rb_entry(__rb_parent, struct vm_area_struct, vm_rb);
++
++ if (vma_tmp->vm_end > addr) {
++ vma = vma_tmp;
++ if (vma_tmp->vm_start <= addr)
++ return vma;
++ __rb_link = &__rb_parent->rb_left;
++ } else {
++ rb_prev = __rb_parent;
++ __rb_link = &__rb_parent->rb_right;
++ }
++ }
++
++ *pprev = NULL;
++ if (rb_prev)
++ *pprev = rb_entry(rb_prev, struct vm_area_struct, vm_rb);
++ *rb_link = __rb_link;
++ *rb_parent = __rb_parent;
++ return vma;
++}
++
++static inline void __vma_link_list(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev,
++ rb_node_t * rb_parent)
++{
++ if (prev) {
++ vma->vm_next = prev->vm_next;
++ prev->vm_next = vma;
++ } else {
++ mm->mmap = vma;
++ if (rb_parent)
++ vma->vm_next = rb_entry(rb_parent, struct vm_area_struct, vm_rb);
++ else
++ vma->vm_next = NULL;
++ }
++}
++
++static inline void __vma_link_rb(struct mm_struct * mm, struct vm_area_struct * vma,
++ rb_node_t ** rb_link, rb_node_t * rb_parent)
++{
++ rb_link_node(&vma->vm_rb, rb_parent, rb_link);
++ rb_insert_color(&vma->vm_rb, &mm->mm_rb);
++}
++
++static inline void __vma_link_file(struct vm_area_struct * vma)
++{
++ struct file * file;
++
++ file = vma->vm_file;
++ if (file) {
++ struct inode * inode = file->f_dentry->d_inode;
++ struct address_space *mapping = inode->i_mapping;
++ struct vm_area_struct **head;
++
++ if (vma->vm_flags & VM_DENYWRITE)
++ atomic_dec(&inode->i_writecount);
++
++ head = &mapping->i_mmap;
++ if (vma->vm_flags & VM_SHARED)
++ head = &mapping->i_mmap_shared;
++
++ /* insert vma into inode's share list */
++ if((vma->vm_next_share = *head) != NULL)
++ (*head)->vm_pprev_share = &vma->vm_next_share;
++ *head = vma;
++ vma->vm_pprev_share = head;
++ }
++}
++
++static void __vma_link(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev,
++ rb_node_t ** rb_link, rb_node_t * rb_parent)
++{
++ __vma_link_list(mm, vma, prev, rb_parent);
++ __vma_link_rb(mm, vma, rb_link, rb_parent);
++ __vma_link_file(vma);
++}
++
++static inline void vma_link(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev,
++ rb_node_t ** rb_link, rb_node_t * rb_parent)
++{
++ lock_vma_mappings(vma);
++ spin_lock(&mm->page_table_lock);
++ __vma_link(mm, vma, prev, rb_link, rb_parent);
++ spin_unlock(&mm->page_table_lock);
++ unlock_vma_mappings(vma);
++
++ mm->map_count++;
++ validate_mm(mm);
++}
++
++static int vma_merge(struct mm_struct * mm, struct vm_area_struct * prev,
++ rb_node_t * rb_parent, unsigned long addr, unsigned long end, unsigned long vm_flags)
++{
++ spinlock_t * lock = &mm->page_table_lock;
++ if (!prev) {
++ prev = rb_entry(rb_parent, struct vm_area_struct, vm_rb);
++ goto merge_next;
++ }
++ if (prev->vm_end == addr && can_vma_merge(prev, vm_flags)) {
++ struct vm_area_struct * next;
++
++ spin_lock(lock);
++ prev->vm_end = end;
++ next = prev->vm_next;
++ if (next && prev->vm_end == next->vm_start && can_vma_merge(next, vm_flags)) {
++ prev->vm_end = next->vm_end;
++ __vma_unlink(mm, next, prev);
++ spin_unlock(lock);
++
++ mm->map_count--;
++ kmem_cache_free(vm_area_cachep, next);
++ return 1;
++ }
++ spin_unlock(lock);
++ return 1;
++ }
++
++ prev = prev->vm_next;
++ if (prev) {
++ merge_next:
++ if (!can_vma_merge(prev, vm_flags))
++ return 0;
++ if (end == prev->vm_start) {
++ spin_lock(lock);
++ prev->vm_start = addr;
++ spin_unlock(lock);
++ return 1;
++ }
++ }
++
++ return 0;
++}
++
++unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned long len,
++ unsigned long prot, unsigned long flags, unsigned long pgoff)
++{
++ struct mm_struct * mm = current->mm;
++ struct vm_area_struct * vma, * prev;
++ unsigned int vm_flags;
++ int correct_wcount = 0;
++ int error;
++ rb_node_t ** rb_link, * rb_parent;
++
++ if (file) {
++ if (!file->f_op || !file->f_op->mmap)
++ return -ENODEV;
++
++ if ((prot & PROT_EXEC) && (file->f_vfsmnt->mnt_flags & MNT_NOEXEC))
++ return -EPERM;
++ }
++
++ if (!len)
++ return addr;
++
++ len = PAGE_ALIGN(len);
++
++ if (len > TASK_SIZE || len == 0)
++ return -EINVAL;
++
++ /* offset overflow? */
++ if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)
++ return -EINVAL;
++
++ /* Too many mappings? */
++ if (mm->map_count > max_map_count)
++ return -ENOMEM;
++
++ /* Obtain the address to map to. we verify (or select) it and ensure
++ * that it represents a valid section of the address space.
++ */
++ addr = get_unmapped_area(file, addr, len, pgoff, flags);
++ if (addr & ~PAGE_MASK)
++ return addr;
++
++ /* Do simple checking here so the lower-level routines won't have
++ * to. we assume access permissions have been handled by the open
++ * of the memory object, so we don't do any here.
++ */
++ vm_flags = calc_vm_flags(prot,flags) | mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
++
++ /* mlock MCL_FUTURE? */
++ if (vm_flags & VM_LOCKED) {
++ unsigned long locked = mm->locked_vm << PAGE_SHIFT;
++ locked += len;
++ if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
++ return -EAGAIN;
++ }
++
++ if (file) {
++ switch (flags & MAP_TYPE) {
++ case MAP_SHARED:
++ if ((prot & PROT_WRITE) && !(file->f_mode & FMODE_WRITE))
++ return -EACCES;
++
++ /* Make sure we don't allow writing to an append-only file.. */
++ if (IS_APPEND(file->f_dentry->d_inode) && (file->f_mode & FMODE_WRITE))
++ return -EACCES;
++
++ /* make sure there are no mandatory locks on the file. */
++ if (locks_verify_locked(file->f_dentry->d_inode))
++ return -EAGAIN;
++
++ vm_flags |= VM_SHARED | VM_MAYSHARE;
++ if (!(file->f_mode & FMODE_WRITE))
++ vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
++
++ /* fall through */
++ case MAP_PRIVATE:
++ if (!(file->f_mode & FMODE_READ))
++ return -EACCES;
++ break;
++
++ default:
++ return -EINVAL;
++ }
++ } else {
++ vm_flags |= VM_SHARED | VM_MAYSHARE;
++ switch (flags & MAP_TYPE) {
++ default:
++ return -EINVAL;
++ case MAP_PRIVATE:
++ vm_flags &= ~(VM_SHARED | VM_MAYSHARE);
++ /* fall through */
++ case MAP_SHARED:
++ break;
++ }
++ }
++
++ /* Clear old maps */
++munmap_back:
++ vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);
++ if (vma && vma->vm_start < addr + len) {
++ if (do_munmap(mm, addr, len))
++ return -ENOMEM;
++ goto munmap_back;
++ }
++
++ /* Check against address space limit. */
++ if ((mm->total_vm << PAGE_SHIFT) + len
++ > current->rlim[RLIMIT_AS].rlim_cur)
++ return -ENOMEM;
++
++ /* Private writable mapping? Check memory availability.. */
++ if ((vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
++ !(flags & MAP_NORESERVE) &&
++ !vm_enough_memory(len >> PAGE_SHIFT))
++ return -ENOMEM;
++
++ /* Can we just expand an old anonymous mapping? */
++ if (!file && !(vm_flags & VM_SHARED) && rb_parent)
++ if (vma_merge(mm, prev, rb_parent, addr, addr + len, vm_flags))
++ goto out;
++
++ /* Determine the object being mapped and call the appropriate
++ * specific mapper. the address has already been validated, but
++ * not unmapped, but the maps are removed from the list.
++ */
++ vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
++ if (!vma)
++ return -ENOMEM;
++
++ vma->vm_mm = mm;
++ vma->vm_start = addr;
++ vma->vm_end = addr + len;
++ vma->vm_flags = vm_flags;
++ vma->vm_page_prot = protection_map[vm_flags & 0x0f];
++ vma->vm_ops = NULL;
++ vma->vm_pgoff = pgoff;
++ vma->vm_file = NULL;
++ vma->vm_private_data = NULL;
++ vma->vm_raend = 0;
++
++ if (file) {
++ error = -EINVAL;
++ if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
++ goto free_vma;
++ if (vm_flags & VM_DENYWRITE) {
++ error = deny_write_access(file);
++ if (error)
++ goto free_vma;
++ correct_wcount = 1;
++ }
++ vma->vm_file = file;
++ get_file(file);
++ error = file->f_op->mmap(file, vma);
++ if (error)
++ goto unmap_and_free_vma;
++ } else if (flags & MAP_SHARED) {
++ error = shmem_zero_setup(vma);
++ if (error)
++ goto free_vma;
++ }
++
++ /* Can addr have changed??
++ *
++ * Answer: Yes, several device drivers can do it in their
++ * f_op->mmap method. -DaveM
++ */
++ if (addr != vma->vm_start) {
++ /*
++ * It is a bit too late to pretend changing the virtual
++ * area of the mapping, we just corrupted userspace
++ * in the do_munmap, so FIXME (not in 2.4 to avoid breaking
++ * the driver API).
++ */
++ struct vm_area_struct * stale_vma;
++ /* Since addr changed, we rely on the mmap op to prevent
++ * collisions with existing vmas and just use find_vma_prepare
++ * to update the tree pointers.
++ */
++ addr = vma->vm_start;
++ stale_vma = find_vma_prepare(mm, addr, &prev,
++ &rb_link, &rb_parent);
++ /*
++ * Make sure the lowlevel driver did its job right.
++ */
++ if (unlikely(stale_vma && stale_vma->vm_start < vma->vm_end)) {
++ printk(KERN_ERR "buggy mmap operation: [<%p>]\n",
++ file ? file->f_op->mmap : NULL);
++ BUG();
++ }
++ }
++
++ vma_link(mm, vma, prev, rb_link, rb_parent);
++ if (correct_wcount)
++ atomic_inc(&file->f_dentry->d_inode->i_writecount);
++
++out:
++ mm->total_vm += len >> PAGE_SHIFT;
++ if (vm_flags & VM_LOCKED) {
++ mm->locked_vm += len >> PAGE_SHIFT;
++ make_pages_present(addr, addr + len);
++ }
++ return addr;
++
++unmap_and_free_vma:
++ if (correct_wcount)
++ atomic_inc(&file->f_dentry->d_inode->i_writecount);
++ vma->vm_file = NULL;
++ fput(file);
++
++ /* Undo any partial mapping done by a device driver. */
++ zap_page_range(mm, vma->vm_start, vma->vm_end - vma->vm_start);
++free_vma:
++ kmem_cache_free(vm_area_cachep, vma);
++ return error;
++}
++
++/* Get an address range which is currently unmapped.
++ * For shmat() with addr=0.
++ *
++ * Ugly calling convention alert:
++ * Return value with the low bits set means error value,
++ * ie
++ * if (ret & ~PAGE_MASK)
++ * error = ret;
++ *
++ * This function "knows" that -ENOMEM has the bits set.
++ */
++#ifndef HAVE_ARCH_UNMAPPED_AREA
++static inline unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
++{
++ struct vm_area_struct *vma;
++
++ if (len > TASK_SIZE)
++ return -ENOMEM;
++
++ if (addr) {
++ addr = PAGE_ALIGN(addr);
++ vma = find_vma(current->mm, addr);
++ if (TASK_SIZE - len >= addr &&
++ (!vma || addr + len <= vma->vm_start))
++ return addr;
++ }
++ addr = PAGE_ALIGN(TASK_UNMAPPED_BASE);
++
++ for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
++ /* At this point: (!vma || addr < vma->vm_end). */
++ if (TASK_SIZE - len < addr)
++ return -ENOMEM;
++ if (!vma || addr + len <= vma->vm_start)
++ return addr;
++ addr = vma->vm_end;
++ }
++}
++#else
++extern unsigned long arch_get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++#endif
++
++unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
++{
++ if (flags & MAP_FIXED) {
++ if (addr > TASK_SIZE - len)
++ return -ENOMEM;
++ if (addr & ~PAGE_MASK)
++ return -EINVAL;
++ return addr;
++ }
++
++ if (file && file->f_op && file->f_op->get_unmapped_area)
++ return file->f_op->get_unmapped_area(file, addr, len, pgoff, flags);
++
++ return arch_get_unmapped_area(file, addr, len, pgoff, flags);
++}
++
++/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
++struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr)
++{
++ struct vm_area_struct *vma = NULL;
++
++ if (mm) {
++ /* Check the cache first. */
++ /* (Cache hit rate is typically around 35%.) */
++ vma = mm->mmap_cache;
++ if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {
++ rb_node_t * rb_node;
++
++ rb_node = mm->mm_rb.rb_node;
++ vma = NULL;
++
++ while (rb_node) {
++ struct vm_area_struct * vma_tmp;
++
++ vma_tmp = rb_entry(rb_node, struct vm_area_struct, vm_rb);
++
++ if (vma_tmp->vm_end > addr) {
++ vma = vma_tmp;
++ if (vma_tmp->vm_start <= addr)
++ break;
++ rb_node = rb_node->rb_left;
++ } else
++ rb_node = rb_node->rb_right;
++ }
++ if (vma)
++ mm->mmap_cache = vma;
++ }
++ }
++ return vma;
++}
++
++/* Same as find_vma, but also return a pointer to the previous VMA in *pprev. */
++struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr,
++ struct vm_area_struct **pprev)
++{
++ if (mm) {
++ /* Go through the RB tree quickly. */
++ struct vm_area_struct * vma;
++ rb_node_t * rb_node, * rb_last_right, * rb_prev;
++
++ rb_node = mm->mm_rb.rb_node;
++ rb_last_right = rb_prev = NULL;
++ vma = NULL;
++
++ while (rb_node) {
++ struct vm_area_struct * vma_tmp;
++
++ vma_tmp = rb_entry(rb_node, struct vm_area_struct, vm_rb);
++
++ if (vma_tmp->vm_end > addr) {
++ vma = vma_tmp;
++ rb_prev = rb_last_right;
++ if (vma_tmp->vm_start <= addr)
++ break;
++ rb_node = rb_node->rb_left;
++ } else {
++ rb_last_right = rb_node;
++ rb_node = rb_node->rb_right;
++ }
++ }
++ if (vma) {
++ if (vma->vm_rb.rb_left) {
++ rb_prev = vma->vm_rb.rb_left;
++ while (rb_prev->rb_right)
++ rb_prev = rb_prev->rb_right;
++ }
++ *pprev = NULL;
++ if (rb_prev)
++ *pprev = rb_entry(rb_prev, struct vm_area_struct, vm_rb);
++ if ((rb_prev ? (*pprev)->vm_next : mm->mmap) != vma)
++ BUG();
++ return vma;
++ }
++ }
++ *pprev = NULL;
++ return NULL;
++}
++
++struct vm_area_struct * find_extend_vma(struct mm_struct * mm, unsigned long addr)
++{
++ struct vm_area_struct * vma;
++ unsigned long start;
++
++ addr &= PAGE_MASK;
++ vma = find_vma(mm,addr);
++ if (!vma)
++ return NULL;
++ if (vma->vm_start <= addr)
++ return vma;
++ if (!(vma->vm_flags & VM_GROWSDOWN))
++ return NULL;
++ start = vma->vm_start;
++ if (expand_stack(vma, addr))
++ return NULL;
++ if (vma->vm_flags & VM_LOCKED) {
++ make_pages_present(addr, start);
++ }
++ return vma;
++}
++
++/* Normal function to fix up a mapping
++ * This function is the default for when an area has no specific
++ * function. This may be used as part of a more specific routine.
++ * This function works out what part of an area is affected and
++ * adjusts the mapping information. Since the actual page
++ * manipulation is done in do_mmap(), none need be done here,
++ * though it would probably be more appropriate.
++ *
++ * By the time this function is called, the area struct has been
++ * removed from the process mapping list, so it needs to be
++ * reinserted if necessary.
++ *
++ * The 4 main cases are:
++ * Unmapping the whole area
++ * Unmapping from the start of the segment to a point in it
++ * Unmapping from an intermediate point to the end
++ * Unmapping between to intermediate points, making a hole.
++ *
++ * Case 4 involves the creation of 2 new areas, for each side of
++ * the hole. If possible, we reuse the existing area rather than
++ * allocate a new one, and the return indicates whether the old
++ * area was reused.
++ */
++static struct vm_area_struct * unmap_fixup(struct mm_struct *mm,
++ struct vm_area_struct *area, unsigned long addr, size_t len,
++ struct vm_area_struct *extra)
++{
++ struct vm_area_struct *mpnt;
++ unsigned long end = addr + len;
++
++ area->vm_mm->total_vm -= len >> PAGE_SHIFT;
++ if (area->vm_flags & VM_LOCKED)
++ area->vm_mm->locked_vm -= len >> PAGE_SHIFT;
++
++ /* Unmapping the whole area. */
++ if (addr == area->vm_start && end == area->vm_end) {
++ if (area->vm_ops && area->vm_ops->close)
++ area->vm_ops->close(area);
++ if (area->vm_file)
++ fput(area->vm_file);
++ kmem_cache_free(vm_area_cachep, area);
++ return extra;
++ }
++
++ /* Work out to one of the ends. */
++ if (end == area->vm_end) {
++ /*
++ * here area isn't visible to the semaphore-less readers
++ * so we don't need to update it under the spinlock.
++ */
++ area->vm_end = addr;
++ lock_vma_mappings(area);
++ spin_lock(&mm->page_table_lock);
++ } else if (addr == area->vm_start) {
++ area->vm_pgoff += (end - area->vm_start) >> PAGE_SHIFT;
++ /* same locking considerations of the above case */
++ area->vm_start = end;
++ lock_vma_mappings(area);
++ spin_lock(&mm->page_table_lock);
++ } else {
++ /* Unmapping a hole: area->vm_start < addr <= end < area->vm_end */
++ /* Add end mapping -- leave beginning for below */
++ mpnt = extra;
++ extra = NULL;
++
++ mpnt->vm_mm = area->vm_mm;
++ mpnt->vm_start = end;
++ mpnt->vm_end = area->vm_end;
++ mpnt->vm_page_prot = area->vm_page_prot;
++ mpnt->vm_flags = area->vm_flags;
++ mpnt->vm_raend = 0;
++ mpnt->vm_ops = area->vm_ops;
++ mpnt->vm_pgoff = area->vm_pgoff + ((end - area->vm_start) >> PAGE_SHIFT);
++ mpnt->vm_file = area->vm_file;
++ mpnt->vm_private_data = area->vm_private_data;
++ if (mpnt->vm_file)
++ get_file(mpnt->vm_file);
++ if (mpnt->vm_ops && mpnt->vm_ops->open)
++ mpnt->vm_ops->open(mpnt);
++ area->vm_end = addr; /* Truncate area */
++
++ /* Because mpnt->vm_file == area->vm_file this locks
++ * things correctly.
++ */
++ lock_vma_mappings(area);
++ spin_lock(&mm->page_table_lock);
++ __insert_vm_struct(mm, mpnt);
++ }
++
++ __insert_vm_struct(mm, area);
++ spin_unlock(&mm->page_table_lock);
++ unlock_vma_mappings(area);
++ return extra;
++}
++
++/*
++ * Try to free as many page directory entries as we can,
++ * without having to work very hard at actually scanning
++ * the page tables themselves.
++ *
++ * Right now we try to free page tables if we have a nice
++ * PGDIR-aligned area that got free'd up. We could be more
++ * granular if we want to, but this is fast and simple,
++ * and covers the bad cases.
++ *
++ * "prev", if it exists, points to a vma before the one
++ * we just free'd - but there's no telling how much before.
++ */
++static void free_pgtables(struct mm_struct * mm, struct vm_area_struct *prev,
++ unsigned long start, unsigned long end)
++{
++ unsigned long first = start & PGDIR_MASK;
++ unsigned long last = end + PGDIR_SIZE - 1;
++ unsigned long start_index, end_index;
++
++ if (!prev) {
++ prev = mm->mmap;
++ if (!prev)
++ goto no_mmaps;
++ if (prev->vm_end > start) {
++ if (last > prev->vm_start)
++ last = prev->vm_start;
++ goto no_mmaps;
++ }
++ }
++ for (;;) {
++ struct vm_area_struct *next = prev->vm_next;
++
++ if (next) {
++ if (next->vm_start < start) {
++ prev = next;
++ continue;
++ }
++ if (last > next->vm_start)
++ last = next->vm_start;
++ }
++ if (prev->vm_end > first)
++ first = prev->vm_end + PGDIR_SIZE - 1;
++ break;
++ }
++no_mmaps:
++ if (last < first)
++ return;
++ /*
++ * If the PGD bits are not consecutive in the virtual address, the
++ * old method of shifting the VA >> by PGDIR_SHIFT doesn't work.
++ */
++ start_index = pgd_index(first);
++ end_index = pgd_index(last);
++ if (end_index > start_index) {
++ clear_page_tables(mm, start_index, end_index - start_index);
++ flush_tlb_pgtables(mm, first & PGDIR_MASK, last & PGDIR_MASK);
++ }
++}
++
++/* Munmap is split into 2 main parts -- this part which finds
++ * what needs doing, and the areas themselves, which do the
++ * work. This now handles partial unmappings.
++ * Jeremy Fitzhardine <jeremy at sw.oz.au>
++ */
++int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len)
++{
++ struct vm_area_struct *mpnt, *prev, **npp, *free, *extra;
++
++ if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
++ return -EINVAL;
++
++ if ((len = PAGE_ALIGN(len)) == 0)
++ return -EINVAL;
++
++ /* Check if this memory area is ok - put it on the temporary
++ * list if so.. The checks here are pretty simple --
++ * every area affected in some way (by any overlap) is put
++ * on the list. If nothing is put on, nothing is affected.
++ */
++ mpnt = find_vma_prev(mm, addr, &prev);
++ if (!mpnt)
++ return 0;
++ /* we have addr < mpnt->vm_end */
++
++ if (mpnt->vm_start >= addr+len)
++ return 0;
++
++ /* If we'll make "hole", check the vm areas limit */
++ if ((mpnt->vm_start < addr && mpnt->vm_end > addr+len)
++ && mm->map_count >= max_map_count)
++ return -ENOMEM;
++
++ /*
++ * We may need one additional vma to fix up the mappings ...
++ * and this is the last chance for an easy error exit.
++ */
++ extra = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
++ if (!extra)
++ return -ENOMEM;
++
++ npp = (prev ? &prev->vm_next : &mm->mmap);
++ free = NULL;
++ spin_lock(&mm->page_table_lock);
++ for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) {
++ *npp = mpnt->vm_next;
++ mpnt->vm_next = free;
++ free = mpnt;
++ rb_erase(&mpnt->vm_rb, &mm->mm_rb);
++ }
++ mm->mmap_cache = NULL; /* Kill the cache. */
++ spin_unlock(&mm->page_table_lock);
++
++ /* Ok - we have the memory areas we should free on the 'free' list,
++ * so release them, and unmap the page range..
++ * If the one of the segments is only being partially unmapped,
++ * it will put new vm_area_struct(s) into the address space.
++ * In that case we have to be careful with VM_DENYWRITE.
++ */
++ while ((mpnt = free) != NULL) {
++ unsigned long st, end, size;
++ struct file *file = NULL;
++
++ free = free->vm_next;
++
++ st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
++ end = addr+len;
++ end = end > mpnt->vm_end ? mpnt->vm_end : end;
++ size = end - st;
++
++ if (mpnt->vm_flags & VM_DENYWRITE &&
++ (st != mpnt->vm_start || end != mpnt->vm_end) &&
++ (file = mpnt->vm_file) != NULL) {
++ atomic_dec(&file->f_dentry->d_inode->i_writecount);
++ }
++ remove_shared_vm_struct(mpnt);
++ mm->map_count--;
++
++ zap_page_range(mm, st, size);
++
++ /*
++ * Fix the mapping, and free the old area if it wasn't reused.
++ */
++ extra = unmap_fixup(mm, mpnt, st, size, extra);
++ if (file)
++ atomic_inc(&file->f_dentry->d_inode->i_writecount);
++ }
++ validate_mm(mm);
++
++ /* Release the extra vma struct if it wasn't used */
++ if (extra)
++ kmem_cache_free(vm_area_cachep, extra);
++
++ free_pgtables(mm, prev, addr, addr+len);
++
++ return 0;
++}
++
++asmlinkage long sys_munmap(unsigned long addr, size_t len)
++{
++ int ret;
++ struct mm_struct *mm = current->mm;
++
++ down_write(&mm->mmap_sem);
++ ret = do_munmap(mm, addr, len);
++ up_write(&mm->mmap_sem);
++ return ret;
++}
++
++/*
++ * this is really a simplified "do_mmap". it only handles
++ * anonymous maps. eventually we may be able to do some
++ * brk-specific accounting here.
++ */
++unsigned long do_brk(unsigned long addr, unsigned long len)
++{
++ struct mm_struct * mm = current->mm;
++ struct vm_area_struct * vma, * prev;
++ unsigned long flags;
++ rb_node_t ** rb_link, * rb_parent;
++
++ len = PAGE_ALIGN(len);
++ if (!len)
++ return addr;
++
++ if ((addr + len) > TASK_SIZE || (addr + len) < addr)
++ return -EINVAL;
++
++ /*
++ * mlock MCL_FUTURE?
++ */
++ if (mm->def_flags & VM_LOCKED) {
++ unsigned long locked = mm->locked_vm << PAGE_SHIFT;
++ locked += len;
++ if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
++ return -EAGAIN;
++ }
++
++ /*
++ * Clear old maps. this also does some error checking for us
++ */
++ munmap_back:
++ vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);
++ if (vma && vma->vm_start < addr + len) {
++ if (do_munmap(mm, addr, len))
++ return -ENOMEM;
++ goto munmap_back;
++ }
++
++ /* Check against address space limits *after* clearing old maps... */
++ if ((mm->total_vm << PAGE_SHIFT) + len
++ > current->rlim[RLIMIT_AS].rlim_cur)
++ return -ENOMEM;
++
++ if (mm->map_count > max_map_count)
++ return -ENOMEM;
++
++ if (!vm_enough_memory(len >> PAGE_SHIFT))
++ return -ENOMEM;
++
++ flags = VM_DATA_DEFAULT_FLAGS | mm->def_flags;
++
++ /* Can we just expand an old anonymous mapping? */
++ if (rb_parent && vma_merge(mm, prev, rb_parent, addr, addr + len, flags))
++ goto out;
++
++ /*
++ * create a vma struct for an anonymous mapping
++ */
++ vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
++ if (!vma)
++ return -ENOMEM;
++
++ vma->vm_mm = mm;
++ vma->vm_start = addr;
++ vma->vm_end = addr + len;
++ vma->vm_flags = flags;
++ vma->vm_page_prot = protection_map[flags & 0x0f];
++ vma->vm_ops = NULL;
++ vma->vm_pgoff = 0;
++ vma->vm_file = NULL;
++ vma->vm_private_data = NULL;
++
++ vma_link(mm, vma, prev, rb_link, rb_parent);
++
++out:
++ mm->total_vm += len >> PAGE_SHIFT;
++ if (flags & VM_LOCKED) {
++ mm->locked_vm += len >> PAGE_SHIFT;
++ make_pages_present(addr, addr + len);
++ }
++ return addr;
++}
++
++/* locking version of do_brk. */
++unsigned long do_brk_locked(unsigned long addr, unsigned long len)
++{
++ unsigned long ret;
++
++ down_write(¤t->mm->mmap_sem);
++ ret = do_brk(addr, len);
++ up_write(¤t->mm->mmap_sem);
++
++ return ret;
++}
++
++
++
++
++/* Build the RB tree corresponding to the VMA list. */
++void build_mmap_rb(struct mm_struct * mm)
++{
++ struct vm_area_struct * vma;
++ rb_node_t ** rb_link, * rb_parent;
++
++ mm->mm_rb = RB_ROOT;
++ rb_link = &mm->mm_rb.rb_node;
++ rb_parent = NULL;
++ for (vma = mm->mmap; vma; vma = vma->vm_next) {
++ __vma_link_rb(mm, vma, rb_link, rb_parent);
++ rb_parent = &vma->vm_rb;
++ rb_link = &rb_parent->rb_right;
++ }
++}
++
++/* Release all mmaps. */
++void exit_mmap(struct mm_struct * mm)
++{
++ struct vm_area_struct * mpnt;
++
++ release_segments(mm);
++ spin_lock(&mm->page_table_lock);
++ mpnt = mm->mmap;
++ mm->mmap = mm->mmap_cache = NULL;
++ mm->mm_rb = RB_ROOT;
++ mm->rss = 0;
++ spin_unlock(&mm->page_table_lock);
++ mm->total_vm = 0;
++ mm->locked_vm = 0;
++
++ flush_cache_mm(mm);
++ while (mpnt) {
++ struct vm_area_struct * next = mpnt->vm_next;
++ unsigned long start = mpnt->vm_start;
++ unsigned long end = mpnt->vm_end;
++ unsigned long size = end - start;
++
++ if (mpnt->vm_ops) {
++ if (mpnt->vm_ops->close)
++ mpnt->vm_ops->close(mpnt);
++ }
++ mm->map_count--;
++ remove_shared_vm_struct(mpnt);
++ zap_page_range(mm, start, size);
++ if (mpnt->vm_file)
++ fput(mpnt->vm_file);
++ kmem_cache_free(vm_area_cachep, mpnt);
++ mpnt = next;
++ }
++
++ /* This is just debugging */
++ if (mm->map_count)
++ BUG();
++
++ clear_page_tables(mm, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);
++
++ flush_tlb_mm(mm);
++}
++
++/* Insert vm structure into process list sorted by address
++ * and into the inode's i_mmap ring. If vm_file is non-NULL
++ * then the i_shared_lock must be held here.
++ */
++void __insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
++{
++ struct vm_area_struct * __vma, * prev;
++ rb_node_t ** rb_link, * rb_parent;
++
++ __vma = find_vma_prepare(mm, vma->vm_start, &prev, &rb_link, &rb_parent);
++ if (__vma && __vma->vm_start < vma->vm_end)
++ BUG();
++ __vma_link(mm, vma, prev, rb_link, rb_parent);
++ mm->map_count++;
++ validate_mm(mm);
++}
++
++void insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
++{
++ struct vm_area_struct * __vma, * prev;
++ rb_node_t ** rb_link, * rb_parent;
++
++ __vma = find_vma_prepare(mm, vma->vm_start, &prev, &rb_link, &rb_parent);
++ if (__vma && __vma->vm_start < vma->vm_end)
++ BUG();
++ vma_link(mm, vma, prev, rb_link, rb_parent);
++ validate_mm(mm);
++}
+diff -urN kernel-source-2.4.27-8/mm/slab.c kernel-source-2.4.27-8-arm-1/mm/slab.c
+--- kernel-source-2.4.27-8/mm/slab.c 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/mm/slab.c 2005-02-18 17:48:45.000000000 +0000
+@@ -1391,7 +1391,7 @@
+ #if DEBUG
+ # define CHECK_NR(pg) \
+ do { \
+- if (!VALID_PAGE(pg)) { \
++ if (!virt_addr_valid(pg)) { \
+ printk(KERN_ERR "kfree: out of range ptr %lxh.\n", \
+ (unsigned long)objp); \
+ BUG(); \
+@@ -1955,7 +1955,7 @@
+ set_fs(KERNEL_DS);
+ if (__get_user(tmp, name))
+ name = "broken";
+- set_fs(old_fs);
++ set_fs (old_fs);
+ }
+
+ seq_printf(m, "%-17s %6lu %6lu %6u %4lu %4lu %4u",
+diff -urN kernel-source-2.4.27-8/mm/vmalloc.c kernel-source-2.4.27-8-arm-1/mm/vmalloc.c
+--- kernel-source-2.4.27-8/mm/vmalloc.c 2005-01-19 09:57:47.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/mm/vmalloc.c 2005-02-18 17:48:45.000000000 +0000
+@@ -44,8 +44,13 @@
+ if (pte_none(page))
+ continue;
+ if (pte_present(page)) {
+- struct page *ptpage = pte_page(page);
+- if (VALID_PAGE(ptpage) && (!PageReserved(ptpage)))
++ unsigned long pfn = pte_pfn(page);
++ struct page *ptpage;
++
++ if (!pfn_valid(pfn))
++ continue;
++ ptpage = pfn_to_page(pfn);
++ if (!PageReserved(ptpage))
+ __free_page(ptpage);
+ continue;
+ }
+diff -urN kernel-source-2.4.27-8/mm/vmscan.c kernel-source-2.4.27-8-arm-1/mm/vmscan.c
+--- kernel-source-2.4.27-8/mm/vmscan.c 2005-01-19 09:57:51.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/mm/vmscan.c 2005-02-18 17:48:45.000000000 +0000
+@@ -165,6 +165,7 @@
+ drop_pte:
+ mm->rss--;
+ UnlockPage(page);
++ memc_clear(vma->vm_mm, page);
+ {
+ int freeable = page_count(page) - !!page->buffers <= 2;
+ page_cache_release(page);
+@@ -250,9 +251,11 @@
+
+ do {
+ if (pte_present(*pte)) {
+- struct page *page = pte_page(*pte);
++ unsigned long pfn = pte_pfn(*pte);
+
+- if (VALID_PAGE(page) && !PageReserved(page)) {
++ if (pfn_valid(pfn)) {
++ struct page *page = pfn_to_page(pfn);
++ if (!PageReserved(page)) {
+ count -= try_to_swap_out(mm, vma, address, pte, page, classzone);
+ if (!count) {
+ address += PAGE_SIZE;
+@@ -260,6 +263,7 @@
+ }
+ }
+ }
++ }
+ address += PAGE_SIZE;
+ pte++;
+ } while (address && (address < end));
+diff -urN kernel-source-2.4.27-8/net/irda/af_irda.c kernel-source-2.4.27-8-arm-1/net/irda/af_irda.c
+--- kernel-source-2.4.27-8/net/irda/af_irda.c 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/net/irda/af_irda.c 2005-02-18 17:48:45.000000000 +0000
+@@ -2581,9 +2581,6 @@
+ #endif
+ return 0;
+ }
+-#ifdef MODULE
+-module_init(irda_proto_init); /* If non-module, called from init/main.c */
+-#endif
+
+ /*
+ * Function irda_proto_cleanup (void)
+@@ -2591,8 +2588,7 @@
+ * Remove IrDA protocol layer
+ *
+ */
+-#ifdef MODULE
+-void irda_proto_cleanup(void)
++static void __exit irda_proto_cleanup(void)
+ {
+ irda_packet_type.type = htons(ETH_P_IRDA);
+ dev_remove_pack(&irda_packet_type);
+@@ -2600,10 +2596,12 @@
+ unregister_netdevice_notifier(&irda_dev_notifier);
+
+ sock_unregister(PF_IRDA);
+- irda_cleanup();
+
+- return;
++ irda_cleanup();
+ }
++
++#ifdef MODULE
++module_init(irda_proto_init);
+ module_exit(irda_proto_cleanup);
+
+ MODULE_AUTHOR("Dag Brattli <dagb at cs.uit.no>");
+@@ -2612,4 +2610,5 @@
+ #ifdef CONFIG_IRDA_DEBUG
+ MODULE_PARM(irda_debug, "1l");
+ #endif
+-#endif /* MODULE */
++#endif
++
+diff -urN kernel-source-2.4.27-8/net/irda/iriap.c kernel-source-2.4.27-8-arm-1/net/irda/iriap.c
+--- kernel-source-2.4.27-8/net/irda/iriap.c 2002-11-28 23:53:16.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/net/irda/iriap.c 2005-02-18 17:48:45.000000000 +0000
+@@ -1004,18 +1004,20 @@
+
+ switch (attrib->value->type) {
+ case IAS_INTEGER:
+- len += sprintf(buf+len, "%d\n",
++ len += sprintf(buf+len, "%d",
+ attrib->value->t.integer);
+ break;
+ case IAS_STRING:
+- len += sprintf(buf+len, "\"%s\"\n",
++ len += sprintf(buf+len, "\"%s\"",
+ attrib->value->t.string);
+ break;
+ case IAS_OCT_SEQ:
+- len += sprintf(buf+len, "octet sequence (%d bytes)\n", attrib->value->len);
++ len += sprintf(buf+len,
++ "octet sequence (%d bytes)",
++ attrib->value->len);
+ break;
+ case IAS_MISSING:
+- len += sprintf(buf+len, "missing\n");
++ len += sprintf(buf+len, "missing");
+ break;
+ default:
+ IRDA_DEBUG(0, "%s(), Unknown value type!\n", __FUNCTION__);
+@@ -1027,6 +1029,7 @@
+ hashbin_get_next(obj->attribs);
+ }
+ obj = (struct ias_object *) hashbin_get_next(objects);
++ len += sprintf(buf+len, "\n");
+ }
+ restore_flags(flags);
+
+diff -urN kernel-source-2.4.27-8/net/irda/irlan/irlan_common.c kernel-source-2.4.27-8-arm-1/net/irda/irlan/irlan_common.c
+--- kernel-source-2.4.27-8/net/irda/irlan/irlan_common.c 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/net/irda/irlan/irlan_common.c 2005-02-18 17:48:45.000000000 +0000
+@@ -317,6 +317,9 @@
+
+ del_timer(&self->watchdog_timer);
+
++ /* since irlan_do_*_event gobble the skb, we must get it once -- rmk */
++ skb_get(skb);
++
+ /* If you want to pass the skb to *both* state machines, you will
+ * need to skb_clone() it, so that you don't free it twice.
+ * As the state machines don't need it, git rid of it here...
+diff -urN kernel-source-2.4.27-8/net/irda/irlap_event.c kernel-source-2.4.27-8-arm-1/net/irda/irlap_event.c
+--- kernel-source-2.4.27-8/net/irda/irlap_event.c 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/net/irda/irlap_event.c 2005-02-18 17:48:45.000000000 +0000
+@@ -532,7 +532,7 @@
+ #endif /* CONFIG_IRDA_ULTRA */
+ case RECV_TEST_CMD:
+ /* Remove test frame header */
+- skb_pull(skb, sizeof(struct test_frame));
++ skb_pull(skb, 10 /*sizeof(struct test_frame)*/);
+
+ /*
+ * Send response. This skb will not be sent out again, and
+@@ -740,7 +740,7 @@
+
+ switch (event) {
+ case CONNECT_RESPONSE:
+- skb_pull(skb, sizeof(struct snrm_frame));
++ skb_pull(skb, 11 /*sizeof(struct snrm_frame)*/);
+
+ ASSERT(self->netdev != NULL, return -1;);
+
+@@ -870,7 +870,7 @@
+
+ ASSERT(self->netdev != NULL, return -1;);
+
+- skb_pull(skb, sizeof(struct snrm_frame));
++ skb_pull(skb, 11 /*sizeof(struct snrm_frame)*/);
+
+ irlap_qos_negotiate(self, skb);
+
+@@ -902,7 +902,7 @@
+ /* Negotiate connection parameters */
+ ASSERT(skb->len > 10, return -1;);
+
+- skb_pull(skb, sizeof(struct ua_frame));
++ skb_pull(skb, 10 /*sizeof(struct ua_frame)*/);
+
+ ASSERT(self->netdev != NULL, return -1;);
+
+diff -urN kernel-source-2.4.27-8/net/irda/irlap_frame.c kernel-source-2.4.27-8-arm-1/net/irda/irlap_frame.c
+--- kernel-source-2.4.27-8/net/irda/irlap_frame.c 2003-11-28 18:26:21.000000000 +0000
++++ kernel-source-2.4.27-8-arm-1/net/irda/irlap_frame.c 2005-02-18 17:48:45.000000000 +0000
+@@ -34,6 +34,7 @@
+ #include <net/sock.h>
+
+ #include <asm/byteorder.h>
++#include <asm/unaligned.h>
+
+ #include <net/irda/irda.h>
+ #include <net/irda/irda_device.h>
+@@ -134,8 +135,8 @@
+ */
+ if (qos) {
+ skb_put(skb, 9); /* 21 left */
+- frame->saddr = cpu_to_le32(self->saddr);
+- frame->daddr = cpu_to_le32(self->daddr);
++ put_unaligned(cpu_to_le32(self->saddr), &frame->saddr);
++ put_unaligned(cpu_to_le32(self->daddr), &frame->daddr);
+
+ frame->ncaddr = self->caddr;
+
+@@ -172,8 +173,8 @@
+ }
+
+ /* Copy peer device address */
+- info->daddr = le32_to_cpu(frame->saddr);
+- info->saddr = le32_to_cpu(frame->daddr);
++ info->daddr = le32_to_cpu(get_unaligned(&frame->saddr));
++ info->saddr = le32_to_cpu(get_unaligned(&frame->daddr));
+
+ /* Only accept if addressed directly to us */
+ if (info->saddr != self->saddr) {
+@@ -217,8 +218,8 @@
+ frame->caddr = self->caddr;
+ frame->control = UA_RSP | PF_BIT;
+
+- frame->saddr = cpu_to_le32(self->saddr);
+- frame->daddr = cpu_to_le32(self->daddr);
++ put_unaligned(cpu_to_le32(self->saddr), &frame->saddr);
++ put_unaligned(cpu_to_le32(self->daddr), &frame->daddr);
+
+ /* Should we send QoS negotiation parameters? */
+ if (qos) {
+@@ -302,7 +303,7 @@
+ {
+ struct sk_buff *skb = NULL;
+ struct xid_frame *frame;
+- __u32 bcast = BROADCAST;
++ __u32 bcast = BROADCAST, daddr;
+ __u8 *info;
+
+ IRDA_DEBUG(4, "%s(), s=%d, S=%d, command=%d\n", __FUNCTION__, s, S,
+@@ -328,12 +329,13 @@
+ }
+ frame->ident = XID_FORMAT;
+
+- frame->saddr = cpu_to_le32(self->saddr);
+-
+ if (command)
+- frame->daddr = cpu_to_le32(bcast);
++ daddr = bcast;
+ else
+- frame->daddr = cpu_to_le32(discovery->daddr);
++ daddr = discovery->daddr;
++
++ put_unaligned(cpu_to_le32(self->saddr), &frame->saddr);
++ put_unaligned(cpu_to_le32(daddr), &frame->daddr);
+
+ switch (S) {
+ case 1:
+@@ -404,8 +406,8 @@
+
+ xid = (struct xid_frame *) skb->data;
+
+- info->daddr = le32_to_cpu(xid->saddr);
+- info->saddr = le32_to_cpu(xid->daddr);
++ info->daddr = le32_to_cpu(get_unaligned(&xid->saddr));
++ info->saddr = le32_to_cpu(get_unaligned(&xid->daddr));
+
+ /* Make sure frame is addressed to us */
+ if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
+@@ -469,8 +471,8 @@
+
+ xid = (struct xid_frame *) skb->data;
+
+- info->daddr = le32_to_cpu(xid->saddr);
+- info->saddr = le32_to_cpu(xid->daddr);
++ info->daddr = le32_to_cpu(get_unaligned(&xid->saddr));
++ info->saddr = le32_to_cpu(get_unaligned(&xid->daddr));
+
+ /* Make sure frame is addressed to us */
+ if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
+@@ -1235,8 +1237,8 @@
+ }
+
+ /* Read and swap addresses */
+- info->daddr = le32_to_cpu(frame->saddr);
+- info->saddr = le32_to_cpu(frame->daddr);
++ info->daddr = le32_to_cpu(get_unaligned(&frame->saddr));
++ info->saddr = le32_to_cpu(get_unaligned(&frame->daddr));
+
+ /* Make sure frame is addressed to us */
+ if ((info->saddr != self->saddr) &&
+diff -urN kernel-source-2.4.27-8/net/sched/Config.in kernel-source-2.4.27-8-arm-1/net/sched/Config.in
+--- kernel-source-2.4.27-8/net/sched/Config.in 2004-08-08 00:26:07.000000000 +0100
++++ kernel-source-2.4.27-8-arm-1/net/sched/Config.in 2005-02-18 17:48:45.000000000 +0000
+@@ -3,9 +3,9 @@
+ #
+ tristate ' CBQ packet scheduler' CONFIG_NET_SCH_CBQ
+ tristate ' HTB packet scheduler' CONFIG_NET_SCH_HTB
+-tristate ' CSZ packet scheduler' CONFIG_NET_SCH_CSZ
++dep_tristate ' CSZ packet scheduler (experimental)' CONFIG_NET_SCH_CSZ $CONFIG_EXPERIMENTAL
+ #tristate ' H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ
+-tristate ' H-FSC packet scheduler' CONFIG_NET_SCH_HFSC
++tristate ' H-FSC packet scheduler' CONFIG_NET_SCH_HFCS
+ if [ "$CONFIG_ATM" = "y" -o "$CONFIG_ATM" = "m" ]; then
+ dep_tristate ' ATM pseudo-scheduler' CONFIG_NET_SCH_ATM $CONFIG_ATM
+ fi
More information about the Kernel-svn-changes
mailing list