# # Patch managed by http://www.holgerschurig.de/patcher.html # --- linux-2.4.27/Documentation/Configure.help~2.4.27-vrs1 +++ linux-2.4.27/Documentation/Configure.help @@ -4941,6 +4941,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 @@ -13338,6 +13345,17 @@ The module will be called tmspci.o. If you want to compile it as a module, say M here and read . +Altera ether00 support +CONFIG_ETHER00 + This is the driver for Altera's ether00 ethernet mac IP core. Say + Y here if you want to build support for this into the kernel. It + is also available as a module (say M here) that can be inserted/ + removed from the kernel at the same time as the PLD is configured. + If this driver is running on an epxa10 development board then it + will generate a suitable hw address based on the board serial + number (MTD support is required for this). Otherwise you will + need to set a suitable hw address using ifconfig. + Generic TMS380 ISA support CONFIG_TMSISA This tms380 module supports generic TMS380-based ISA cards. @@ -15292,6 +15310,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 @@ -19326,6 +19354,20 @@ . The module will be called i2c-velleman.o. +Guide GPIO adapter +CONFIG_I2C_GUIDE + This supports the Iders GUIDE I2C bit-bashing adapter. If you have + selected the GUIDE A07 as your ARM system type, you cannot deselect + this option, as it is required for proper operation of the GUIDE. + + This interface uses /dev/i2c-0 (major 89, minor 0). + + Say Y if you own such an adapter. + + This driver is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. The + module will be called i2c-guide.o. + I2C PCF 8584 interfaces CONFIG_I2C_ALGOPCF This allows you to use a range of I2C adapters called PCF adapters. @@ -20463,6 +20505,17 @@ . The module will be called softdog.o. +SA1100 Internal Watchdog +CONFIG_SA1100_WATCHDOG + Watchdog timer embedded into SA11x0 chips. This will reboot your + system when timeout is reached. + NOTE, that once enabled, this timer cannot be disabled. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read + Documentation/modules.txt. The module will be called sa1100_wdt.o. + Berkshire Products PC Watchdog CONFIG_PCWATCHDOG This is the driver for the Berkshire Products PC Watchdog card. @@ -22124,6 +22177,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 @@ -25818,6 +25895,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) @@ -25825,6 +25916,14 @@ for information on this system. +Radisys Corp. Tulsa +CONFIG_SA1100_PFS168 + The Radisys Corp. PFS-168 (aka Tulsa) is an Intel® StrongArm® SA-1110 based + computer which includes the SA-1111 Microprocessor Companion Chip and other + custom I/O designed to add connectivity and multimedia features for vending + and business machine applications. Say Y here if you require support for + this target. + # Choice: cerf_ram Cerf on-board RAM size CONFIG_SA1100_CERF_8MB @@ -25892,37 +25991,65 @@ Say Y if you want support for the ARM920T processor. Otherwise, say N. -Support ARM1020 processor -CONFIG_CPU_ARM1020 - The ARM1020 is the cached version of the ARM10 processor, - with an addition of a floating-point unit. +Support ARM922T processor +CONFIG_CPU_ARM922T + The ARM922T is a version of the ARM920T, but with smaller + instruction and data caches. It is used in Altera's + Excalibur XA device family. - Say Y if you want support for the ARM1020 processor. + Say Y if you want support for the ARM922T processor. Otherwise, say N. -Disable I-Cache +Disable instruction cache CONFIG_CPU_ICACHE_DISABLE - Say Y here to disable the processor instruction cache. Unless - you have a reason not to or are unsure, say N. + Say Y here to disable the processor instruction cache. Unless + you have a reason to do this, say N. -Disable D-Cache +Disable data cache CONFIG_CPU_DCACHE_DISABLE - Say Y here to disable the processor data cache. Unless - you have a reason not to or are unsure, say N. + Say Y here to disable the processor data cache. Unless + you have a reason to do this, say N. -Force write through D-cache +Use data cache in writethrough mode CONFIG_CPU_DCACHE_WRITETHROUGH - Say Y here to use the data cache in write-through mode. Unless you - specifically require this or are unsure, say N. + Say Y here to use the data cache in writethough mode. Unless you + specifically require this, say N. -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 @@ -25969,6 +26096,11 @@ Say Y here if you are using the inhand electronics OmniMeter. See for details. +HP Laboratories BadgePAD 4 +CONFIG_SA1100_BADGE4 + Say Y here if you want to build a kernel for the HP Laboratories + BadgePAD 4. + Load kernel using Angel Debug Monitor CONFIG_ANGELBOOT Say Y if you plan to load the kernel using Angel, ARM Ltd's target @@ -25981,6 +26113,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 @@ -26009,24 +26150,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. + 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. - 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. + 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 --- /dev/null +++ linux-2.4.27/Documentation/arm/Porting @@ -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. + --- /dev/null +++ linux-2.4.27/Documentation/arm/mem_alignment @@ -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. --- /dev/null +++ linux-2.4.27/Documentation/arm/memory.txt @@ -0,0 +1,74 @@ + Kernel Memory Layout on ARM Linux + + Russell King + April 27, 2003 (2.5.68) + +This document describes the virtual memory layout which the Linux +kernel uses for ARM processors. It indicates which regions are +free for platforms to use, and which are used by generic code. + +The ARM CPU is capable of addressing a maximum of 4GB virtual memory +space, and this must be shared between user space processes, the +kernel, and hardware devices. + +As the ARM architecture matures, it becomes necessary to reserve +certain regions of VM space for use for new facilities; therefore +this document may reserve more VM space over time. + +Start End Use +-------------------------------------------------------------------------- +ffff8000 ffffffff copy_user_page / clear_user_page use. + For SA11xx and Xscale, this is used to + setup a minicache mapping. + +ffff1000 ffff7fff Reserved. + Platforms must not use this address range. + +ffff0000 ffff0fff CPU vector page. + The CPU vectors are mapped here if the + CPU supports vector relocation (control + register V bit.) + +ffe00000 fffeffff Free for platform use, not recommended. + +ffc00000 ffdfffff 2MB consistent memory mapping. + Memory returned by the consistent_alloc + low level function will be dynamically + mapped here. + +ff000000 ffbfffff Free for platform use, not recommended. + +VMALLOC_END ff000000 Free for platform use, recommended. + +VMALLOC_START VMALLOC_END vmalloc() / ioremap() space. + Memory returned by vmalloc/ioremap will + be dynamically placed in this region. + VMALLOC_START may be based upon the value + of the high_memory variable. + +PAGE_OFFSET high_memory Kernel direct-mapped RAM region. + This maps the platforms RAM, and typically + maps all platform RAM in a 1:1 relationship. + +TASK_SIZE PAGE_OFFSET Kernel module space + Kernel modules inserted via insmod are + placed here using dynamic mappings. + +00001000 TASK_SIZE User space mappings + Per-thread mappings are placed here via + the mmap() system call. + +00000000 00000fff CPU vector page / null pointer trap + CPUs which do not support vector remapping + place their vector page here. NULL pointer + dereferences by both the kernel and user + space are also caught via this mapping. + +Please note that mappings which collide with the above areas may result +in a non-bootable kernel, or may cause the kernel to (eventually) panic +at run time. + +Since future CPUs may impact the kernel mapping layout, user programs +must not access any memory which is not mapped inside their 0x0001000 +to TASK_SIZE address range. If they wish to access these areas, they +must set up their own mappings using open() and mmap(). --- /dev/null +++ linux-2.4.27/Documentation/cpufreq/core.txt @@ -0,0 +1,94 @@ + CPU frequency and voltage scaling code in the Linux(TM) kernel + + + L i n u x C P U F r e q + + C P U F r e q C o r e + + + Dominik Brodowski + David Kimdon + + + + Clock scaling allows you to change the clock speed of the CPUs on the + fly. This is a nice method to save battery power, because the lower + the clock speed, the less power the CPU consumes. + + +Contents: +--------- +1. CPUFreq core and interfaces +2. CPUFreq notifiers + +1. General Information +======================= + +The CPUFreq core code is located in linux/kernel/cpufreq.c. This +cpufreq code offers a standardized interface for the CPUFreq +architecture drivers (those pieces of code that do actual +frequency transitions), as well as to "notifiers". These are device +drivers or other part of the kernel that need to be informed of +policy changes (ex. thermal modules like ACPI) or of all +frequency changes (ex. timing code) or even need to force certain +speed limits (like LCD drivers on ARM architecture). Additionally, the +kernel "constant" loops_per_jiffy is updated on frequency changes +here. + +Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu, +which make sure that the cpufreq processor driver is correctly +registered with the core, and will not be unloaded until +cpufreq_put_cpu is called. + +2. CPUFreq notifiers +==================== + +CPUFreq notifiers conform to the standard kernel notifier interface. +See linux/include/linux/notifier.h for details on notifiers. + +There are two different CPUFreq notifiers - policy notifiers and +transition notifiers. + + +2.1 CPUFreq policy notifiers +---------------------------- + +These are notified when a new policy is intended to be set. Each +CPUFreq policy notifier is called three times for a policy transition: + +1.) During CPUFREQ_ADJUST all CPUFreq notifiers may change the limit if + they see a need for this - may it be thermal considerations or + hardware limitations. + +2.) During CPUFREQ_INCOMPATIBLE only changes may be done in order to avoid + hardware failure. + +3.) And during CPUFREQ_NOTIFY all notifiers are informed of the new policy + - if two hardware drivers failed to agree on a new policy before this + stage, the incompatible hardware shall be shut down, and the user + informed of this. + +The phase is specified in the second argument to the notifier. + +The third argument, a void *pointer, points to a struct cpufreq_policy +consisting of five values: cpu, min, max, policy and max_cpu_freq. min +and max are the lower and upper frequencies (in kHz) of the new +policy, policy the new policy, cpu the number of the affected CPU or +CPUFREQ_ALL_CPUS for all CPUs; and max_cpu_freq the maximum supported +CPU frequency. This value is given for informational purposes only. + + +2.2 CPUFreq transition notifiers +-------------------------------- + +These are notified twice when the CPUfreq driver switches the CPU core +frequency and this change has any external implications. + +The second argument specifies the phase - CPUFREQ_PRECHANGE or +CPUFREQ_POSTCHANGE. + +The third argument is a struct cpufreq_freqs with the following +values: +cpu - number of the affected CPU or CPUFREQ_ALL_CPUS +old - old frequency +new - new frequency --- /dev/null +++ linux-2.4.27/Documentation/cpufreq/cpu-drivers.txt @@ -0,0 +1,210 @@ + CPU frequency and voltage scaling code in the Linux(TM) kernel + + + L i n u x C P U F r e q + + C P U D r i v e r s + + - information for developers - + + + Dominik Brodowski + + + + Clock scaling allows you to change the clock speed of the CPUs on the + fly. This is a nice method to save battery power, because the lower + the clock speed, the less power the CPU consumes. + + +Contents: +--------- +1. What To Do? +1.1 Initialization +1.2 Per-CPU Initialization +1.3 verify +1.4 target or setpolicy? +1.5 target +1.6 setpolicy +2. Frequency Table Helpers + + + +1. What To Do? +============== + +So, you just got a brand-new CPU / chipset with datasheets and want to +add cpufreq support for this CPU / chipset? Great. Here are some hints +on what is neccessary: + + +1.1 Initialization +------------------ + +First of all, in an __initcall level 7 or later (preferrably +module_init() so that your driver is modularized) function check +whether this kernel runs on the right CPU and the right chipset. If +so, register a struct cpufreq_driver with the CPUfreq core using +cpufreq_register_driver() + +What shall this struct cpufreq_driver contain? + +cpufreq_driver.name - The name of this driver. + +cpufreq_driver.init - A pointer to the per-CPU initialization + function. + +cpufreq_driver.verify - A pointer to a "verfication" funciton. + +cpufreq_driver.setpolicy _or_ +cpufreq_driver.target - See below on the differences. + +And optionally + +cpufreq_driver.exit - A pointer to a per-CPU cleanup function. + +cpufreq_driver.attr - A pointer to a NULL-terminated list of + "struct freq_attr" which allow to + export values to sysfs. + + +1.2 Per-CPU Initialization +-------------------------- + +Whenever a new CPU is registered with the device model, or after the +cpufreq driver registers itself, the per-CPU initialization fucntion +cpufreq_driver.init is called. It takes a struct cpufreq_policy +*policy as argument. What to do now? + +If necessary, activate the CPUfreq support on your CPU (unlock that +register etc.). + +Then, the driver must fill in the following values: + +policy->cpuinfo.min_freq _and_ +policy->cpuinfo.max_freq - the minimum and maximum frequency + (in kHz) which is supported by + this CPU +policy->cpuinfo.transition_latency the time it takes on this CPU to + switch between two frequencies (if + appropriate, else specify + CPUFREQ_ETERNAL) + +policy->cur The current operating frequency of + this CPU (if appropriate) +policy->min, +policy->max, +policy->policy and, if neccessary, +policy->governor must contain the "default policy" for + this CPU. A few moments later, + cpufreq_driver.verify and either + cpufreq_driver.setpolicy or + cpufreq_driver.target is called with + these values. + +For setting some of these values, the frequency table helpers might be +helpful. See the section 2 for more information on them. + + +1.3 verify +------------ + +When the user decides a new policy (consisting of +"policy,governor,min,max") shall be set, this policy must be validated +so that incompatible values can be corrected. For verifying these +values, a frequency table helper and/or the +cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned +int min_freq, unsigned int max_freq) function might be helpful. See +section 2 for details on frequency table helpers. + +You need to make sure that at least one valid frequency (or operating +range) is within policy->min and policy->max. If necessary, increase +policy->max fist, and only if this is no solution, decreas policy->min. + + +1.4 target or setpolicy? +---------------------------- + +Most cpufreq drivers or even most cpu frequency scaling algorithms +only allow the CPU to be set to one frequency. For these, you use the +->target call. + +Some cpufreq-capable processors switch the frequency between certain +limits on their own. These shall use the ->setpolicy call + + +1.4. target +------------- + +The target call has three arguments: struct cpufreq_policy *policy, +unsigned int target_frequency, unsigned int relation. + +The CPUfreq driver must set the new frequency when called here. The +actual frequency must be determined using the following rules: + +- keep close to "target_freq" +- policy->min <= new_freq <= policy->max (THIS MUST BE VALID!!!) +- if relation==CPUFREQ_REL_L, try to select a new_freq higher than or equal + target_freq. ("L for lowest, but no lower than") +- if relation==CPUFREQ_REL_H, try to select a new_freq lower than or equal + target_freq. ("H for highest, but no higher than") + +Here again the frequency table helper might assist you - see section 3 +for details. + + +1.5 setpolicy +--------------- + +The setpolicy call only takes a struct cpufreq_policy *policy as +argument. You need to set the lower limit of the in-processor or +in-chipset dynamic frequency switching to policy->min, the upper limit +to policy->max, and -if supported- select a performance-oriented +setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a +powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check +the reference implementation in arch/i386/kernel/cpu/cpufreq/longrun.c + + + +2. Frequency Table Helpers +========================== + +As most cpufreq processors only allow for being set to a few specific +frequencies, a "frequency table" with some functions might assist in +some work of the processor driver. Such a "frequency table" consists +of an array of struct cpufreq_freq_table entries, with any value in +"index" you want to use, and the corresponding frequency in +"frequency". At the end of the table, you need to add a +cpufreq_freq_table entry with frequency set to CPUFREQ_TABLE_END. And +if you want to skip one entry in the table, set the frequency to +CPUFREQ_ENTRY_INVALID. The entries don't need to be in ascending +order. + +By calling cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table); +the cpuinfo.min_freq and cpuinfo.max_freq values are detected, and +policy->min and policy->max are set to the same values. This is +helpful for the per-CPU initialization stage. + +int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table); +assures that at least one valid frequency is within policy->min and +policy->max, and all other criteria are met. This is helpful for the +->verify call. + +int cpufreq_frequency_table_target(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq, + unsigned int relation, + unsigned int *index); + +is the corresponding frequency table helper for the ->target +stage. Just pass the values to this function, and the unsigned int +index returns the number of the frequency table entry which contains +the frequency the CPU shall be set to. PLEASE NOTE: This is not the +"index" which is in this cpufreq_table_entry.index, but instead +cpufreq_table[index]. So, the new frequency is +cpufreq_table[index].frequency, and the value you stored into the +frequency table "index" field is +cpufreq_table[index].index. + --- /dev/null +++ linux-2.4.27/Documentation/cpufreq/governors.txt @@ -0,0 +1,155 @@ + CPU frequency and voltage scaling code in the Linux(TM) kernel + + + L i n u x C P U F r e q + + C P U F r e q G o v e r n o r s + + - information for users and developers - + + + Dominik Brodowski + + + + Clock scaling allows you to change the clock speed of the CPUs on the + fly. This is a nice method to save battery power, because the lower + the clock speed, the less power the CPU consumes. + + +Contents: +--------- +1. What is a CPUFreq Governor? + +2. Governors In the Linux Kernel +2.1 Performance +2.2 Powersave +2.3 Userspace + +3. The Governor Interface in the CPUfreq Core + + + +1. What Is A CPUFreq Governor? +============================== + +Most cpufreq drivers (in fact, all except one, longrun) or even most +cpu frequency scaling algorithms only offer the CPU to be set to one +frequency. In order to offer dynamic frequency scaling, the cpufreq +core must be able to tell these drivers of a "target frequency". So +these specific drivers will be transformed to offer a "->target" +call instead of the existing "->setpolicy" call. For "longrun", all +stays the same, though. + +How to decide what frequency within the CPUfreq policy should be used? +That's done using "cpufreq governors". Two are already in this patch +-- they're +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. + --- /dev/null +++ linux-2.4.27/Documentation/cpufreq/index.txt @@ -0,0 +1,56 @@ + CPU frequency and voltage scaling code in the Linux(TM) kernel + + + L i n u x C P U F r e q + + + + + Dominik Brodowski + + + + Clock scaling allows you to change the clock speed of the CPUs on the + fly. This is a nice method to save battery power, because the lower + the clock speed, the less power the CPU consumes. + + + +Documents in this directory: +---------------------------- +core.txt - General description of the CPUFreq core and + of CPUFreq notifiers + +cpu-drivers.txt - How to implement a new cpufreq processor driver + +governors.txt - What are cpufreq governors and how to + implement them? + +index.txt - File index, Mailing list and Links (this document) + +user-guide.txt - User Guide to CPUFreq + + +Mailing List +------------ +There is a CPU frequency changing CVS commit and general list where +you can report bugs, problems or submit patches. To post a message, +send an email to cpufreq@www.linux.org.uk, to subscribe go to +http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the +mailing list are available to subscribers at +http://www.linux.org.uk/mailman/private/cpufreq/. + + +Links +----- +the FTP archives: +* ftp://ftp.linux.org.uk/pub/linux/cpufreq/ + +how to access the CVS repository: +* http://cvs.arm.linux.org.uk/ + +the CPUFreq Mailing list: +* http://www.linux.org.uk/mailman/listinfo/cpufreq + +Clock and voltage scaling for the SA-1100: +* http://www.lart.tudelft.nl/projects/scaling --- /dev/null +++ linux-2.4.27/Documentation/cpufreq/user-guide.txt @@ -0,0 +1,166 @@ + CPU frequency and voltage scaling code in the Linux(TM) kernel + + + L i n u x C P U F r e q + + U S E R G U I D E + + + Dominik Brodowski + + + + Clock scaling allows you to change the clock speed of the CPUs on the + fly. This is a nice method to save battery power, because the lower + the clock speed, the less power the CPU consumes. + + +Contents: +--------- +1. Supported Architectures and Processors +1.1 ARM +1.2 x86 +1.3 sparc64 + +2. "Policy" / "Governor"? +2.1 Policy +2.2 Governor + +3. How to change the CPU cpufreq policy and/or speed +3.1 Preferred interface: sysfs +3.2 Deprecated interfaces + + + +1. Supported Architectures and Processors +========================================= + +1.1 ARM +------- + +The following ARM processors are supported by cpufreq: + +ARM Integrator +ARM-SA1100 +ARM-SA1110 + + +1.2 x86 +------- + +The following processors for the x86 architecture are supported by cpufreq: + +AMD Elan - SC400, SC410 +AMD mobile K6-2+ +AMD mobile K6-3+ +Cyrix Media GXm +Intel mobile PIII [*] and Intel mobile PIII-M on certain chipsets +Intel Pentium 4, Intel Xeon +National Semiconductors Geode GX +Transmeta Crusoe +varios processors on some ACPI 2.0-compatible systems [**] + +[*] only certain Intel mobile PIII processors are supported. If you +know that you own a speedstep-capable processor, pass the option +"speedstep_coppermine=1" to the module speedstep.o + +[**] Only if "ACPI Processor Performance States" are available +to the ACPI<->BIOS interface. + + +1.3 sparc64 +----------- + +The following processors for the sparc64 architecture are supported by +cpufreq: + +UltraSPARC-III + + + +2. "Policy" / "Governor" ? +========================== + +Some CPU frequency scaling-capable processor switch between varios +frequencies and operating voltages "on the fly" without any kernel or +user involvement. This guarantuees very fast switching to a frequency +which is high enough to serve the user's needs, but low enough to save +power. + + +2.1 Policy +---------- + +On these systems, all you can do is select the lower and upper +frequency limit as well as whether you want more aggressive +power-saving or more instantly avaialble processing power. + + +2.2 Governor +------------ + +On all other cpufreq implementations, these boundaries still need to +be set. Then, a "governor" must be selected. Such a "governor" decides +what speed the processor shall run within the boundaries. One such +"governor" is the "userspace" governor. This one allows the user - or +a yet-to-implement userspace program - to decide what specific speed +the processor shall run at. + + +3. How to change the CPU cpufreq policy and/or speed +==================================================== + +3.1 Preferred Interface: sysfs +------------------------------ + +The preferred interface is located in the sysfs filesystem. If you +mounted it at /sys, the cpufreq interface is located in a subdirectory +"cpufreq" within the cpu-device directory +(e.g. /sys/devices/sys/cpu0/cpufreq/ for the first CPU). + +cpuinfo_min_freq : this file shows the minimum operating + frequency the processor can run at(in kHz) +cpuinfo_max_freq : this file shows the maximum operating + frequency the processor can run at(in kHz) +scaling_driver : this file shows what cpufreq driver is + used to set the frequency on this CPU + +scaling_available_governors : this file shows the CPUfreq governors + available in this kernel. You can see the + currently activated governor in + +scaling_governor, and by "echoing" the name of another + governor you can change it. Please note + that some governors won't load - they only + work on some specific architectures or + processors. +scaling_min_freq and +scaling_max_freq show the current "policy limits" (in + kHz). By echoing new values into these + files, you can change these limits. + + +If you have selected the "userspace" governor which allows you to +set the CPU operating frequency to a specific value, you can read out +the current frequency in + +scaling_setspeed. By "echoing" a new frequency into this + you can change the speed of the CPU, + but only within the limits of + scaling_min_freq and scaling_max_freq. + + +3.2 Deprecated Interfaces +------------------------- + +Depending on your kernel configuration, you might find the following +cpufreq-related files: +/proc/cpufreq +/proc/sys/cpu/*/speed +/proc/sys/cpu/*/speed-min +/proc/sys/cpu/*/speed-max + +These are files for deprecated interfaces to cpufreq, which offer far +less functionality. Because of this, these interfaces aren't described +here. + --- /dev/null +++ linux-2.4.27/Documentation/cpufreq-old @@ -0,0 +1,332 @@ + CPU frequency and voltage scaling code in the Linux(TM) kernel + + + L i n u x C P U F r e q + + + + + Dominik Brodowski + + + + Clock scaling allows you to change the clock speed of the CPUs on the + fly. This is a nice method to save battery power, because the lower + the clock speed, the less power the CPU consumes. + + + +Contents: +--------- +1. Supported architectures +2. User interface +2.1 Sample script for command line interface +3. CPUFreq core and interfaces +3.1 General information +3.2 CPUFreq notifiers +3.3 CPUFreq architecture drivers +4. Mailing list and Links + + + +1. Supported architectures +========================== + +Some architectures detect the lowest and highest possible speed +settings, while others rely on user information on this. For the +latter, a boot parameter is required, for the former, you can specify +one to set the limits between speed settings may occur. +The boot parameter has the following syntax: + + cpufreq=minspeed-maxspeed + +with both minspeed and maxspeed being given in kHz. To set the lower +limit to 59 MHz and the upper limit to 221 MHz, specify: + + cpufreq=59000-221000 + +Check the "Speed Limits Detection" information below on whether +the driver detects the lowest and highest allowed speed setting +automatically. + + +ARM Integrator: + SA 1100, SA1110 +-------------------------------- + Speed Limits Detection: On Integrators, the minimum speed is set + and the maximum speed has to be specified using the boot + parameter. On SA11x0s, the frequencies are fixed (59 - 287 MHz) + + +AMD Elan: + SC400, SC410 +-------------------------------- + Speed Limits Detection: Not implemented. You need to specify the + minimum and maximum frequency in the boot parameter (see above). + + +VIA Cyrix Longhaul: + VIA Samuel/CyrixIII, VIA Cyrix Samuel/C3, + VIA Cyrix Ezra, VIA Cyrix Ezra-T +-------------------------------- + Speed Limits Detection: working. No need for boot parameters. + NOTE: Support for certain processors is currently disabled, + waiting on updated docs from VIA. + + +Intel SpeedStep: + certain mobile Intel Pentium III (Coppermine), and all mobile + Intel Pentium III-M (Tulatin) and mobile Intel Pentium 4 P4-Ms. +-------------------------------- + Speed Limits Detection: working. No need for boot parameters. + NOTE: + 1.) mobile Intel Pentium III (Coppermine): + The SpeedStep interface may only be used on SpeedStep + capable processors. Unforunately, due to lack of documentation, + such detection is not yet possible on mobile Intel PIII + (Coppermine) processors. In order to activate SpeedStep on such a + processor, you have to remove one line manually in + linux/drivers/arch/i386/speedstep.c + + +P4 CPU Clock Modulation: + Intel Pentium 4 Xeon processors +-------------------------------- + Speed Limits Detection: Not implemented. You need to specify the + minimum and maximum frequency in the boot parameter (see above). + + + +2. User Interface +================= + +CPUFreq uses a "sysctl" interface which is located in + /proc/sys/cpu/0/ on UP (uniprocessor) kernels, or + /proc/sys/cpu/any/ on SMP (symmetric multiprocessoring) kernels. + + +In this directory, you will find three files of importance for +CPUFreq: speed-max, speed-min, and speed: + +speed shows the current CPU frequency in kHz, +speed-min the minimal supported CPU frequency, and +speed-max the maximal supported CPU frequency. + +Please note that you might have to specify these limits as a boot +parameter depending on the architecture (see above). + + +To change the CPU frequency, "echo" the desired CPU frequency (in kHz) +to speed. For example, to set the CPU speed to the lowest/highest +allowed frequency do: + +root@notebook:# cat /proc/sys/cpu/0/speed-min > /proc/sys/cpu/0/speed +root@notebook:# cat /proc/sys/cpu/0/speed-max > /proc/sys/cpu/0/speed + + +2.1 Sample script for command line interface +********************************************** + + +Michael Ossmann has written a small command line +interface for the infinitely lazy. + +#!/bin/bash +# +# /usr/local/bin/freq +# simple command line interface to cpufreq + +[ -n "$1" ] && case "$1" in + "min" ) + # set frequency to minimum + cat /proc/sys/cpu/0/speed-min >/proc/sys/cpu/0/speed + ;; + "max" ) + # set frequency to maximum + cat /proc/sys/cpu/0/speed-max >/proc/sys/cpu/0/speed + ;; + * ) + echo "Usage: $0 [min|max]" + echo " min: set frequency to minimum and display new frequency" + echo " max: set frequency to maximum and display new frequency" + echo " no options: display current frequency" + exit 1 + ;; +esac + +# display current frequency +cat /proc/sys/cpu/0/speed +exit 0 + + + +3. CPUFreq core and interfaces +=============================== + +3.1 General information +************************* + +The CPUFreq core code is located in linux/kernel/cpufreq.c. This +cpufreq code offers a standardized interface for the CPUFreq +architecture drivers (those pieces of code that do the actual +frequency transition), as well as to "notifiers". These are device +drivers or other part of the kernel that need to be informed of +frequency changes (like timing code) or even need to force certain +speed limits (like LCD drivers on ARM architecture). Aditionally, the +kernel "constant" loops_per_jiffy is updated on frequency changes +here. + + +3.2 CPUFreq notifiers +*********************** + +CPUFreq notifiers are kernel code that need to be called to either +a) define certain minimum or maximum speed settings, +b) be informed of frequency changes in advance of the transition, or +c) be informed of frequency changes directly after the transition. + +A standard kernel notifier interface is offered for this. See +linux/include/linux/notifier.h for details on notifiers. + + +Data and value passed to CPUFreq notifiers +------------------------------------------ +The second argument passed to any notifier is an unsigned int stating +the phase of the transition: +CPUFREQ_MINMAX during the process of determing a valid new CPU + frequency, +CPUFREQ_PRECHANGE right before the transition, and +CPUFREQ_POSTCHANGE right after the transition. + +The third argument, a void *pointer, points to a struct +cpufreq_freqs. This consists of four values: min, max, cur and new. + +min and max are the current speed limits. Please note: Never update +these values directly, use cpufreq_updateminmax(struct cpufreq_freqs +*freqs, unsigned int min, unsigned int max) instead. cur is the +current/old speed, and new is the new speed, but might only be valid +on CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE. + +Each notifier gets called all three times on any transition: + +CPUFREQ_MINMAX +Here the notifier is supposed to update the min and max values to the +limits the protected device / kernel code needs. As stated above, +always use cpufreq_updateminmax for this. + +CPUFREQ_PRECHANGE +CPUFREQ_POSTCHANGE +Here the notifier is supposed to update all internal (e.g. device +driver) code which is dependend on the CPU frequency. + + +3.3 CPUFreq architecture drivers +********************************** + +CPUFreq architecture drivers are the pieces of kernel code that +actually perform CPU frequency transitions. These need to be +initialised seperately (seperate initcalls), and may be +modularized. They interact with the CPUFreq core in the following way: + + +cpufreq_register() +------------------ +cpufreq_register registers an arch driver to the CPUFreq core. Please +note that only one arch driver may be registered at any time, -EBUSY +is returned when an arch driver is already registered. The argument to +cpufreq_register, cpufreq_driver_t driver, is described later. + + +cpufreq_unregister() +-------------------- +cpufreq_unregister unregisters an arch driver, e.g. on module +unloading. Please note that there is no check done that this is called +from the driver which actually registered itself to the core, so +please only call this function when you are sure the arch driver got +registered correctly before. + + +struct cpufreq_driver +---------------- +On initialisation, the arch driver is supposed to pass the following +entries in struct cpufreq_driver cpufreq_driver: + +cpufreq_verify_t validate: This is a pointer to a function with the +following definition: + unsigned int validating_function (unsigned int kHz). +It is called right before a transition occurs. The proposed new +speed setting is passed as an argument in kHz; the validating code +should verify this is a valid speed setting which is currently +supported by the CPU. It shall return the closest valid CPU frequency +in kHz. + +cpufreq_setspeed_t setspeed: This is a pointer to a function with the +following definition: + void setspeed_function (unsigned int kHz). +This function shall perform the transition to the new CPU frequency +given as argument in kHz. Note that this argument is exactly the same +as the one returned by cpufreq_verify_t validate. + + +unsigned int freq.cur: The current CPU core frequency. Note that this +is a requirement while the next two entries are optional. + + +unsigned int freq.min (optional): The minimal CPU core frequency this +CPU supports. This value may be limited further by the +cpufreq_verify_t validate function, and so this value should be the +minimal core frequency allowed "theoretically" on this system in this +configuration. + + +unsigned int freq.max (optional): The maximum CPU core frequency this +CPU supports. This value may be limited further by the +cpufreq_verify_t validate function, and so this value should be the +maximum core frequency allowed "theoretically" on this system in this +configuration. + + +Some Requirements to CPUFreq architecture drivers +------------------------------------------------- +* Only call cpufreq_register() when the ability to switch CPU + frequencies is _verified_ or can't be missing +* cpufreq_unregister() may only be called if cpufreq_register() has + been successfully(!) called before +* All CPUs have to be set to the same speed whenever setspeed() is + called +* Be aware that there is currently no error management in the + setspeed() code in the CPUFreq core. So only call yourself a + cpufreq_driver if you are really a working cpufreq_driver! + + + +4. Mailing list and Links +************************** + + +Mailing List +------------ +There is a CPU frequency changing CVS commit and general list where +you can report bugs, problems or submit patches. To post a message, +send an email to cpufreq@www.linux.org.uk, to subscribe go to +http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the +mailing list are available to subscribers at +http://www.linux.org.uk/mailman/private/cpufreq/. + + +Links +----- +the FTP archives: +* ftp://ftp.linux.org.uk/pub/linux/cpufreq/ + +how to access the CVS repository: +* http://www.arm.linux.org.uk/cvs/ + +the CPUFreq Mailing list: +* http://www.linux.org.uk/mailman/listinfo/cpufreq + +Clock and voltage scaling for the SA-1100: +* http://www.lart.tudelft.nl/projects/scaling + +CPUFreq project homepage +* http://www.brodo.de/cpufreq/ --- /dev/null +++ linux-2.4.27/Documentation/l3/structure @@ -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. --- /dev/null +++ linux-2.4.27/Documentation/serial/driver @@ -0,0 +1,208 @@ + + Low Level Serial API + -------------------- + + + $Id: driver,v 1.3 2001/11/24 23:24:47 rmk Exp $ + + +This document is meant as a brief overview of some aspects of the new serial +driver. It is not complete, any questions you have should be directed to + + +The reference implementation is contained within serial_amba.c. + + + +Low Level Serial Hardware Driver +-------------------------------- + +The low level serial hardware driver is responsible for supplying port +information (defined by uart_port) and a set of control methods (defined +by uart_ops) to the core serial driver. The low level driver is also +responsible for handling interrupts for the port, and providing any +console support. + + +Console Support +--------------- + +The serial core provides a few helper functions. This includes identifing +the correct port structure (via uart_get_console) and decoding command line +arguments (uart_parse_options). + + +Locking +------- + +Generally, all locking is done by the core driver, except for the interrupt +functions. It is the responsibility of the low level hardware driver to +perform the necessary locking there using info->lock. (since it is running +in an interrupt, you only need to use spin_lock() and spin_unlock() from +the interrupt handler). + + +uart_ops +-------- + +The uart_ops structure is the main interface between serial_core and the +hardware specific driver. It contains all the methods to control the +hardware. + + tx_empty(port) + This function tests whether the transmitter fifo and shifter + for the port described by 'port' is empty. If it is empty, + this function should return TIOCSER_TEMT, otherwise return 0. + If the port does not support this operation, then it should + return TIOCSER_TEMT. + + set_mctrl(port, mctrl) + This function sets the modem control lines for port described + by 'port' to the state described by mctrl. The relevant bits + of mctrl are: + - TIOCM_RTS RTS signal. + - TIOCM_DTR DTR signal. + - TIOCM_OUT1 OUT1 signal. + - TIOCM_OUT2 OUT2 signal. + If the appropriate bit is set, the signal should be driven + active. If the bit is clear, the signal should be driven + inactive. + + get_mctrl(port) + Returns the current state of modem control inputs. The state + of the outputs should not be returned, since the core keeps + track of their state. The state information should include: + - TIOCM_DCD state of DCD signal + - TIOCM_CTS state of CTS signal + - TIOCM_DSR state of DSR signal + - TIOCM_RI state of RI signal + The bit is set if the signal is currently driven active. If + the port does not support CTS, DCD or DSR, the driver should + indicate that the signal is permanently active. If RI is + not available, the signal should not be indicated as active. + + stop_tx(port,from_tty) + Stop transmitting characters. This might be due to the CTS + line becoming inactive or the tty layer indicating we want + to stop transmission. + + start_tx(port,nonempty,from_tty) + start transmitting characters. (incidentally, nonempty will + always be nonzero, and shouldn't be used - it will be dropped). + + stop_rx(port) + Stop receiving characters; the port is in the process of + being closed. + + enable_ms(port) + Enable the modem status interrupts. + + break_ctl(port,ctl) + Control the transmission of a break signal. If ctl is + nonzero, the break signal should be transmitted. The signal + should be terminated when another call is made with a zero + ctl. + + startup(port,info) + Grab any interrupt resources and initialise any low level driver + state. Enable the port for reception. It should not activate + RTS nor DTR; this will be done via a separate call to set_mctrl. + + shutdown(port,info) + Disable the port, disable any break condition that may be in + effect, and free any interrupt resources. It should not disable + RTS nor DTR; this will have already been done via a separate + call to set_mctrl. + + change_speed(port,cflag,iflag,quot) + Change the port parameters, including word length, parity, stop + bits. Update read_status_mask and ignore_status_mask to indicate + the types of events we are interested in receiving. Relevant + cflag bits are: + CSIZE - word size + CSTOPB - 2 stop bits + PARENB - parity enable + PARODD - odd parity (when PARENB is in force) + CREAD - enable reception of characters (if not set, + still receive characters from the port, but + throw them away. + CRTSCTS - if set, enable CTS status change reporting + CLOCAL - if not set, enable modem status change + reporting. + Relevant iflag bits are: + INPCK - enable frame and parity error events to be + passed to the TTY layer. + BRKINT + PARMRK - both of these enable break events to be + passed to the TTY layer. + + IGNPAR - ignore parity and framing errors + IGNBRK - ignore break errors, If IGNPAR is also + set, ignore overrun errors as well. + The interaction of the iflag bits is as follows (parity error + given as an example): + Parity error INPCK IGNPAR + None n/a n/a character received + Yes n/a 0 character discarded + Yes 0 1 character received, marked as + TTY_NORMAL + Yes 1 1 character received, marked as + TTY_PARITY + + pm(port,state,oldstate) + perform any power management related activities on the specified + port. state indicates the new state (defined by ACPI D0-D3), + oldstate indicates the previous state. Essentially, D0 means + fully on, D3 means powered down. + + This function should not be used to grab any resources. + + type(port) + Return a pointer to a string constant describing the specified + port, or return NULL, in which case the string 'unknown' is + substituted. + + release_port(port) + Release any memory and IO region resources currently in use by + the port. + + request_port(port) + Request any memory and IO region resources required by the port. + If any fail, no resources should be registered when this function + returns, and it should return -EBUSY on failure. + + config_port(port,type) + Perform any autoconfiguration steps required for the port. `type` + contains a bit mask of the required configuration. UART_CONFIG_TYPE + indicates that the port requires detection and identification. + port->type should be set to the type found, or PORT_UNKNOWN if + no port was detected. + + UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, + which should be probed using standard kernel autoprobing techniques. + This is not necessary on platforms where ports have interrupts + internally hard wired (eg, system on a chip implementations). + + verify_port(port,serinfo) + Verify the new serial port information contained within serinfo is + suitable for this port type. + + ioctl(port,cmd,arg) + Perform any port specific IOCTLs. IOCTL commands must be defined + using the standard numbering system found in + + +Other notes +----------- + +It is intended some day to drop the 'unused' entries from uart_port, and +allow low level drivers to register their own individual uart_port's with +the core. This will allow drivers to use uart_port as a pointer to a +structure containing both the uart_port entry with their own extensions, +thus: + + struct my_port { + struct uart_port port; + int my_stuff; + }; + --- linux-2.4.27/MAINTAINERS~2.4.27-vrs1 +++ linux-2.4.27/MAINTAINERS @@ -262,6 +262,8 @@ ARM/STRONGARM110 PORT P: Russell King M: rmk@arm.linux.org.uk +P: Vincent Sanders +M: vince@arm.linux.org.uk L: linux-arm-kernel@lists.arm.linux.org.uk W: http://www.arm.linux.org.uk/ S: Maintained --- linux-2.4.27/Makefile~2.4.27-vrs1 +++ linux-2.4.27/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 27 -EXTRAVERSION = +EXTRAVERSION = -vrs1 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -137,7 +137,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 @@ -161,6 +163,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 @@ -194,6 +197,8 @@ DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o DRIVERS-$(CONFIG_CRYPTO) += crypto/crypto.o +DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o +DRIVERS-$(CONFIG_ARCH_AT91RM9200) += drivers/at91/at91drv.o DRIVERS := $(DRIVERS-y) @@ -274,11 +279,6 @@ export NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS -.S.s: - $(CPP) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -o $*.s $< -.S.o: - $(CC) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -c -o $*.o $< - Version: dummy @rm -f include/linux/compile.h --- linux-2.4.27/Rules.make~2.4.27-vrs1 +++ linux-2.4.27/Rules.make @@ -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)))' ; \ --- linux-2.4.27/arch/alpha/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/alpha/config.in @@ -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" --- linux-2.4.27/arch/arm/Makefile~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/Makefile @@ -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 --- linux-2.4.27/arch/arm/boot/compressed/head.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/boot/compressed/head.S @@ -40,6 +40,14 @@ .macro writeb, rb strb \rb, [r3, #0x3f8 << 2] .endm +#elif defined(CONFIG_ARCH_RISCSTATION) + .macro loadsp, rb + mov \rb, #0x03000000 + orr \rb, \rb, #0x00010000 + .endm + .macro writeb, rb + strb \rb, [r3, #0x3f8 << 2] + .endm #elif defined(CONFIG_ARCH_INTEGRATOR) .macro loadsp, rb mov \rb, #0x16000000 @@ -396,6 +404,20 @@ mcr p15, 0, r0, c1, c0, 0 @ load control register mov pc, r12 +__arm7_cache_on: + mov r12, lr + bl __setup_mmu + mov r0, #0 + mcr p15, 0, r0, c7, c0, 0 @ invalidate whole cache v3 + mcr p15, 0, r0, c5, c0, 0 @ invalidate whole TLB v3 + mcr p15, 0, r3, c2, c0, 0 @ load page table pointer + mov r0, #-1 + mcr p15, 0, r0, c3, c0, 0 @ load domain access control + mov r0, #0x7d + mcr p15, 0, r0, c1, c0, 0 @ load control register + mov pc, r12 + + /* * All code following this line is relocatable. It is relocated by * the above code to the end of the decompressed kernel image and @@ -480,9 +502,9 @@ .word 0x41007000 @ ARM7/710 .word 0xfff8fe00 + b __arm7_cache_on b __arm7_cache_off - b __arm7_cache_off - mov pc, lr + b __armv3_cache_flush .word 0x41807200 @ ARM720T (writethrough) .word 0xffffff00 @@ -490,14 +512,14 @@ b __armv4_cache_off mov pc, lr - .word 0x41129200 @ ARM920T - .word 0xff00fff0 + .word 0x41009200 @ ARM920T, ARM922T, ARM926TEJ-S + .word 0xff00ff90 b __armv4_cache_on b __armv4_cache_off b __armv4_cache_flush - .word 0x41029220 @ ARM922T - .word 0xff00fff0 + .word 0x4100a200 @ ARM1020T/E, ARM1022E, ARM1026TEJ-S + .word 0xff00ff90 b __armv4_cache_on b __armv4_cache_off b __armv4_cache_flush --- linux-2.4.27/arch/arm/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/config.in @@ -144,6 +144,7 @@ mainmenu_option next_comment comment 'AT91RM9200 Implementations' dep_bool ' Atmel AT91RM9200 Development Board' CONFIG_ARCH_AT91RM9200DK $CONFIG_ARCH_AT91RM9200 +dep_bool ' Cogent CSB337' CONFIG_MACH_CSB337 $CONFIG_ARCH_AT91RM9200 endmenu mainmenu_option next_comment @@ -189,6 +190,12 @@ define_bool CONFIG_ARCH_ACORN n fi +if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then + define_bool CONFIG_PLD y +else + define_bool CONFIG_PLD n +fi + ##################################################################### # Footbridge support if [ "$CONFIG_ARCH_CO285" = "y" -o \ @@ -315,26 +322,42 @@ # ARM922T if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then define_bool CONFIG_CPU_ARM922T y - define_bool CONFIG_PLD y else - define_bool CONFIG_CPU_ARM922T n - define_bool CONFIG_PLD n + if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then + bool 'Support ARM922T(Excalibur) processor' CONFIG_ARM922T + else + define_bool CONFIG_CPU_ARM922T n + fi fi # ARM926T if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then - bool 'Support ARM926T processor' CONFIG_CPU_ARM926T + bool 'Support ARM926TEJ-S processor' CONFIG_CPU_ARM926T else define_bool CONFIG_CPU_ARM926T n fi # ARM1020 if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then - bool 'Support ARM1020 processor' CONFIG_CPU_ARM1020 + bool 'Support ARM1020T (Rev0) processor' CONFIG_CPU_ARM1020 else define_bool CONFIG_CPU_ARM1020 n fi +# ARM1020E +if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then + bool 'Support ARM1020E (Rev1) processor' CONFIG_CPU_ARM1020E +else + define_bool CONFIG_CPU_ARM1020E n +fi + +# ARM1022 +if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then + bool 'Support ARM1022 processor' CONFIG_CPU_ARM1020E +else + define_bool CONFIG_CPU_ARM1022 n +fi + # ARM1026EJ-S if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then bool 'Support ARM1026EJ-S processor' CONFIG_CPU_ARM1026 @@ -388,25 +411,29 @@ if [ "$CONFIG_CPU_ARM720T" = "y" -o "$CONFIG_CPU_ARM920T" = "y" -o \ "$CONFIG_CPU_ARM922T" = "y" -o "$CONFIG_CPU_ARM926T" = "y" -o \ - "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then + "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1020E" = "y" -o \ + "$CONFIG_CPU_ARM1022" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then dep_bool 'Support Thumb instructions (EXPERIMENTAL)' CONFIG_ARM_THUMB $CONFIG_EXPERIMENTAL fi if [ "$CONFIG_CPU_ARM920T" = "y" -o "$CONFIG_CPU_ARM922T" = "y" -o \ "$CONFIG_CPU_ARM926T" = "y" -o "$CONFIG_CPU_ARM1020" = "y" -o \ + "$CONFIG_CPU_ARM1020E" = "y" -o "$CONFIG_CPU_ARM1022" = "y" -o \ "$CONFIG_CPU_ARM1026" = "y" ]; then bool 'Disable I-Cache' CONFIG_CPU_ICACHE_DISABLE bool 'Disable D-Cache' CONFIG_CPU_DCACHE_DISABLE - if [ "$CONFIG_CPU_DISABLE_DCACHE" = "n" ]; then + if [ "$CONFIG_CPU_DCACHE_DISABLE" = "n" ]; then bool 'Force write through D-cache' CONFIG_CPU_DCACHE_WRITETHROUGH fi fi if [ "$CONFIG_CPU_ARM926T" = "y" -o "$CONFIG_CPU_ARM1020" = "y" -o \ + "$CONFIG_CPU_ARM1020E" = "y" -o "$CONFIG_CPU_ARM1022" = "y" -o \ "$CONFIG_CPU_ARM1026" = "y" ]; then if [ "$CONFIG_CPU_ICACHE_DISABLE" = "n" -o "$CONFIG_CPU_DCACHE_DISABLE" = "n" ]; then bool 'Round robin I and D cache replacement algorithm' CONFIG_CPU_CACHE_ROUND_ROBIN fi fi -if [ "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then +if [ "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1020E" = "y" -o \ + "$CONFIG_CPU_ARM1026" = "y" -o "$CONFIG_CPU_ARM1022" = "y" ]; then bool 'Disable branch prediction' CONFIG_CPU_BPREDICT_DISABLE fi @@ -491,7 +518,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 +541,8 @@ "$CONFIG_ARCH_INTEGRATOR" = "y" -o \ "$CONFIG_ARCH_CDB89712" = "y" -o \ "$CONFIG_ARCH_P720T" = "y" -o \ - "$CONFIG_ARCH_OMAHA" = "y" ]; then + "$CONFIG_ARCH_OMAHA" = "y" -o \ + "$CONFIG_ARCH_AT91RM9200" = "y" ]; then bool 'Timer and CPU usage LEDs' CONFIG_LEDS if [ "$CONFIG_LEDS" = "y" ]; then if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \ @@ -524,7 +552,8 @@ "$CONFIG_ARCH_SA1100" = "y" -o \ "$CONFIG_ARCH_INTEGRATOR" = "y" -o \ "$CONFIG_ARCH_P720T" = "y" -o \ - "$CONFIG_ARCH_OMAHA" = "y" ]; then + "$CONFIG_ARCH_OMAHA" = "y" -o \ + "$CONFIG_ARCH_AT91RM9200" = "y" ]; then bool ' Timer LED' CONFIG_LEDS_TIMER bool ' CPU usage LED' CONFIG_LEDS_CPU fi @@ -729,10 +758,7 @@ dep_bool ' Kernel low-level debugging functions' CONFIG_DEBUG_LL $CONFIG_DEBUG_KERNEL dep_bool ' Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT $CONFIG_DEBUG_LL $CONFIG_FOOTBRIDGE dep_bool ' Kernel low-level debugging messages via UART2' CONFIG_DEBUG_CLPS711X_UART2 $CONFIG_DEBUG_LL $CONFIG_ARCH_CLPS711X - -int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0 - endmenu -source crypto/Config.in source lib/Config.in + --- /dev/null +++ linux-2.4.27/arch/arm/config.in.rej @@ -0,0 +1,16 @@ +*************** +*** 144,149 **** + mainmenu_option next_comment + comment 'AT91RM9200 Implementations' + dep_bool ' Atmel AT91RM9200 Development Board' CONFIG_ARCH_AT91RM9200DK $CONFIG_ARCH_AT91RM9200 + endmenu + + mainmenu_option next_comment +--- 144,150 ---- + 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 --- linux-2.4.27/arch/arm/def-configs/at91rm9200dk~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/def-configs/at91rm9200dk @@ -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 --- /dev/null +++ linux-2.4.27/arch/arm/def-configs/csb337 @@ -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 --- /dev/null +++ linux-2.4.27/arch/arm/fastfpe/CPDO.S @@ -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 + +/*---------------------------------------------------------------------------*/ --- /dev/null +++ linux-2.4.27/arch/arm/fastfpe/CPDT.S @@ -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 + +/*---------------------------------------------------------------------------*/ --- /dev/null +++ linux-2.4.27/arch/arm/fastfpe/CPRT.S @@ -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 + +/*---------------------------------------------------------------------------*/ --- /dev/null +++ linux-2.4.27/arch/arm/fastfpe/Makefile @@ -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) --- /dev/null +++ linux-2.4.27/arch/arm/fastfpe/entry.S @@ -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 + +/*---------------------------------------------------------------------------*/ + + .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 + +/*---------------------------------------------------------------------------*/ --- /dev/null +++ linux-2.4.27/arch/arm/fastfpe/module.c @@ -0,0 +1,64 @@ +/* + Fast Floating Point Emulator + (c) Peter Teichmann + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include + +#ifndef MODULE +#define kern_fp_enter fp_enter + +extern char fpe_type[]; +#endif + +static void (*orig_fp_enter)(void); /* old kern_fp_enter value */ +extern void (*kern_fp_enter)(void); /* current FP handler */ +extern void fastfpe_enter(void); /* forward declarations */ +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 "); +MODULE_DESCRIPTION("Fast floating point emulator with full precision"); --- /dev/null +++ linux-2.4.27/arch/arm/fastfpe/round.S @@ -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 + +/*------------------------------------------------------------------------*/ --- linux-2.4.27/arch/arm/kernel/calls.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/calls.S @@ -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) --- linux-2.4.27/arch/arm/kernel/dma-rpc.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/dma-rpc.c @@ -26,19 +26,6 @@ #include #include -#if 0 -typedef enum { - dma_size_8 = 1, - dma_size_16 = 2, - dma_size_32 = 4, - dma_size_128 = 16 -} dma_size_t; - -typedef struct { - dma_size_t transfersize; -} dma_t; -#endif - #define TRANSFER_SIZE 2 #define CURA (0) @@ -48,10 +35,6 @@ #define CR (IOMD_IO0CR - IOMD_IO0CURA) #define ST (IOMD_IO0ST - IOMD_IO0CURA) -#define state_prog_a 0 -#define state_wait_a 1 -#define state_wait_b 2 - static void iomd_get_next_sg(struct scatterlist *sg, dma_t *dma) { unsigned long end, offset, flags = 0; @@ -65,7 +48,7 @@ if (end > PAGE_SIZE) end = PAGE_SIZE; - if (offset + (int) TRANSFER_SIZE > end) + if (offset + TRANSFER_SIZE >= end) flags |= DMA_END_L; sg->length = end - TRANSFER_SIZE; @@ -103,27 +86,31 @@ if (!(status & DMA_ST_INT)) return; - if (status & DMA_ST_OFL && !dma->sg) - break; - - iomd_get_next_sg(&dma->cur_sg, dma); + if ((dma->state ^ status) & DMA_ST_AB) + iomd_get_next_sg(&dma->cur_sg, dma); switch (status & (DMA_ST_OFL | DMA_ST_AB)) { case DMA_ST_OFL: /* OIA */ case DMA_ST_AB: /* .IB */ iomd_writel(dma->cur_sg.dma_address, base + CURA); iomd_writel(dma->cur_sg.length, base + ENDA); + dma->state = DMA_ST_AB; break; case DMA_ST_OFL | DMA_ST_AB: /* OIB */ case 0: /* .IA */ iomd_writel(dma->cur_sg.dma_address, base + CURB); iomd_writel(dma->cur_sg.length, base + ENDB); + dma->state = 0; break; } + + if (status & DMA_ST_OFL && + dma->cur_sg.length == (DMA_END_S|DMA_END_L)) + break; } while (1); - iomd_writeb(0, base + CR); + dma->state = ~DMA_ST_AB; disable_irq(irq); } @@ -158,6 +145,7 @@ } iomd_writeb(DMA_CR_C, dma_base + CR); + dma->state = DMA_ST_AB; } if (dma->dma_mode == DMA_MODE_READ) @@ -171,13 +159,11 @@ { unsigned long dma_base = dma->dma_base; unsigned long flags; - unsigned int ctrl; local_irq_save(flags); - ctrl = iomd_readb(dma_base + CR); - if (ctrl & DMA_CR_E) + if (dma->state != ~DMA_ST_AB) disable_irq(dma->dma_irq); - iomd_writeb(ctrl & ~DMA_CR_E, dma_base + CR); + iomd_writeb(0, dma_base + CR); local_irq_restore(flags); } --- linux-2.4.27/arch/arm/kernel/entry-armv.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/entry-armv.S @@ -677,12 +677,11 @@ mrs r9, cpsr @ Enable interrupts if they were tst r3, #I_BIT biceq r9, r9, #I_BIT @ previously - mov r0, r2 @ *** remove once everyones in sync /* * This routine must not corrupt r9 */ #ifdef MULTI_CPU - ldr r4, .LCprocfns @ pass r0, r3 to + ldr r4, .LCprocfns @ pass r2, r3 to mov lr, pc @ processor code ldr pc, [r4] @ call processor specific code #else @@ -788,9 +787,8 @@ stmdb r5, {sp, lr}^ alignment_trap r7, r7, __temp_abt zero_fp - mov r0, r2 @ remove once everyones in sync #ifdef MULTI_CPU - ldr r4, .LCprocfns @ pass r0, r3 to + ldr r4, .LCprocfns @ pass r2, r3 to mov lr, pc @ processor code ldr pc, [r4] @ call processor specific code #else @@ -840,7 +838,8 @@ adrsvc al, r9, ret_from_exception @ r9 = normal FP return adrsvc al, lr, fpundefinstr @ lr = undefined instr return -call_fpe: get_current_task r10 +call_fpe: enable_irq r10 + get_current_task r10 mov r8, #1 strb r8, [r10, #TSK_USED_MATH] @ set current->used_math ldr r4, .LCfp --- linux-2.4.27/arch/arm/kernel/fiq.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/fiq.c @@ -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) --- linux-2.4.27/arch/arm/kernel/head-armv.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/head-armv.S @@ -1,7 +1,7 @@ /* * linux/arch/arm/kernel/head-armv.S * - * Copyright (C) 1994-1999 Russell King + * Copyright (C) 1994-2003 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -163,10 +163,10 @@ */ .type __ret, %function __ret: ldr lr, __switch_data - mcr p15, 0, r0, c1, c0 - mrc p15, 0, r0, c1, c0, 0 @ read it back. - mov r0, r0 - mov r0, r0 + mcr p15, 0, r0, c1, c0, 0 + mrc p15, 0, r3, c0, c0, 0 + mov r3, r3 + mov r3, r3 mov pc, lr /* @@ -214,6 +214,11 @@ */ __create_page_tables: pgtbl r4, r5 @ page table address +#if defined(CONFIG_CPU_DCACHE_DISABLE) + bic r8, r8, #0x00c @ clear B, C +#elif defined(CONFIG_CPU_DCACHE_WRITETHROUGH) + bic r8, r8, #0x004 @ clear B +#endif /* * Clear the 16K level 1 swapper page table --- linux-2.4.27/arch/arm/kernel/irq.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/irq.c @@ -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 --- linux-2.4.27/arch/arm/kernel/ptrace.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/ptrace.c @@ -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); --- linux-2.4.27/arch/arm/kernel/semaphore.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/semaphore.c @@ -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\ --- linux-2.4.27/arch/arm/kernel/signal.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/kernel/signal.c @@ -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 */ } } --- linux-2.4.27/arch/arm/lib/Makefile~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/lib/Makefile @@ -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 := --- /dev/null +++ linux-2.4.27/arch/arm/lib/div64.S @@ -0,0 +1,59 @@ +#include + +#ifndef __ARMEB__ +ql .req r0 @ quotient low +qh .req r1 @ quotient high +onl .req r0 @ original dividend low +onh .req r1 @ original dividend high +nl .req r4 @ dividend low +nh .req r5 @ dividend high +res .req r4 @ result +#else +ql .req r1 +qh .req r0 +onl .req r1 +onh .req r0 +nl .req r5 +nh .req r4 +res .req r5 +#endif + +dl .req r3 @ divisor low +dh .req r2 @ divsor high + + +ENTRY(do_div64) + stmfd sp!, {r4, r5, lr} + mov nl, onl + movs nh, onh @ if high bits are zero + movne lr, #33 + moveq lr, #1 @ only divide low bits + moveq nh, onl + + tst dh, #0x80000000 + bne 2f +1: cmp nh, dh + bls 2f + add lr, lr, #1 + movs dh, dh, lsl #1 @ left justify disor + bpl 1b + +2: movs nh, onh + moveq dl, dh + moveq dh, #0 + movne dl, #0 + mov ql, #0 + mov qh, #0 +3: subs ip, nl, dl @ trial subtraction + sbcs ip, nh, dh + movcs nh, ip @ only update if successful + subcs nl, nl, dl @ (repeat the subtraction) + adcs ql, ql, ql @ C=1 if successful, shift into + adc qh, qh, qh @ quotient + movs dh, dh, lsr #1 @ shift base high part right + mov dl, dl, rrx @ shift base low part right + subs lr, lr, #1 + bne 3b + + mov r2, res + ldmfd sp!, {r4, r5, pc} --- linux-2.4.27/arch/arm/lib/putuser.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/lib/putuser.S @@ -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 --- linux-2.4.27/arch/arm/mach-at91rm9200/Makefile~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mach-at91rm9200/Makefile @@ -18,4 +18,8 @@ export-objs := +# LEDs support +leds-$(CONFIG_ARCH_AT91RM9200DK) += dk-leds.o +obj-$(CONFIG_LEDS) += $(leds-y) + include $(TOPDIR)/Rules.make --- /dev/null +++ linux-2.4.27/arch/arm/mach-at91rm9200/dk-leds.c @@ -0,0 +1,97 @@ +/* + * LED driver for the Atmel AT91RM9200 Development Kit. + * + * (c) SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + + +static inline void at91_led_on(void) +{ + AT91_SYS->PIOB_CODR = AT91C_PIO_PB2; +} + +static inline void at91_led_off(void) +{ + AT91_SYS->PIOB_SODR = AT91C_PIO_PB2; +} + +static inline void at91_led_toggle(void) +{ + unsigned long curr = AT91_SYS->PIOB_ODSR; + if (curr & AT91C_PIO_PB2) + AT91_SYS->PIOB_CODR = AT91C_PIO_PB2; + else + AT91_SYS->PIOB_SODR = AT91C_PIO_PB2; +} + + +/* + * Handle LED events. + */ +static void at91rm9200dk_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch(evt) { + case led_start: /* System startup */ + at91_led_on(); + break; + + case led_stop: /* System stop / suspend */ + at91_led_off(); + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: /* Every 50 timer ticks */ + at91_led_toggle(); + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: /* Entering idle state */ + at91_led_off(); + break; + + case led_idle_end: /* Exit idle state */ + at91_led_on(); + break; +#endif + + default: + break; + } + + local_irq_restore(flags); +} + + +static int __init leds_init(void) +{ + /* Enable PIO to access the LEDs */ + AT91_SYS->PIOB_PER = AT91C_PIO_PB2; + AT91_SYS->PIOB_OER = AT91C_PIO_PB2; + + leds_event = at91rm9200dk_leds_event; + + leds_event(led_start); + return 0; +} + +__initcall(leds_init); --- linux-2.4.27/arch/arm/mach-integrator/pci_v3.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mach-integrator/pci_v3.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -#include #include #include #include @@ -32,6 +31,7 @@ #include #include +#include #include #include #include @@ -447,15 +447,16 @@ #define SC_LBFADDR (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x20) #define SC_LBFCODE (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x24) -static int v3_fault(unsigned long addr, struct pt_regs *regs) +static int +v3_pci_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { unsigned long pc = instruction_pointer(regs); unsigned long instr = *(unsigned long *)pc; #if 0 char buf[128]; - sprintf(buf, "V3 fault: address=0x%08lx, pc=0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n", - addr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255, + sprintf(buf, "V3 fault: addr 0x%08lx, FSR 0x%03x, PC 0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n", + addr, fsr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255, v3_readb(V3_LB_ISTAT)); printk(KERN_DEBUG "%s", buf); printascii(buf); @@ -523,8 +524,6 @@ #endif } -extern int (*external_fault)(unsigned long addr, struct pt_regs *regs); - /* * V3_LB_BASE? - local bus address * V3_LB_MAP? - pci bus address @@ -539,7 +538,10 @@ /* * Hook in our fault handler for PCI errors */ - external_fault = v3_fault; + hook_fault_code(4, v3_pci_fault, SIGBUS, "external abort on linefetch"); + hook_fault_code(6, v3_pci_fault, SIGBUS, "external abort on linefetch"); + hook_fault_code(8, v3_pci_fault, SIGBUS, "external abort on non-linefetch"); + hook_fault_code(10, v3_pci_fault, SIGBUS, "external abort on non-linefetch"); spin_lock_irqsave(&v3_lock, flags); @@ -629,7 +631,7 @@ #if 0 ret = request_irq(IRQ_LBUSTIMEOUT, lb_timeout, 0, "bus timeout", NULL); if (ret) - printk(KERN_ERR "PCI: unable to grab local bus timeout ". + printk(KERN_ERR "PCI: unable to grab local bus timeout " "interrupt: %d\n", ret); #endif } --- linux-2.4.27/arch/arm/mach-sa1100/pm.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mach-sa1100/pm.c @@ -21,6 +21,8 @@ * * 2002-05-27: Nicolas Pitre Killed sleep.h and the kmalloced save array. * Storage is local on the stack now. + * 2003-06-25: Jeff Corrall + * Saved the GPIO levels for resume after sleep. */ #include #include @@ -70,13 +72,20 @@ int pm_do_suspend(void) { unsigned long sleep_save[SLEEP_SAVE_SIZE]; + unsigned long sleep_save_gpsr; + unsigned long sleep_save_gpcr; + unsigned long delta; cli(); leds_event(led_stop); /* preserve current time */ - RCNR = xtime.tv_sec; + delta = xtime.tv_sec - RCNR; + + /* save the current state of the GPIO output pins */ + sleep_save_gpsr = GPDR & GPLR; + sleep_save_gpcr = GPDR & ~GPLR; /* save vital registers */ SAVE(OSCR); @@ -121,6 +130,10 @@ printk(KERN_DEBUG "*** made it back from resume\n"); #endif + /* restore GPIO output state before enabling the pins */ + GPSR = sleep_save_gpsr; + GPCR = sleep_save_gpcr; + /* restore registers */ RESTORE(GPDR); RESTORE(GRER); @@ -151,7 +164,7 @@ RESTORE(ICMR); /* restore current time */ - xtime.tv_sec = RCNR; + xtime.tv_sec = RCNR + delta; leds_event(led_start); --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/sa1100_usb.h @@ -0,0 +1,193 @@ +/* + * sa1100_usb.h + * + * Public interface to the sa1100 USB core. For use by client modules + * like usb-eth and usb-char. + * + */ + +#ifndef _SA1100_USB_H +#define _SA1100_USB_H +#include + +typedef void (*usb_callback_t)(int flag, int size); + +/* in usb_ctl.c (see also descriptor methods at bottom of file) */ + +// Open the USB client for client and initialize data structures +// to default values, but _do not_ start UDC. +int sa1100_usb_open( const char * client_name ); + +// Start UDC running +int sa1100_usb_start( void ); + +// Immediately stop udc, fire off completion routines w/-EINTR +int sa1100_usb_stop( void ) ; + +// Disconnect client from usb core +int sa1100_usb_close( void ) ; + +// set notify callback for when core reaches configured state +// return previous pointer (if any) +typedef void (*usb_notify_t)(void); +usb_notify_t sa1100_set_configured_callback( usb_notify_t callback ); + +/* in usb_send.c */ +int sa1100_usb_xmitter_avail( void ); +int sa1100_usb_send(char *buf, int len, usb_callback_t callback); +void sa1100_usb_send_reset(void); + +/* in usb_recev.c */ +int sa1100_usb_recv(char *buf, int len, usb_callback_t callback); +void sa1100_usb_recv_reset(void); + +////////////////////////////////////////////////////////////////////////////// +// Descriptor Management +////////////////////////////////////////////////////////////////////////////// + +#define DescriptorHeader \ + __u8 bLength; \ + __u8 bDescriptorType + + +// --- Device Descriptor ------------------- + +typedef struct { + DescriptorHeader; + __u16 bcdUSB; /* USB specification revision number in BCD */ + __u8 bDeviceClass; /* USB class for entire device */ + __u8 bDeviceSubClass; /* USB subclass information for entire device */ + __u8 bDeviceProtocol; /* USB protocol information for entire device */ + __u8 bMaxPacketSize0; /* Max packet size for endpoint zero */ + __u16 idVendor; /* USB vendor ID */ + __u16 idProduct; /* USB product ID */ + __u16 bcdDevice; /* vendor assigned device release number */ + __u8 iManufacturer; /* index of manufacturer string */ + __u8 iProduct; /* index of string that describes product */ + __u8 iSerialNumber; /* index of string containing device serial number */ + __u8 bNumConfigurations; /* number fo configurations */ +} __attribute__ ((packed)) device_desc_t; + +// --- Configuration Descriptor ------------ + +typedef struct { + DescriptorHeader; + __u16 wTotalLength; /* total # of bytes returned in the cfg buf 4 this cfg */ + __u8 bNumInterfaces; /* number of interfaces in this cfg */ + __u8 bConfigurationValue; /* used to uniquely ID this cfg */ + __u8 iConfiguration; /* index of string describing configuration */ + __u8 bmAttributes; /* bitmap of attributes for ths cfg */ + __u8 MaxPower; /* power draw in 2ma units */ +} __attribute__ ((packed)) config_desc_t; + +// bmAttributes: +enum { USB_CONFIG_REMOTEWAKE=0x20, USB_CONFIG_SELFPOWERED=0x40, + USB_CONFIG_BUSPOWERED=0x80 }; +// MaxPower: +#define USB_POWER( x) ((x)>>1) /* convert mA to descriptor units of A for MaxPower */ + +// --- Interface Descriptor --------------- + +typedef struct { + DescriptorHeader; + __u8 bInterfaceNumber; /* Index uniquely identfying this interface */ + __u8 bAlternateSetting; /* ids an alternate setting for this interface */ + __u8 bNumEndpoints; /* number of endpoints in this interface */ + __u8 bInterfaceClass; /* USB class info applying to this interface */ + __u8 bInterfaceSubClass; /* USB subclass info applying to this interface */ + __u8 bInterfaceProtocol; /* USB protocol info applying to this interface */ + __u8 iInterface; /* index of string describing interface */ +} __attribute__ ((packed)) intf_desc_t; + +// --- Endpoint Descriptor --------------- + +typedef struct { + DescriptorHeader; + __u8 bEndpointAddress; /* 0..3 ep num, bit 7: 0 = 0ut 1= in */ + __u8 bmAttributes; /* 0..1 = 0: ctrl, 1: isoc, 2: bulk 3: intr */ + __u16 wMaxPacketSize; /* data payload size for this ep in this cfg */ + __u8 bInterval; /* polling interval for this ep in this cfg */ +} __attribute__ ((packed)) ep_desc_t; + +// bEndpointAddress: +enum { USB_OUT= 0, USB_IN=1 }; +#define USB_EP_ADDRESS(a,d) (((a)&0xf) | ((d) << 7)) +// bmAttributes: +enum { USB_EP_CNTRL=0, USB_EP_BULK=2, USB_EP_INT=3 }; + +// --- String Descriptor ------------------- + +typedef struct { + DescriptorHeader; + __u16 bString[1]; /* unicode string .. actaully 'n' __u16s */ +} __attribute__ ((packed)) string_desc_t; + +/*======================================================= + * Handy helpers when working with above + * + */ +// these are x86-style 16 bit "words" ... +#define make_word_c( w ) __constant_cpu_to_le16(w) +#define make_word( w ) __cpu_to_le16(w) + +// descriptor types +enum { USB_DESC_DEVICE=1, USB_DESC_CONFIG=2, USB_DESC_STRING=3, + USB_DESC_INTERFACE=4, USB_DESC_ENDPOINT=5 }; + + +/*======================================================= + * Default descriptor layout for SA-1100 and SA-1110 UDC + */ + +/* "config descriptor buffer" - that is, one config, + ..one interface and 2 endpoints */ +struct cdb { + config_desc_t cfg; + intf_desc_t intf; + ep_desc_t ep1; + ep_desc_t ep2; +} __attribute__ ((packed)); + + +/* all SA device descriptors */ +typedef struct { + device_desc_t dev; /* device descriptor */ + struct cdb b; /* bundle of descriptors for this cfg */ +} __attribute__ ((packed)) desc_t; + + +/*======================================================= + * Descriptor API + */ + +/* Get the address of the statically allocated desc_t structure + in the usb core driver. Clients can modify this between + the time they call sa1100_usb_open() and sa1100_usb_start() +*/ +desc_t * +sa1100_usb_get_descriptor_ptr( void ); + + +/* Set a pointer to the string descriptor at "index". The driver + ..has room for 8 string indicies internally. Index zero holds + ..a LANGID code and is set to US English by default. Inidices + ..1-7 are available for use in the config descriptors as client's + ..see fit. This pointer is assumed to be good as long as the + ..SA usb core is open (so statically allocate them). Returnes -EINVAL + ..if index out of range */ +int sa1100_usb_set_string_descriptor( int index, string_desc_t * p ); + +/* reverse of above */ +string_desc_t * +sa1100_usb_get_string_descriptor( int index ); + +/* kmalloc() a string descriptor and convert "p" to unicode in it */ +string_desc_t * +sa1100_usb_kmalloc_string_descriptor( const char * p ); + + + + + + +#endif /* _SA1100_USB_H */ --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/sa1111-ohci.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include + +#ifdef CONFIG_USB_OHCI + +/* + * The SA-1111 errata says that the DMA hardware needs to be exercised + * before the clocks are turned on to work properly. This code does + * a tiny dma transfer to prime to hardware. + * + * What DMA errata? I've checked October 1999 and February 2001, both + * of which do not mention such a bug, let alone any details of this + * work-around. + */ +static void __init sa1111_dma_setup(void) +{ + dma_addr_t dma_buf; + void * vbuf; + + /* DMA init & setup */ + + /* WARNING: The SA-1111 L3 function is used as part of this + * SA-1111 DMA errata workaround. + * + * N.B., When the L3 function is enabled, it uses GPIO_B<4:5> + * and takes precedence over the PS/2 mouse and GPIO_B + * functions. Refer to "Intel StrongARM SA-1111 Microprocessor + * Companion Chip, Sect 10.2" for details. So this "fix" may + * "break" support of either PS/2 mouse or GPIO_B if + * precautions are not taken to avoid collisions in + * configuration and use of these pins. AFAIK, no precautions + * are taken at this time. So it is likely that the action + * taken here may cause problems in PS/2 mouse and/or GPIO_B + * pin use elsewhere. + * + * But wait, there's more... What we're doing here is + * obviously altogether a bad idea. We're indiscrimanately bit + * flipping config for a few different functions here which + * are "owned" by other drivers. This needs to be handled + * better than it is being done here at this time. */ + + /* prime the dma engine with a tiny dma */ + SKPCR |= SKPCR_I2SCLKEN; + SKAUD |= SKPCR_L3CLKEN | SKPCR_SCLKEN; + + SACR0 |= 0x00003305; + SACR1 = 0x00000000; + + /* + * We need memory below 1MB. + * NOTE: consistent_alloc gives you some random virtual + * address as its return value, and the DMA address via + * the dma_addr_t pointer. + */ + vbuf = consistent_alloc(GFP_KERNEL | GFP_DMA, 4, &dma_buf); + + SADTSA = (unsigned long)dma_buf; + SADTCA = 4; + + SADTCS |= 0x00000011; + SKPCR |= SKPCR_DCLKEN; + + /* wait */ + udelay(100); + + /* clear reserved but, then disable SAC */ + SACR0 &= ~(0x00000002); + SACR0 &= ~(0x00000001); + + /* toggle bit clock direction */ + SACR0 |= 0x00000004; + SACR0 &= ~(0x00000004); + + SKAUD &= ~(SKPCR_L3CLKEN | SKPCR_SCLKEN); + + SKPCR &= ~SKPCR_I2SCLKEN; + + consistent_free(vbuf, 4, dma_buf); +} + +/* + * reset the SA-1111 usb controller and turn on it's clocks + */ +int __init sa1111_ohci_hcd_init(void) +{ + unsigned int usb_reset = 0; + + if (machine_is_xp860() || + machine_has_neponset() || + machine_is_pfs168() || + machine_is_badge4()) + usb_reset = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; + + /* + * turn on USB clocks + */ + SKPCR |= SKPCR_UCLKEN; + udelay(100); + + /* + * Force USB reset + */ + USB_RESET = USB_RESET_FORCEIFRESET; + USB_RESET |= USB_RESET_FORCEHCRESET; + udelay(100); + + /* + * Take out of reset + */ + USB_RESET = 0; + + /* + * set power sense and control lines (this from the diags code) + */ + USB_RESET = usb_reset; + + /* + * Huh? This is a _read only_ register --rmk + */ + USB_STATUS = 0; + + udelay(10); + + /* + * compensate for dma bug + */ + sa1111_dma_setup(); + + return 0; +} + +void sa1111_ohci_hcd_cleanup(void) +{ + /* turn the USB clock off */ + SKPCR &= ~SKPCR_UCLKEN; +} + +#endif /* CONFIG_USB_OHCI */ --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/usb-char.c @@ -0,0 +1,723 @@ +/* + * (C) Copyright 2000-2001 Extenex Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * usb-char.c + * + * Miscellaneous character device interface for SA1100 USB function + * driver. + * + * Background: + * The SA1100 function driver ported from the Compaq Itsy project + * has an interface, usb-eth.c, to feed network packets over the + * usb wire and into the Linux TCP/IP stack. + * + * This file replaces that one with a simple character device + * interface that allows unstructured "byte pipe" style reads and + * writes over the USB bulk endpoints by userspace programs. + * + * A new define, CONFIG_SA1100_USB_NETLINK, has been created that, + * when set, (the default) causes the ethernet interface to be used. + * When not set, this more pedestrian character interface is linked + * in instead. + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * + * ward.willats@extenex.com + * + * To do: + * - Can't dma into ring buffer directly with pci_map/unmap usb_recv + * uses and get bytes out at the same time DMA is going on. Investigate: + * a) changing usb_recv to use alloc_consistent() at client request; or + * b) non-ring-buffer based data structures. In the meantime, I am using + * a bounce buffer. Simple, but wasteful. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "usb-char.h" +#include "sa1100_usb.h" + + + +////////////////////////////////////////////////////////////////////////////// +// Driver Options +////////////////////////////////////////////////////////////////////////////// + +#define VERSION "0.4" + + +#define VERBOSITY 1 + +#if VERBOSITY +# define PRINTK(x, a...) printk (x, ## a) +#else +# define PRINTK(x, a...) /**/ +#endif + +////////////////////////////////////////////////////////////////////////////// +// Globals - Macros - Enums - Structures +////////////////////////////////////////////////////////////////////////////// +#ifndef MIN +#define MIN( a, b ) ((a)<(b)?(a):(b)) +#endif + +typedef int bool; enum { false = 0, true = 1 }; + +static const char pszMe[] = "usbchr: "; + +static wait_queue_head_t wq_read; +static wait_queue_head_t wq_write; +static wait_queue_head_t wq_poll; + +/* Serialze multiple writers onto the transmit hardware +.. since we sleep the writer during transmit to stay in +.. sync. (Multiple writers don't make much sense, but..) */ +static DECLARE_MUTEX( xmit_sem ); + +// size of usb DATA0/1 packets. 64 is standard maximum +// for bulk transport, though most hosts seem to be able +// to handle larger. +#define TX_PACKET_SIZE 64 +#define RX_PACKET_SIZE 64 +#define RBUF_SIZE (4*PAGE_SIZE) + +static struct wcirc_buf { + char *buf; + int in; + int out; +} rx_ring = { NULL, 0, 0 }; + +static struct { + unsigned long cnt_rx_complete; + unsigned long cnt_rx_errors; + unsigned long bytes_rx; + unsigned long cnt_tx_timeouts; + unsigned long cnt_tx_errors; + unsigned long bytes_tx; +} charstats; + + +static char * tx_buf = NULL; +static char * packet_buffer = NULL; +static int sending = 0; +static int usb_ref_count = 0; +static int last_tx_result = 0; +static int last_rx_result = 0; +static int last_tx_size = 0; +static struct timer_list tx_timer; + +////////////////////////////////////////////////////////////////////////////// +// Prototypes +////////////////////////////////////////////////////////////////////////////// +static char * what_the_f( int e ); +static void free_txrx_buffers( void ); +static void twiddle_descriptors( void ); +static void free_string_descriptors( void ) ; +static int usbc_open( struct inode *pInode, struct file *pFile ); +static void rx_done_callback_packet_buffer( int flag, int size ); + +static void tx_timeout( unsigned long ); +static void tx_done_callback( int flag, int size ); + +static ssize_t usbc_read( struct file *, char *, size_t, loff_t * ); +static ssize_t usbc_write( struct file *, const char *, size_t, loff_t * ); +static unsigned int usbc_poll( struct file *pFile, poll_table * pWait ); +static int usbc_ioctl( struct inode *pInode, struct file *pFile, + unsigned int nCmd, unsigned long argument ); +static int usbc_close( struct inode *pInode, struct file *pFile ); + +#ifdef CONFIG_SA1100_EXTENEX1 +static void extenex_configured_notify_proc( void ); +#endif +////////////////////////////////////////////////////////////////////////////// +// Private Helpers +////////////////////////////////////////////////////////////////////////////// + +static char * what_the_f( int e ) +{ + char * p; + switch( e ) { + case 0: + p = "noErr"; + break; + case -ENODEV: + p = "ENODEV - usb not in config state"; + break; + case -EBUSY: + p = "EBUSY - another request on the hardware"; + break; + case -EAGAIN: + p = "EAGAIN"; + break; + case -EINTR: + p = "EINTR - interrupted\n"; + break; + case -EPIPE: + p = "EPIPE - zero length xfer\n"; + break; + default: + p = "????"; + break; + } + return p; +} + +static void free_txrx_buffers( void ) +{ + if ( rx_ring.buf != NULL ) { + kfree( rx_ring.buf ); + rx_ring.buf = NULL; + } + if ( packet_buffer != NULL ) { + kfree( packet_buffer ); + packet_buffer = NULL; + } + if ( tx_buf != NULL ) { + kfree( tx_buf ); + tx_buf = NULL; + } +} + +/* twiddle_descriptors() + * It is between open() and start(). Setup descriptors. + */ +static void twiddle_descriptors( void ) +{ + desc_t * pDesc = sa1100_usb_get_descriptor_ptr(); + string_desc_t * pString; + + pDesc->b.ep1.wMaxPacketSize = make_word_c( RX_PACKET_SIZE ); + pDesc->b.ep1.bmAttributes = USB_EP_BULK; + pDesc->b.ep2.wMaxPacketSize = make_word_c( TX_PACKET_SIZE ); + pDesc->b.ep2.bmAttributes = USB_EP_BULK; + + if ( machine_is_extenex1() ) { +#ifdef CONFIG_SA1100_EXTENEX1 + pDesc->dev.idVendor = make_word_c( 0xC9F ); + pDesc->dev.idProduct = 1; + pDesc->dev.bcdDevice = make_word_c( 0x0001 ); + pDesc->b.cfg.bmAttributes = USB_CONFIG_SELFPOWERED; + pDesc->b.cfg.MaxPower = 0; + + pString = sa1100_usb_kmalloc_string_descriptor( "Extenex" ); + if ( pString ) { + sa1100_usb_set_string_descriptor( 1, pString ); + pDesc->dev.iManufacturer = 1; + } + + pString = sa1100_usb_kmalloc_string_descriptor( "Handheld Theater" ); + if ( pString ) { + sa1100_usb_set_string_descriptor( 2, pString ); + pDesc->dev.iProduct = 2; + } + + pString = sa1100_usb_kmalloc_string_descriptor( "00000000" ); + if ( pString ) { + sa1100_usb_set_string_descriptor( 3, pString ); + pDesc->dev.iSerialNumber = 3; + } + + pString = sa1100_usb_kmalloc_string_descriptor( "HHT Bulk Transfer" ); + if ( pString ) { + sa1100_usb_set_string_descriptor( 4, pString ); + pDesc->b.intf.iInterface = 4; + } + sa1100_set_configured_callback( extenex_configured_notify_proc ); +#endif + } +} + +static void free_string_descriptors( void ) +{ + if ( machine_is_extenex1() ) { + string_desc_t * pString; + int i; + for( i = 1 ; i <= 4 ; i++ ) { + pString = sa1100_usb_get_string_descriptor( i ); + if ( pString ) + kfree( pString ); + } + } +} + +////////////////////////////////////////////////////////////////////////////// +// ASYNCHRONOUS +////////////////////////////////////////////////////////////////////////////// +static void kick_start_rx( void ) +{ + if ( usb_ref_count ) { + int total_space = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE ); + if ( total_space >= RX_PACKET_SIZE ) { + sa1100_usb_recv( packet_buffer, + RX_PACKET_SIZE, + rx_done_callback_packet_buffer + ); + } + } +} +/* + * rx_done_callback_packet_buffer() + * We have completed a DMA xfer into the temp packet buffer. + * Move to ring. + * + * flag values: + * on init, -EAGAIN + * on reset, -EINTR + * on RPE, -EIO + * on short packet -EPIPE + */ +static void +rx_done_callback_packet_buffer( int flag, int size ) +{ + charstats.cnt_rx_complete++; + + if ( flag == 0 || flag == -EPIPE ) { + size_t n; + + charstats.bytes_rx += size; + + n = CIRC_SPACE_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE ); + n = MIN( n, size ); + size -= n; + + memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer, n ); + rx_ring.in = (rx_ring.in + n) & (RBUF_SIZE-1); + memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer + n, size ); + rx_ring.in = (rx_ring.in + size) & (RBUF_SIZE-1); + + wake_up_interruptible( &wq_read ); + wake_up_interruptible( &wq_poll ); + + last_rx_result = 0; + + kick_start_rx(); + + } else if ( flag != -EAGAIN ) { + charstats.cnt_rx_errors++; + last_rx_result = flag; + wake_up_interruptible( &wq_read ); + wake_up_interruptible( &wq_poll ); + } + else /* init, start a read */ + kick_start_rx(); +} + + +static void tx_timeout( unsigned long unused ) +{ + printk( "%stx timeout\n", pszMe ); + sa1100_usb_send_reset(); + charstats.cnt_tx_timeouts++; +} + + +// on init, -EAGAIN +// on reset, -EINTR +// on TPE, -EIO +static void tx_done_callback( int flags, int size ) +{ + if ( flags == 0 ) + charstats.bytes_tx += size; + else + charstats.cnt_tx_errors++; + last_tx_size = size; + last_tx_result = flags; + sending = 0; + wake_up_interruptible( &wq_write ); + wake_up_interruptible( &wq_poll ); +} + + +////////////////////////////////////////////////////////////////////////////// +// Workers +////////////////////////////////////////////////////////////////////////////// + +static int usbc_open( struct inode *pInode, struct file *pFile ) +{ + int retval = 0; + + PRINTK( KERN_DEBUG "%sopen()\n", pszMe ); + + /* start usb core */ + retval = sa1100_usb_open( "usb-char" ); + if ( retval ) return retval; + + /* allocate memory */ + if ( usb_ref_count == 0 ) { + tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); + if ( tx_buf == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE TX BUFFER\n", pszMe ); + goto malloc_fail; + } + rx_ring.buf = + (char*) kmalloc( RBUF_SIZE, GFP_KERNEL ); + + if ( rx_ring.buf == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE RX BUFFER\n", pszMe ); + goto malloc_fail; + } + + packet_buffer = + (char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); + + if ( packet_buffer == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n", pszMe ); + goto malloc_fail; + } + rx_ring.in = rx_ring.out = 0; + memset( &charstats, 0, sizeof( charstats ) ); + sending = 0; + last_tx_result = 0; + last_tx_size = 0; + } + + /* modify default descriptors */ + twiddle_descriptors(); + + retval = sa1100_usb_start(); + if ( retval ) { + printk( "%sAGHH! Could not USB core\n", pszMe ); + free_txrx_buffers(); + return retval; + } + usb_ref_count++; /* must do _before_ kick_start() */ + MOD_INC_USE_COUNT; + kick_start_rx(); + return 0; + + malloc_fail: + free_txrx_buffers(); + return -ENOMEM; +} + +/* + * Read endpoint. Note that you can issue a read to an + * unconfigured endpoint. Eventually, the host may come along + * and configure underneath this module and data will appear. + */ +static ssize_t usbc_read( struct file *pFile, char *pUserBuffer, + size_t stCount, loff_t *pPos ) +{ + ssize_t retval; + int flags; + DECLARE_WAITQUEUE( wait, current ); + + PRINTK( KERN_DEBUG "%sread()\n", pszMe ); + + local_irq_save( flags ); + if ( last_rx_result == 0 ) { + local_irq_restore( flags ); + } else { /* an error happended and receiver is paused */ + local_irq_restore( flags ); + last_rx_result = 0; + kick_start_rx(); + } + + add_wait_queue( &wq_read, &wait ); + while( 1 ) { + ssize_t bytes_avail; + ssize_t bytes_to_end; + + set_current_state( TASK_INTERRUPTIBLE ); + + /* snap ring buf state */ + local_irq_save( flags ); + bytes_avail = CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ); + bytes_to_end = CIRC_CNT_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE ); + local_irq_restore( flags ); + + if ( bytes_avail != 0 ) { + ssize_t bytes_to_move = MIN( stCount, bytes_avail ); + retval = 0; // will be bytes transfered + if ( bytes_to_move != 0 ) { + size_t n = MIN( bytes_to_end, bytes_to_move ); + if ( copy_to_user( pUserBuffer, + &rx_ring.buf[ rx_ring.out ], + n ) ) { + retval = -EFAULT; + break; + } + bytes_to_move -= n; + retval += n; + // might go 1 char off end, so wrap + rx_ring.out = ( rx_ring.out + n ) & (RBUF_SIZE-1); + if ( copy_to_user( pUserBuffer + n, + &rx_ring.buf[ rx_ring.out ], + bytes_to_move ) + ) { + retval = -EFAULT; + break; + } + rx_ring.out += bytes_to_move; // cannot wrap + retval += bytes_to_move; + kick_start_rx(); + } + break; + } + else if ( last_rx_result ) { + retval = last_rx_result; + break; + } + else if ( pFile->f_flags & O_NONBLOCK ) { // no data, can't sleep + retval = -EAGAIN; + break; + } + else if ( signal_pending( current ) ) { // no data, can sleep, but signal + retval = -ERESTARTSYS; + break; + } + schedule(); // no data, can sleep + } + set_current_state( TASK_RUNNING ); + remove_wait_queue( &wq_read, &wait ); + + if ( retval < 0 ) + printk( "%sread error %d - %s\n", pszMe, retval, what_the_f( retval ) ); + return retval; +} + +/* + * Write endpoint. This routine attempts to break the passed in buffer + * into usb DATA0/1 packet size chunks and send them to the host. + * (The lower-level driver tries to do this too, but easier for us + * to manage things here.) + * + * We are at the mercy of the host here, in that it must send an IN + * token to us to pull this data back, so hopefully some higher level + * protocol is expecting traffic to flow in that direction so the host + * is actually polling us. To guard against hangs, a 5 second timeout + * is used. + * + * This routine takes some care to only report bytes sent that have + * actually made it across the wire. Thus we try to stay in lockstep + * with the completion routine and only have one packet on the xmit + * hardware at a time. Multiple simultaneous writers will get + * "undefined" results. + * + */ +static ssize_t usbc_write( struct file *pFile, const char * pUserBuffer, + size_t stCount, loff_t *pPos ) +{ + ssize_t retval = 0; + ssize_t stSent = 0; + + DECLARE_WAITQUEUE( wait, current ); + + PRINTK( KERN_DEBUG "%swrite() %d bytes\n", pszMe, stCount ); + + down( &xmit_sem ); // only one thread onto the hardware at a time + + while( stCount != 0 && retval == 0 ) { + int nThisTime = MIN( TX_PACKET_SIZE, stCount ); + copy_from_user( tx_buf, pUserBuffer, nThisTime ); + sending = nThisTime; + retval = sa1100_usb_send( tx_buf, nThisTime, tx_done_callback ); + if ( retval < 0 ) { + char * p = what_the_f( retval ); + printk( "%sCould not queue xmission. rc=%d - %s\n", + pszMe, retval, p ); + sending = 0; + break; + } + /* now have something on the diving board */ + add_wait_queue( &wq_write, &wait ); + tx_timer.expires = jiffies + ( HZ * 5 ); + add_timer( &tx_timer ); + while( 1 ) { + set_current_state( TASK_INTERRUPTIBLE ); + if ( sending == 0 ) { /* it jumped into the pool */ + del_timer( &tx_timer ); + retval = last_tx_result; + if ( retval == 0 ) { + stSent += last_tx_size; + pUserBuffer += last_tx_size; + stCount -= last_tx_size; + } + else + printk( "%sxmission error rc=%d - %s\n", + pszMe, retval, what_the_f(retval) ); + break; + } + else if ( signal_pending( current ) ) { + del_timer( &tx_timer ); + printk( "%ssignal\n", pszMe ); + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state( TASK_RUNNING ); + remove_wait_queue( &wq_write, &wait ); + } + + up( &xmit_sem ); + + if ( 0 == retval ) + retval = stSent; + return retval; +} + +static unsigned int usbc_poll( struct file *pFile, poll_table * pWait ) +{ + unsigned int retval = 0; + + PRINTK( KERN_DEBUG "%poll()\n", pszMe ); + + poll_wait( pFile, &wq_poll, pWait ); + + if ( CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ) ) + retval |= POLLIN | POLLRDNORM; + if ( sa1100_usb_xmitter_avail() ) + retval |= POLLOUT | POLLWRNORM; + return retval; +} + +static int usbc_ioctl( struct inode *pInode, struct file *pFile, + unsigned int nCmd, unsigned long argument ) +{ + int retval = 0; + + switch( nCmd ) { + + case USBC_IOC_FLUSH_RECEIVER: + sa1100_usb_recv_reset(); + rx_ring.in = rx_ring.out = 0; + break; + + case USBC_IOC_FLUSH_TRANSMITTER: + sa1100_usb_send_reset(); + break; + + case USBC_IOC_FLUSH_ALL: + sa1100_usb_recv_reset(); + rx_ring.in = rx_ring.out = 0; + sa1100_usb_send_reset(); + break; + + default: + retval = -ENOIOCTLCMD; + break; + + } + return retval; +} + + +static int usbc_close( struct inode *pInode, struct file * pFile ) +{ + PRINTK( KERN_DEBUG "%sclose()\n", pszMe ); + if ( --usb_ref_count == 0 ) { + down( &xmit_sem ); + sa1100_usb_stop(); + free_txrx_buffers(); + free_string_descriptors(); + del_timer( &tx_timer ); + sa1100_usb_close(); + up( &xmit_sem ); + } + MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef CONFIG_SA1100_EXTENEX1 +#include "../../../drivers/char/ex_gpio.h" +void extenex_configured_notify_proc( void ) +{ + if ( exgpio_play_string( "440,1:698,1" ) == -EAGAIN ) + printk( "%sWanted to BEEP but ex_gpio not open\n", pszMe ); +} +#endif +////////////////////////////////////////////////////////////////////////////// +// Initialization +////////////////////////////////////////////////////////////////////////////// + +static struct file_operations usbc_fops = { + owner: THIS_MODULE, + open: usbc_open, + read: usbc_read, + write: usbc_write, + poll: usbc_poll, + ioctl: usbc_ioctl, + release: usbc_close, +}; + +static struct miscdevice usbc_misc_device = { + USBC_MINOR, "usb_char", &usbc_fops +}; + +/* + * usbc_init() + */ + +int __init usbc_init( void ) +{ + int rc; + +#if !defined( CONFIG_ARCH_SA1100 ) + return -ENODEV; +#endif + + if ( (rc = misc_register( &usbc_misc_device )) != 0 ) { + printk( KERN_WARNING "%sCould not register device 10, " + "%d. (%d)\n", pszMe, USBC_MINOR, rc ); + return -EBUSY; + } + + // initialize wait queues + init_waitqueue_head( &wq_read ); + init_waitqueue_head( &wq_write ); + init_waitqueue_head( &wq_poll ); + + // initialize tx timeout timer + init_timer( &tx_timer ); + tx_timer.function = tx_timeout; + + printk( KERN_INFO "USB Function Character Driver Interface" + " - %s, (C) 2001, Extenex Corp.\n", VERSION + ); + + return rc; +} + +void __exit usbc_exit( void ) +{ +} + +EXPORT_NO_SYMBOLS; + +module_init(usbc_init); +module_exit(usbc_exit); + + + +// end: usb-char.c + + + --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/usb-char.h @@ -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 */ + --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/usb-eth.c @@ -0,0 +1,447 @@ + /* + * Ethernet driver for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original initial ethernet test driver + * Copyright (c) Compaq Computer Corporation, 1999 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is still work in progress... + * + * 19/02/2001 - Now we are compatible with generic usbnet driver. green@iXcelerator.com + * 09/03/2001 - Dropped 'framing' scheme, as it seems to cause a lot of problems with little benefit. + * Now, since we do not know what size of packet we are receiving + * last usb packet in sequence will always be less than max packet + * receive endpoint can accept. + * Now the only way to check correct start of frame is to compare + * MAC address. Also now we are stalling on each receive error. + * + * 15/03/2001 - Using buffer to get data from UDC. DMA needs to have 8 byte + * aligned buffer, but this breaks IP code (unaligned access). + * + * 01/04/2001 - stall endpoint operations appeared to be very unstable, so + * they are disabled now. + * + * 03/06/2001 - Readded "zerocopy" receive path (tunable). + * + */ + +// Define DMA_NO_COPY if you want data to arrive directly into the +// receive network buffers, instead of arriving into bounce buffer +// and then get copied to network buffer. +// This does not work correctly right now. +#undef DMA_NO_COPY + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "sa1100_usb.h" + + +#define ETHERNET_VENDOR_ID 0x49f +#define ETHERNET_PRODUCT_ID 0x505A +#define MAX_PACKET 32768 +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +// Should be global, so that insmod can change these +int usb_rsize=64; +int usb_wsize=64; + +static struct usbe_info_t { + struct net_device *dev; + u16 packet_id; + struct net_device_stats stats; +} usbe_info; + +static char usb_eth_name[16] = "usbf"; +static struct net_device usb_eth_device; +static struct sk_buff *cur_tx_skb, *next_tx_skb; +static struct sk_buff *cur_rx_skb, *next_rx_skb; +static volatile int terminating; +#ifndef DMA_NO_COPY +static char *dmabuf; // we need that, as dma expect it's buffers to be aligned on 8 bytes boundary +#endif + +static int usb_change_mtu (struct net_device *net, int new_mtu) +{ + if (new_mtu <= sizeof (struct ethhdr) || new_mtu > MAX_PACKET) + return -EINVAL; + // no second zero-length packet read wanted after mtu-sized packets + if (((new_mtu + sizeof (struct ethhdr)) % usb_rsize) == 0) + return -EDOM; + + net->mtu = new_mtu; + return 0; +} + +static struct sk_buff * +usb_new_recv_skb(void) +{ + struct sk_buff *skb = alloc_skb( 2 + sizeof (struct ethhdr) + usb_eth_device.mtu,GFP_ATOMIC); + + if (skb) { + skb_reserve(skb, 2); + } + return skb; +} + +static u8 bcast_hwaddr[ETH_ALEN]={0xff,0xff,0xff,0xff,0xff,0xff}; +static void +usb_recv_callback(int flag, int size) +{ + struct sk_buff *skb; + + if (terminating) + return; + + skb = cur_rx_skb; + + /* flag validation */ + if (flag == 0) { + if ( skb_tailroom (skb) < size ) { // hey! we are overloaded!!! + usbe_info.stats.rx_over_errors++; + goto error; + } +#ifndef DMA_NO_COPY + memcpy(skb->tail,dmabuf,size); +#endif + skb_put(skb, size); + } else { + if (flag == -EIO) { + usbe_info.stats.rx_errors++; + } + goto error; + } + + /* validate packet length */ + if (size == usb_rsize ) { + /* packet not complete yet */ + skb = NULL; + } + + /* + * At this point skb is non null if we have a complete packet. + * If so take a fresh skb right away and restart USB receive without + * further delays, then process the packet. Otherwise resume USB + * receive on the current skb and exit. + */ + + if (skb) + cur_rx_skb = next_rx_skb; +#ifndef DMA_NO_COPY + sa1100_usb_recv(dmabuf, usb_rsize, + usb_recv_callback); +#else + sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), + usb_recv_callback); +#endif + if (!skb) + return; + + next_rx_skb = usb_new_recv_skb(); + if (!next_rx_skb) { + /* + * We can't aford loosing buffer space... + * So we drop the current packet and recycle its skb. + */ + printk("%s: can't allocate new skb\n", __FUNCTION__); + usbe_info.stats.rx_dropped++; + skb_trim(skb, 0); + next_rx_skb = skb; + return; + } + if ( skb->len >= sizeof(struct ethhdr)) { + if (memcmp(skb->data,usb_eth_device.dev_addr,ETH_ALEN) && memcmp(skb->data,bcast_hwaddr,ETH_ALEN) ) { + // This frame is not for us. nor it is broadcast + usbe_info.stats.rx_frame_errors++; + kfree_skb(skb); + goto error; + } + } + + if (skb->len) { + int status; +// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ? + + skb->dev = &usb_eth_device; + skb->protocol = eth_type_trans (skb, &usb_eth_device); + usbe_info.stats.rx_packets++; + usbe_info.stats.rx_bytes += skb->len; + skb->ip_summed = CHECKSUM_NONE; + status = netif_rx (skb); + if (status != NET_RX_SUCCESS) + printk("netif_rx failed with code %d\n",status); + } else { +error: + /* + * Error due to HW addr mismatch, or IO error. + * Recycle the current skb and reset USB reception. + */ + skb_trim(cur_rx_skb, 0); +// if ( flag == -EINTR || flag == -EAGAIN ) // only if we are coming out of stall +#ifndef DMA_NO_COPY + sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback); +#else + sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), usb_recv_callback); +#endif + } +} + + +static void +usb_send_callback(int flag, int size) +{ + struct net_device *dev = usbe_info.dev; + struct net_device_stats *stats; + struct sk_buff *skb=cur_tx_skb; + int ret; + + if (terminating) + return; + + stats = &usbe_info.stats; + switch (flag) { + case 0: + stats->tx_packets++; + stats->tx_bytes += size; + break; + case -EIO: + stats->tx_errors++; + break; + default: + stats->tx_dropped++; + break; + } + + cur_tx_skb = next_tx_skb; + next_tx_skb = NULL; + dev_kfree_skb_irq(skb); + if (!cur_tx_skb) + return; + + dev->trans_start = jiffies; + ret = sa1100_usb_send(cur_tx_skb->data, cur_tx_skb->len, usb_send_callback); + if (ret) { + /* If the USB core can't accept the packet, we drop it. */ + dev_kfree_skb_irq(cur_tx_skb); + cur_tx_skb = NULL; + usbe_info.stats.tx_carrier_errors++; + } + netif_wake_queue(dev); +} + +static int +usb_eth_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int ret; + unsigned long flags; + + if (next_tx_skb) { + printk("%s: called with next_tx_skb != NULL\n", __FUNCTION__); + return 1; + } + + if (skb_shared (skb)) { + struct sk_buff *skb2 = skb_unshare(skb, GFP_ATOMIC); + if (!skb2) { + usbe_info.stats.tx_dropped++; + dev_kfree_skb(skb); + return 1; + } + skb = skb2; + } + + if ((skb->len % usb_wsize) == 0) { + skb->len++; // other side will ignore this one, anyway. + } + + local_irq_save(flags); + if (cur_tx_skb) { + next_tx_skb = skb; + netif_stop_queue(dev); + } else { + cur_tx_skb = skb; + dev->trans_start = jiffies; + ret = sa1100_usb_send(skb->data, skb->len, usb_send_callback); + if (ret) { + /* If the USB core can't accept the packet, we drop it. */ + dev_kfree_skb(skb); + cur_tx_skb = NULL; + usbe_info.stats.tx_carrier_errors++; + } + } + local_irq_restore(flags); + return 0; +} + +static void +usb_xmit_timeout(struct net_device *dev ) +{ + sa1100_usb_send_reset(); + dev->trans_start = jiffies; + netif_wake_queue(dev); +} + + +static int +usb_eth_open(struct net_device *dev) +{ + terminating = 0; + cur_tx_skb = next_tx_skb = NULL; + cur_rx_skb = usb_new_recv_skb(); + next_rx_skb = usb_new_recv_skb(); + if (!cur_rx_skb || !next_rx_skb) { + printk("%s: can't allocate new skb\n", __FUNCTION__); + if (cur_rx_skb) + kfree_skb(cur_rx_skb); + if (next_rx_skb) + kfree_skb(next_rx_skb); + return -ENOMEM;; + } + + MOD_INC_USE_COUNT; +#ifndef DMA_NO_COPY + sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback); +#else + sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), + usb_recv_callback); +#endif + return 0; +} + +static int +usb_eth_release(struct net_device *dev) +{ + terminating = 1; + sa1100_usb_send_reset(); + sa1100_usb_recv_reset(); + if (cur_tx_skb) + kfree_skb(cur_tx_skb); + if (next_tx_skb) + kfree_skb(next_tx_skb); + if (cur_rx_skb) + kfree_skb(cur_rx_skb); + if (next_rx_skb) + kfree_skb(next_rx_skb); + MOD_DEC_USE_COUNT; + return 0; +} + +static struct net_device_stats * +usb_eth_stats(struct net_device *dev) +{ + struct usbe_info_t *priv = (struct usbe_info_t*) dev->priv; + struct net_device_stats *stats=NULL; + + if (priv) + stats = &priv->stats; + return stats; +} + +static int +usb_eth_probe(struct net_device *dev) +{ + u8 node_id [ETH_ALEN]; + + get_random_bytes (node_id, sizeof node_id); + node_id [0] &= 0xfe; // clear multicast bit + + /* + * Assign the hardware address of the board: + * generate it randomly, as there can be many such + * devices on the bus. + */ + memcpy (dev->dev_addr, node_id, sizeof node_id); + + dev->open = usb_eth_open; + dev->change_mtu = usb_change_mtu; + dev->stop = usb_eth_release; + dev->hard_start_xmit = usb_eth_xmit; + dev->get_stats = usb_eth_stats; + dev->watchdog_timeo = 1*HZ; + dev->tx_timeout = usb_xmit_timeout; + dev->priv = &usbe_info; + + usbe_info.dev = dev; + + /* clear the statistics */ + memset(&usbe_info.stats, 0, sizeof(struct net_device_stats)); + + ether_setup(dev); + dev->flags &= ~IFF_MULTICAST; + dev->flags &= ~IFF_BROADCAST; + //dev->flags |= IFF_NOARP; + + return 0; +} + +#ifdef MODULE +MODULE_PARM(usb_rsize, "1i"); +MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to sa1100"); +MODULE_PARM(usb_wsize, "1i"); +MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from sa1100 to host"); +#endif + +static int __init +usb_eth_init(void) +{ + int rc; + +#ifndef DMA_NO_COPY + dmabuf = kmalloc( usb_rsize, GFP_KERNEL | GFP_DMA ); + if (!dmabuf) + return -ENOMEM; +#endif + strncpy(usb_eth_device.name, usb_eth_name, IFNAMSIZ); + usb_eth_device.init = usb_eth_probe; + if (register_netdev(&usb_eth_device) != 0) + return -EIO; + + rc = sa1100_usb_open( "usb-eth" ); + if ( rc == 0 ) { + string_desc_t * pstr; + desc_t * pd = sa1100_usb_get_descriptor_ptr(); + + pd->b.ep1.wMaxPacketSize = make_word( usb_rsize ); + pd->b.ep2.wMaxPacketSize = make_word( usb_wsize ); + pd->dev.idVendor = ETHERNET_VENDOR_ID; + pd->dev.idProduct = ETHERNET_PRODUCT_ID; + pstr = sa1100_usb_kmalloc_string_descriptor( "SA1100 USB NIC" ); + if ( pstr ) { + sa1100_usb_set_string_descriptor( 1, pstr ); + pd->dev.iProduct = 1; + } + rc = sa1100_usb_start(); + } + return rc; +} + +module_init(usb_eth_init); + +static void __exit +usb_eth_cleanup(void) +{ + string_desc_t * pstr; + sa1100_usb_stop(); + sa1100_usb_close(); + if ( (pstr = sa1100_usb_get_string_descriptor(1)) != NULL ) + kfree( pstr ); +#ifndef DMA_NO_COPY + kfree(dmabuf); +#endif + unregister_netdev(&usb_eth_device); +} + +module_exit(usb_eth_cleanup); --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/usb_ctl.c @@ -0,0 +1,774 @@ + /* + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation, 2001 + * + * usb_ctl.c + * + * SA1100 USB controller core driver. + * + * This file provides interrupt routing and overall coordination + * of the three endpoints in usb_ep0, usb_receive (1), and usb_send (2). + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sa1100_usb.h" +#include "usb_ctl.h" + +////////////////////////////////////////////////////////////////////////////// +// Prototypes +////////////////////////////////////////////////////////////////////////////// + +int usbctl_next_state_on_event( int event ); +static void udc_int_hndlr(int, void *, struct pt_regs *); +static void initialize_descriptors( void ); +static void soft_connect_hook( int enable ); +static void udc_disable(void); +static void udc_enable(void); + +#if CONFIG_PROC_FS +#define PROC_NODE_NAME "sausb" +static int usbctl_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); +#endif + +////////////////////////////////////////////////////////////////////////////// +// Globals +////////////////////////////////////////////////////////////////////////////// +static const char pszMe[] = "usbctl: "; +struct usb_info_t usbd_info; /* global to ep0, usb_recv, usb_send */ + +/* device descriptors */ +static desc_t desc; + +#define MAX_STRING_DESC 8 +static string_desc_t * string_desc_array[ MAX_STRING_DESC ]; +static string_desc_t sd_zero; /* special sd_zero holds language codes */ + +// called when configured +static usb_notify_t configured_callback = NULL; + +enum { kStateZombie = 0, kStateZombieSuspend = 1, + kStateDefault = 2, kStateDefaultSuspend = 3, + kStateAddr = 4, kStateAddrSuspend = 5, + kStateConfig = 6, kStateConfigSuspend = 7 +}; + +static int device_state_machine[8][6] = { +// suspend reset resume adddr config deconfig +/* zombie */ { kStateZombieSuspend, kStateDefault, kError, kError, kError, kError }, +/* zom sus */ { kError, kStateDefault, kStateZombie, kError, kError, kError }, +/* default */ { kStateDefaultSuspend, kError, kStateDefault, kStateAddr, kError, kError }, +/* def sus */ { kError, kStateDefault, kStateDefault, kError, kError, kError }, +/* addr */ { kStateAddrSuspend, kStateDefault, kError, kError, kStateConfig, kError }, +/* addr sus */{ kError, kStateDefault, kStateAddr, kError, kError, kError }, +/* config */ { kStateConfigSuspend, kStateDefault, kError, kError, kError, kStateAddr }, +/* cfg sus */ { kError, kStateDefault, kStateConfig, kError, kError, kError } +}; + +/* "device state" is the usb device framework state, as opposed to the + "state machine state" which is whatever the driver needs and is much + more fine grained +*/ +static int sm_state_to_device_state[8] = +// zombie zom suspend default default sus +{ USB_STATE_POWERED, USB_STATE_SUSPENDED, USB_STATE_DEFAULT, USB_STATE_SUSPENDED, +// addr addr sus config config sus + USB_STATE_ADDRESS, USB_STATE_SUSPENDED, USB_STATE_CONFIGURED, USB_STATE_SUSPENDED +}; + +static char * state_names[8] = +{ "zombie", "zombie suspended", "default", "default suspended", + "address", "address suspended", "configured", "config suspended" +}; + +static char * event_names[6] = +{ "suspend", "reset", "resume", + "address assigned", "configure", "de-configure" +}; + +static char * device_state_names[] = +{ "not attached", "attached", "powered", "default", + "address", "configured", "suspended" }; + +static int sm_state = kStateZombie; + +////////////////////////////////////////////////////////////////////////////// +// Async +////////////////////////////////////////////////////////////////////////////// +static void core_kicker(void); + +static inline void enable_resume_mask_suspend( void ); +static inline void enable_suspend_mask_resume(void); + +static void +udc_int_hndlr(int irq, void *dev_id, struct pt_regs *regs) +{ + __u32 status = Ser0UDCSR; + + /* ReSeT Interrupt Request - UDC has been reset */ + if ( status & UDCSR_RSTIR ) + { + if ( usbctl_next_state_on_event( kEvReset ) != kError ) + { + /* starting 20ms or so reset sequence now... */ + printk("%sResetting\n", pszMe); + ep0_reset(); // just set state to idle + ep1_reset(); // flush dma, clear false stall + ep2_reset(); // flush dma, clear false stall + } + // mask reset ints, they flood during sequence, enable + // suspend and resume + Ser0UDCCR |= UDCCR_REM; // mask reset + Ser0UDCCR &= ~(UDCCR_SUSIM | UDCCR_RESIM); // enable suspend and resume + UDC_flip( Ser0UDCSR, status ); // clear all pending sources + return; // <-- no reason to continue if resetting + } + // else we have done something other than reset, so be sure reset enabled + UDC_clear( Ser0UDCCR, UDCCR_REM ); + + /* RESume Interrupt Request */ + if ( status & UDCSR_RESIR ) + { + usbctl_next_state_on_event( kEvResume ); + core_kicker(); + enable_suspend_mask_resume(); + } + + /* SUSpend Interrupt Request */ + if ( status & UDCSR_SUSIR ) + { + usbctl_next_state_on_event( kEvSuspend ); + enable_resume_mask_suspend(); + } + + UDC_flip(Ser0UDCSR, status); // clear all pending sources + + if (status & UDCSR_EIR) + ep0_int_hndlr(); + + if (status & UDCSR_RIR) + ep1_int_hndlr(status); + + if (status & UDCSR_TIR) + ep2_int_hndlr(status); +} + +static inline void enable_resume_mask_suspend( void ) +{ + int i = 0; + + while( 1 ) { + Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events + udelay( i ); + if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR) ) + break; + if ( ++i == 50 ) { + printk( "%senable_resume(): Could not set SUSIM %8.8X\n", + pszMe, Ser0UDCCR ); + break; + } + } + + i = 0; + while( 1 ) { + Ser0UDCCR &= ~UDCCR_RESIM; + udelay( i ); + if ( ( Ser0UDCCR & UDCCR_RESIM ) == 0 + || + (Ser0UDCSR & UDCSR_RSTIR) + ) + break; + if ( ++i == 50 ) { + printk( "%senable_resume(): Could not clear RESIM %8.8X\n", + pszMe, Ser0UDCCR ); + break; + } + } +} + +static inline void enable_suspend_mask_resume(void) +{ + int i = 0; + while( 1 ) { + Ser0UDCCR |= UDCCR_RESIM; // mask future resume events + udelay( i ); + if ( Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR) ) + break; + if ( ++i == 50 ) { + printk( "%senable_suspend(): Could not set RESIM %8.8X\n", + pszMe, Ser0UDCCR ); + break; + } + } + i = 0; + while( 1 ) { + Ser0UDCCR &= ~UDCCR_SUSIM; + udelay( i ); + if ( ( Ser0UDCCR & UDCCR_SUSIM ) == 0 + || + (Ser0UDCSR & UDCSR_RSTIR) + ) + break; + if ( ++i == 50 ) { + printk( "%senable_suspend(): Could not clear SUSIM %8.8X\n", + pszMe, Ser0UDCCR ); + break; + } + } +} + + +////////////////////////////////////////////////////////////////////////////// +// Public Interface +////////////////////////////////////////////////////////////////////////////// + +/* Open SA usb core on behalf of a client, but don't start running */ + +int +sa1100_usb_open( const char * client ) +{ + if ( usbd_info.client_name != NULL ) + return -EBUSY; + + usbd_info.client_name = (char*) client; + memset(&usbd_info.stats, 0, sizeof(struct usb_stats_t)); + memset(string_desc_array, 0, sizeof(string_desc_array)); + + /* hack to start in zombie suspended state */ + sm_state = kStateZombieSuspend; + usbd_info.state = USB_STATE_SUSPENDED; + + /* create descriptors for enumeration */ + initialize_descriptors(); + + printk( "%sOpened for %s\n", pszMe, client ); + return 0; +} + +/* Start running. Must have called usb_open (above) first */ +int +sa1100_usb_start( void ) +{ + if ( usbd_info.client_name == NULL ) { + printk( "%s%s - no client registered\n", + pszMe, __FUNCTION__ ); + return -EPERM; + } + + /* start UDC internal machinery running */ + udc_enable(); + udelay( 100 ); + + /* clear stall - receiver seems to start stalled? 19Jan01ww */ + /* also clear other stuff just to be thurough 22Feb01ww */ + UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC ); + UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC ); + + /* mask everything */ + Ser0UDCCR = 0xFC; + + /* flush DMA and fire through some -EAGAINs */ + ep1_init( usbd_info.dmach_rx ); + ep2_init( usbd_info.dmach_tx ); + + /* give endpoint notification we are starting */ + ep1_state_change_notify( USB_STATE_SUSPENDED ); + ep2_state_change_notify( USB_STATE_SUSPENDED ); + + /* enable any platform specific hardware */ + soft_connect_hook( 1 ); + + /* clear all top-level sources */ + Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR | + UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ; + + /* EXERIMENT - a short line in the spec says toggling this + ..bit diddles the internal state machine in the udc to + ..expect a suspend */ + Ser0UDCCR |= UDCCR_RESIM; + /* END EXPERIMENT 10Feb01ww */ + + /* enable any platform specific hardware */ + soft_connect_hook( 1 ); + + /* enable interrupts. If you are unplugged you will + immediately get a suspend interrupt. If you are plugged + and have a soft connect-circuit, you will get a reset + If you are plugged without a soft-connect, I think you + also get suspend. In short, start with suspend masked + and everything else enabled */ + UDC_write( Ser0UDCCR, UDCCR_SUSIM ); + + printk( "%sStarted for %s\n", pszMe, usbd_info.client_name ); + return 0; +} + +/* Stop USB core from running */ +int +sa1100_usb_stop( void ) +{ + if ( usbd_info.client_name == NULL ) { + printk( "%s%s - no client registered\n", + pszMe, __FUNCTION__ ); + return -EPERM; + } + /* mask everything */ + Ser0UDCCR = 0xFC; + ep1_reset(); + ep2_reset(); + udc_disable(); + printk( "%sStopped\n", pszMe ); + return 0; +} + +/* Tell SA core client is through using it */ +int +sa1100_usb_close( void ) +{ + if ( usbd_info.client_name == NULL ) { + printk( "%s%s - no client registered\n", + pszMe, __FUNCTION__ ); + return -EPERM; + } + usbd_info.client_name = NULL; + printk( "%sClosed\n", pszMe ); + return 0; +} + +/* set a proc to be called when device is configured */ +usb_notify_t sa1100_set_configured_callback( usb_notify_t func ) +{ + usb_notify_t retval = configured_callback; + configured_callback = func; + return retval; +} + +/*==================================================== + * Descriptor Manipulation. + * Use these between open() and start() above to setup + * the descriptors for your device. + * + */ + +/* get pointer to static default descriptor */ +desc_t * +sa1100_usb_get_descriptor_ptr( void ) { return &desc; } + +/* optional: set a string descriptor */ +int +sa1100_usb_set_string_descriptor( int i, string_desc_t * p ) +{ + int retval; + if ( i < MAX_STRING_DESC ) { + string_desc_array[i] = p; + retval = 0; + } else { + retval = -EINVAL; + } + return retval; +} + +/* optional: get a previously set string descriptor */ +string_desc_t * +sa1100_usb_get_string_descriptor( int i ) +{ + return ( i < MAX_STRING_DESC ) + ? string_desc_array[i] + : NULL; +} + + +/* optional: kmalloc and unicode up a string descriptor */ +string_desc_t * +sa1100_usb_kmalloc_string_descriptor( const char * p ) +{ + string_desc_t * pResult = NULL; + + if ( p ) { + int len = strlen( p ); + int uni_len = len * sizeof( __u16 ); + pResult = (string_desc_t*) kmalloc( uni_len + 2, GFP_KERNEL ); /* ugh! */ + if ( pResult != NULL ) { + int i; + pResult->bLength = uni_len + 2; + pResult->bDescriptorType = USB_DESC_STRING; + for( i = 0; i < len ; i++ ) { + pResult->bString[i] = make_word( (__u16) p[i] ); + } + } + } + return pResult; +} + +////////////////////////////////////////////////////////////////////////////// +// Exports to rest of driver +////////////////////////////////////////////////////////////////////////////// + +/* called by the int handler here and the two endpoint files when interesting + .."events" happen */ + +int +usbctl_next_state_on_event( int event ) +{ + int next_state = device_state_machine[ sm_state ][ event ]; + if ( next_state != kError ) + { + int next_device_state = sm_state_to_device_state[ next_state ]; + printk( "%s%s --> [%s] --> %s. Device in %s state.\n", + pszMe, state_names[ sm_state ], event_names[ event ], + state_names[ next_state ], device_state_names[ next_device_state ] ); + + sm_state = next_state; + if ( usbd_info.state != next_device_state ) + { + if ( configured_callback != NULL + && + next_device_state == USB_STATE_CONFIGURED + && + usbd_info.state != USB_STATE_SUSPENDED + ) { + configured_callback(); + } + usbd_info.state = next_device_state; + ep1_state_change_notify( next_device_state ); + ep2_state_change_notify( next_device_state ); + } + } +#if 0 + else + printk( "%s%s --> [%s] --> ??? is an error.\n", + pszMe, state_names[ sm_state ], event_names[ event ] ); +#endif + return next_state; +} + +////////////////////////////////////////////////////////////////////////////// +// Private Helpers +////////////////////////////////////////////////////////////////////////////// + +/* setup default descriptors */ + +static void +initialize_descriptors(void) +{ + desc.dev.bLength = sizeof( device_desc_t ); + desc.dev.bDescriptorType = USB_DESC_DEVICE; + desc.dev.bcdUSB = 0x100; /* 1.0 */ + desc.dev.bDeviceClass = 0xFF; /* vendor specific */ + desc.dev.bDeviceSubClass = 0; + desc.dev.bDeviceProtocol = 0; + desc.dev.bMaxPacketSize0 = 8; /* ep0 max fifo size */ + desc.dev.idVendor = 0; /* vendor ID undefined */ + desc.dev.idProduct = 0; /* product */ + desc.dev.bcdDevice = 0; /* vendor assigned device release num */ + desc.dev.iManufacturer = 0; /* index of manufacturer string */ + desc.dev.iProduct = 0; /* index of product description string */ + desc.dev.iSerialNumber = 0; /* index of string holding product s/n */ + desc.dev.bNumConfigurations = 1; + + desc.b.cfg.bLength = sizeof( config_desc_t ); + desc.b.cfg.bDescriptorType = USB_DESC_CONFIG; + desc.b.cfg.wTotalLength = make_word_c( sizeof(struct cdb) ); + desc.b.cfg.bNumInterfaces = 1; + desc.b.cfg.bConfigurationValue = 1; + desc.b.cfg.iConfiguration = 0; + desc.b.cfg.bmAttributes = USB_CONFIG_BUSPOWERED; + desc.b.cfg.MaxPower = USB_POWER( 500 ); + + desc.b.intf.bLength = sizeof( intf_desc_t ); + desc.b.intf.bDescriptorType = USB_DESC_INTERFACE; + desc.b.intf.bInterfaceNumber = 0; /* unique intf index*/ + desc.b.intf.bAlternateSetting = 0; + desc.b.intf.bNumEndpoints = 2; + desc.b.intf.bInterfaceClass = 0xFF; /* vendor specific */ + desc.b.intf.bInterfaceSubClass = 0; + desc.b.intf.bInterfaceProtocol = 0; + desc.b.intf.iInterface = 0; + + desc.b.ep1.bLength = sizeof( ep_desc_t ); + desc.b.ep1.bDescriptorType = USB_DESC_ENDPOINT; + desc.b.ep1.bEndpointAddress = USB_EP_ADDRESS( 1, USB_OUT ); + desc.b.ep1.bmAttributes = USB_EP_BULK; + desc.b.ep1.wMaxPacketSize = make_word_c( 64 ); + desc.b.ep1.bInterval = 0; + + desc.b.ep2.bLength = sizeof( ep_desc_t ); + desc.b.ep2.bDescriptorType = USB_DESC_ENDPOINT; + desc.b.ep2.bEndpointAddress = USB_EP_ADDRESS( 2, USB_IN ); + desc.b.ep2.bmAttributes = USB_EP_BULK; + desc.b.ep2.wMaxPacketSize = make_word_c( 64 ); + desc.b.ep2.bInterval = 0; + + /* set language */ + /* See: http://www.usb.org/developers/data/USB_LANGIDs.pdf */ + sd_zero.bDescriptorType = USB_DESC_STRING; + sd_zero.bLength = sizeof( string_desc_t ); + sd_zero.bString[0] = make_word_c( 0x409 ); /* American English */ + sa1100_usb_set_string_descriptor( 0, &sd_zero ); +} + +/* soft_connect_hook() + * Some devices have platform-specific circuitry to make USB + * not seem to be plugged in, even when it is. This allows + * software to control when a device 'appears' on the USB bus + * (after Linux has booted and this driver has loaded, for + * example). If you have such a circuit, control it here. + */ +static void +soft_connect_hook( int enable ) +{ +#ifdef CONFIG_SA1100_EXTENEX1 + if (machine_is_extenex1() ) { + if ( enable ) { + PPDR |= PPC_USB_SOFT_CON; + PPSR |= PPC_USB_SOFT_CON; + } else { + PPSR &= ~PPC_USB_SOFT_CON; + PPDR &= ~PPC_USB_SOFT_CON; + } + } +#endif +} + +/* disable the UDC at the source */ +static void +udc_disable(void) +{ + soft_connect_hook( 0 ); + UDC_set( Ser0UDCCR, UDCCR_UDD ); +} + + +/* enable the udc at the source */ +static void +udc_enable(void) +{ + UDC_clear(Ser0UDCCR, UDCCR_UDD); +} + +// HACK DEBUG 3Mar01ww +// Well, maybe not, it really seems to help! 08Mar01ww +static void +core_kicker( void ) +{ + __u32 car = Ser0UDCAR; + __u32 imp = Ser0UDCIMP; + __u32 omp = Ser0UDCOMP; + + UDC_set( Ser0UDCCR, UDCCR_UDD ); + udelay( 300 ); + UDC_clear(Ser0UDCCR, UDCCR_UDD); + + Ser0UDCAR = car; + Ser0UDCIMP = imp; + Ser0UDCOMP = omp; +} + +////////////////////////////////////////////////////////////////////////////// +// Proc Filesystem Support +////////////////////////////////////////////////////////////////////////////// + +#if CONFIG_PROC_FS + +#define SAY( fmt, args... ) p += sprintf(p, fmt, ## args ) +#define SAYV( num ) p += sprintf(p, num_fmt, "Value", num ) +#define SAYC( label, yn ) p += sprintf(p, yn_fmt, label, yn ) +#define SAYS( label, v ) p += sprintf(p, cnt_fmt, label, v ) + +static int usbctl_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + const char * num_fmt = "%25.25s: %8.8lX\n"; + const char * cnt_fmt = "%25.25s: %lu\n"; + const char * yn_fmt = "%25.25s: %s\n"; + const char * yes = "YES"; + const char * no = "NO"; + unsigned long v; + char * p = page; + int len; + + SAY( "SA1100 USB Controller Core\n" ); + SAY( "USB state: %s (%s) %d\n", + device_state_names[ sm_state_to_device_state[ sm_state ] ], + state_names[ sm_state ], + sm_state ); + + SAYS( "ep0 bytes read", usbd_info.stats.ep0_bytes_read ); + SAYS( "ep0 bytes written", usbd_info.stats.ep0_bytes_written ); + SAYS( "ep0 FIFO read failures", usbd_info.stats.ep0_fifo_write_failures ); + SAYS( "ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures ); + + SAY( "\n" ); + + v = Ser0UDCAR; + SAY( "%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v ); + v = Ser0UDCIMP; + SAY( "%25.25s: %ld (%8.8lX)\n", "IN max packet size", v+1, v ); + v = Ser0UDCOMP; + SAY( "%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v ); + + v = Ser0UDCCR; + SAY( "\nUDC Mask Register\n" ); + SAYV( v ); + SAYC( "UDC Active", ( v & UDCCR_UDA ) ? yes : no ); + SAYC( "Suspend interrupts masked", ( v & UDCCR_SUSIM ) ? yes : no ); + SAYC( "Resume interrupts masked", ( v & UDCCR_RESIM ) ? yes : no ); + SAYC( "Reset interrupts masked", ( v & UDCCR_REM ) ? yes : no ); + + v = Ser0UDCSR; + SAY( "\nUDC Interrupt Request Register\n" ); + SAYV( v ); + SAYC( "Reset pending", ( v & UDCSR_RSTIR ) ? yes : no ); + SAYC( "Suspend pending", ( v & UDCSR_SUSIR ) ? yes : no ); + SAYC( "Resume pending", ( v & UDCSR_RESIR ) ? yes : no ); + SAYC( "ep0 pending", ( v & UDCSR_EIR ) ? yes : no ); + SAYC( "receiver pending", ( v & UDCSR_RIR ) ? yes : no ); + SAYC( "tramsitter pending", ( v & UDCSR_TIR ) ? yes : no ); + +#ifdef CONFIG_SA1100_EXTENEX1 + SAYC( "\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden" ); +#endif + +#if 0 + v = Ser0UDCCS0; + SAY( "\nUDC Endpoint Zero Status Register\n" ); + SAYV( v ); + SAYC( "Out Packet Ready", ( v & UDCCS0_OPR ) ? yes : no ); + SAYC( "In Packet Ready", ( v & UDCCS0_IPR ) ? yes : no ); + SAYC( "Sent Stall", ( v & UDCCS0_SST ) ? yes : no ); + SAYC( "Force Stall", ( v & UDCCS0_FST ) ? yes : no ); + SAYC( "Data End", ( v & UDCCS0_DE ) ? yes : no ); + SAYC( "Data Setup End", ( v & UDCCS0_SE ) ? yes : no ); + SAYC( "Serviced (SO)", ( v & UDCCS0_SO ) ? yes : no ); + + v = Ser0UDCCS1; + SAY( "\nUDC Receiver Status Register\n" ); + SAYV( v ); + SAYC( "Receive Packet Complete", ( v & UDCCS1_RPC ) ? yes : no ); + SAYC( "Sent Stall", ( v & UDCCS1_SST ) ? yes : no ); + SAYC( "Force Stall", ( v & UDCCS1_FST ) ? yes : no ); + SAYC( "Receive Packet Error", ( v & UDCCS1_RPE ) ? yes : no ); + SAYC( "Receive FIFO not empty", ( v & UDCCS1_RNE ) ? yes : no ); + + v = Ser0UDCCS2; + SAY( "\nUDC Transmitter Status Register\n" ); + SAYV( v ); + SAYC( "FIFO has < 8 of 16 chars", ( v & UDCCS2_TFS ) ? yes : no ); + SAYC( "Transmit Packet Complete", ( v & UDCCS2_TPC ) ? yes : no ); + SAYC( "Transmit FIFO underrun", ( v & UDCCS2_TUR ) ? yes : no ); + SAYC( "Transmit Packet Error", ( v & UDCCS2_TPE ) ? yes : no ); + SAYC( "Sent Stall", ( v & UDCCS2_SST ) ? yes : no ); + SAYC( "Force Stall", ( v & UDCCS2_FST ) ? yes : no ); +#endif + + len = ( p - page ) - off; + if ( len < 0 ) + len = 0; + *eof = ( len <=count ) ? 1 : 0; + *start = page + off; + return len; +} + +#endif /* CONFIG_PROC_FS */ + +////////////////////////////////////////////////////////////////////////////// +// Module Initialization and Shutdown +////////////////////////////////////////////////////////////////////////////// +/* + * usbctl_init() + * Module load time. Allocate dma and interrupt resources. Setup /proc fs + * entry. Leave UDC disabled. + */ +int __init usbctl_init( void ) +{ + int retval = 0; + + udc_disable(); + + memset( &usbd_info, 0, sizeof( usbd_info ) ); + +#if CONFIG_PROC_FS + create_proc_read_entry ( PROC_NODE_NAME, 0, NULL, usbctl_read_proc, NULL); +#endif + + /* setup rx dma */ + retval = sa1100_request_dma(&usbd_info.dmach_rx, "USB receive", DMA_Ser0UDCRd); + if (retval) { + printk("%sunable to register for rx dma rc=%d\n", pszMe, retval ); + goto err_rx_dma; + } + + /* setup tx dma */ + retval = sa1100_request_dma(&usbd_info.dmach_tx, "USB transmit", DMA_Ser0UDCWr); + if (retval) { + printk("%sunable to register for tx dma rc=%d\n",pszMe,retval); + goto err_tx_dma; + } + + /* now allocate the IRQ. */ + retval = request_irq(IRQ_Ser0UDC, udc_int_hndlr, SA_INTERRUPT, + "SA USB core", NULL); + if (retval) { + printk("%sCouldn't request USB irq rc=%d\n",pszMe, retval); + goto err_irq; + } + + printk( "SA1100 USB Controller Core Initialized\n"); + return 0; + +err_irq: + sa1100_free_dma(usbd_info.dmach_tx); + usbd_info.dmach_tx = 0; +err_tx_dma: + sa1100_free_dma(usbd_info.dmach_rx); + usbd_info.dmach_rx = 0; +err_rx_dma: + return retval; +} +/* + * usbctl_exit() + * Release DMA and interrupt resources + */ +void __exit usbctl_exit( void ) +{ + printk("Unloading SA1100 USB Controller\n"); + + udc_disable(); + +#if CONFIG_PROC_FS + remove_proc_entry ( PROC_NODE_NAME, NULL); +#endif + + sa1100_free_dma(usbd_info.dmach_rx); + sa1100_free_dma(usbd_info.dmach_tx); + free_irq(IRQ_Ser0UDC, NULL); +} + +EXPORT_SYMBOL( sa1100_usb_open ); +EXPORT_SYMBOL( sa1100_usb_start ); +EXPORT_SYMBOL( sa1100_usb_stop ); +EXPORT_SYMBOL( sa1100_usb_close ); + + +EXPORT_SYMBOL( sa1100_usb_get_descriptor_ptr ); +EXPORT_SYMBOL( sa1100_usb_set_string_descriptor ); +EXPORT_SYMBOL( sa1100_usb_get_string_descriptor ); +EXPORT_SYMBOL( sa1100_usb_kmalloc_string_descriptor ); + + +module_init( usbctl_init ); +module_exit( usbctl_exit ); --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/usb_ctl.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation 2001 + * + * usb_ctl.h + * + * PRIVATE interface used to share info among components of the SA-1100 USB + * core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core + * should use sa1100_usb.h. + * + */ + +#ifndef _USB_CTL_H +#define _USB_CTL_H + +#include /* dmach_t */ + + +/* + * These states correspond to those in the USB specification v1.0 + * in chapter 8, Device Framework. + */ +enum { USB_STATE_NOTATTACHED=0, USB_STATE_ATTACHED=1,USB_STATE_POWERED=2, + USB_STATE_DEFAULT=3, USB_STATE_ADDRESS=4, USB_STATE_CONFIGURED=5, + USB_STATE_SUSPENDED=6}; + +struct usb_stats_t { + unsigned long ep0_fifo_write_failures; + unsigned long ep0_bytes_written; + unsigned long ep0_fifo_read_failures; + unsigned long ep0_bytes_read; +}; + +struct usb_info_t +{ + char * client_name; + dmach_t dmach_tx, dmach_rx; + int state; + unsigned char address; + struct usb_stats_t stats; +}; + +/* in usb_ctl.c */ +extern struct usb_info_t usbd_info; + +/* + * Function Prototypes + */ +enum { kError=-1, kEvSuspend=0, kEvReset=1, + kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 }; +int usbctl_next_state_on_event( int event ); + +/* endpoint zero */ +void ep0_reset(void); +void ep0_int_hndlr(void); + +/* receiver */ +void ep1_state_change_notify( int new_state ); +int ep1_recv(void); +int ep1_init(int chn); +void ep1_int_hndlr(int status); +void ep1_reset(void); +void ep1_stall(void); + +/* xmitter */ +void ep2_state_change_notify( int new_state ); +void ep2_reset(void); +int ep2_init(int chn); +void ep2_int_hndlr(int status); +void ep2_stall(void); + +#define UDC_write(reg, val) { \ + int i = 10000; \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: write %#x to %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) != (val)); \ +} + +#define UDC_set(reg, val) { \ + int i = 10000; \ + do { \ + (reg) |= (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: set %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(!((reg) & (val))); \ +} + +#define UDC_clear(reg, val) { \ + int i = 10000; \ + do { \ + (reg) &= ~(val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) & (val)); \ +} + +#define UDC_flip(reg, val) { \ + int i = 10000; \ + (reg) = (val); \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(((reg) & (val))); \ +} + + +#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}} +#endif /* _USB_CTL_H */ --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/usb_ep0.c @@ -0,0 +1,911 @@ +/* + * Copyright (C) Extenex Corporation 2001 + * Much folklore gleaned from original code: + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * + * usb_ep0.c - SA1100 USB controller driver. + * Endpoint zero management + * + * Please see: + * linux/Documentation/arm/SA1100/SA1100_USB + * for details. (Especially since Intel docs are full of + * errors about ep0 operation.) ward.willats@extenex.com. + * + * Intel also has a "Universal Serial Bus Client Device + * Validation for the StrongARM SA-1100 Microprocessor" + * document, which has flow charts and assembler test driver, + * but be careful, since it is just for validation and not + * a "real world" solution. + * + * A summary of three types of data-returning setups: + * + * 1. Setup request <= 8 bytes. That is, requests that can + * be fullfilled in one write to the FIFO. DE is set + * with IPR in queue_and_start_write(). (I don't know + * if there really are any of these!) + * + * 2. Setup requests > 8 bytes (requiring more than one + * IN to get back to the host), and we have at least + * as much or more data than the host requested. In + * this case we pump out everything we've got, and + * when the final interrupt comes in due to the UDC + * clearing the last IPR, we just set DE. + * + * 3. Setup requests > 8 bytes, but we don't have enough + * data to satisfy the request. In this case, we send + * everything we've got, and when the final interrupt + * comes in due to the UDC clearing the last IPR + * we write nothing to the FIFO and set both IPR and DE + * so the UDC sends an empty packet and forces the host + * to perform short packet retirement instead of stalling + * out. + * + */ + +#include +#include "sa1100_usb.h" /* public interface */ +#include "usb_ctl.h" /* private stuff */ + + +// 1 == lots of trace noise, 0 = only "important' stuff +#define VERBOSITY 0 + +enum { true = 1, false = 0 }; +typedef int bool; +#ifndef MIN +#define MIN( a, b ) ((a)<(b)?(a):(b)) +#endif + +#if 1 && !defined( ASSERT ) +# define ASSERT(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n",\ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#else +# define ASSERT(expr) +#endif + +#if VERBOSITY +#define PRINTKD(fmt, args...) printk( fmt , ## args) +#else +#define PRINTKD(fmt, args...) +#endif + +/*================================================ + * USB Protocol Stuff + */ + +/* Request Codes */ +enum { GET_STATUS=0, CLEAR_FEATURE=1, SET_FEATURE=3, + SET_ADDRESS=5, GET_DESCRIPTOR=6, SET_DESCRIPTOR=7, + GET_CONFIGURATION=8, SET_CONFIGURATION=9, GET_INTERFACE=10, + SET_INTERFACE=11 }; + + +/* USB Device Requests */ +typedef struct +{ + __u8 bmRequestType; + __u8 bRequest; + __u16 wValue; + __u16 wIndex; + __u16 wLength; +} usb_dev_request_t __attribute__ ((packed)); + +/*************************************************************************** +Prototypes +***************************************************************************/ +/* "setup handlers" -- the main functions dispatched to by the + .. isr. These represent the major "modes" of endpoint 0 operaton */ +static void sh_setup_begin(void); /* setup begin (idle) */ +static void sh_write( void ); /* writing data */ +static void sh_write_with_empty_packet( void ); /* empty packet at end of xfer*/ +/* called before both sh_write routines above */ +static void common_write_preamble( void ); + +/* other subroutines */ +static __u32 queue_and_start_write( void * p, int req, int act ); +static void write_fifo( void ); +static int read_fifo( usb_dev_request_t * p ); +static void get_descriptor( usb_dev_request_t * pReq ); + +/* some voodo helpers 01Mar01ww */ +static void set_cs_bits( __u32 set_bits ); +static void set_de( void ); +static void set_ipr( void ); +static void set_ipr_and_de( void ); +static bool clear_opr( void ); + +/*************************************************************************** +Inline Helpers +***************************************************************************/ + +/* Data extraction from usb_request_t fields */ +enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 }; +static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); } + +static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); } +inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); } + +/* following is hook for self-powered flag in GET_STATUS. Some devices + .. might like to override and return real info */ +static inline bool self_powered_hook( void ) { return true; } + +/* print string descriptor */ +static inline void psdesc( string_desc_t * p ) +{ + int i; + int nchars = ( p->bLength - 2 ) / sizeof( __u16 ); + printk( "'" ); + for( i = 0 ; i < nchars ; i++ ) { + printk( "%c", (char) p->bString[i] ); + } + printk( "'\n" ); +} + + +#if VERBOSITY +/* "pcs" == "print control status" */ +static inline void pcs( void ) +{ + __u32 foo = Ser0UDCCS0; + printk( "%8.8X: %s %s %s %s\n", + foo, + foo & UDCCS0_SE ? "SE" : "", + foo & UDCCS0_OPR ? "OPR" : "", + foo & UDCCS0_IPR ? "IPR" : "", + foo & UDCCS0_SST ? "SST" : "" + ); +} +static inline void preq( usb_dev_request_t * pReq ) +{ + static char * tnames[] = { "dev", "intf", "ep", "oth" }; + static char * rnames[] = { "std", "class", "vendor", "???" }; + char * psz; + switch( pReq->bRequest ) { + case GET_STATUS: psz = "get stat"; break; + case CLEAR_FEATURE: psz = "clr feat"; break; + case SET_FEATURE: psz = "set feat"; break; + case SET_ADDRESS: psz = "set addr"; break; + case GET_DESCRIPTOR: psz = "get desc"; break; + case SET_DESCRIPTOR: psz = "set desc"; break; + case GET_CONFIGURATION: psz = "get cfg"; break; + case SET_CONFIGURATION: psz = "set cfg"; break; + case GET_INTERFACE: psz = "get intf"; break; + case SET_INTERFACE: psz = "set intf"; break; + default: psz = "unknown"; break; + } + printk( "- [%s: %s req to %s. dir=%s]\n", psz, + rnames[ (pReq->bmRequestType >> 5) & 3 ], + tnames[ pReq->bmRequestType & 3 ], + ( pReq->bmRequestType & 0x80 ) ? "in" : "out" ); +} + +#else +static inline void pcs( void ){} +static inline void preq( void ){} +#endif + +/*************************************************************************** +Globals +***************************************************************************/ +static const char pszMe[] = "usbep0: "; + +/* pointer to current setup handler */ +static void (*current_handler)(void) = sh_setup_begin; + +/* global write struct to keep write + ..state around across interrupts */ +static struct { + unsigned char *p; + int bytes_left; +} wr; + +/*************************************************************************** +Public Interface +***************************************************************************/ + +/* reset received from HUB (or controller just went nuts and reset by itself!) + so udc core has been reset, track this state here */ +void +ep0_reset(void) +{ + /* reset state machine */ + current_handler = sh_setup_begin; + wr.p = NULL; + wr.bytes_left = 0; + usbd_info.address=0; +} + +/* handle interrupt for endpoint zero */ +void +ep0_int_hndlr( void ) +{ + PRINTKD( "/\\(%d)\n", Ser0UDCAR ); + pcs(); + + /* if not in setup begin, we are returning data. + execute a common preamble to both write handlers + */ + if ( current_handler != sh_setup_begin ) + common_write_preamble(); + + (*current_handler)(); + + PRINTKD( "---\n" ); + pcs(); + PRINTKD( "\\/\n" ); +} + +/*************************************************************************** +Setup Handlers +***************************************************************************/ +/* + * sh_setup_begin() + * This setup handler is the "idle" state of endpoint zero. It looks for OPR + * (OUT packet ready) to see if a setup request has been been received from the + * host. Requests without a return data phase are immediately handled. Otherwise, + * in the case of GET_XXXX the handler may be set to one of the sh_write_xxxx + * data pumpers if more than 8 bytes need to get back to the host. + * + */ +static void +sh_setup_begin( void ) +{ + unsigned char status_buf[2]; /* returned in GET_STATUS */ + usb_dev_request_t req; + int request_type; + int n; + __u32 cs_bits; + __u32 address; + __u32 cs_reg_in = Ser0UDCCS0; + + if (cs_reg_in & UDCCS0_SST) { + PRINTKD( "%ssetup begin: sent stall. Continuing\n", pszMe ); + set_cs_bits( UDCCS0_SST ); + } + + if ( cs_reg_in & UDCCS0_SE ) { + PRINTKD( "%ssetup begin: Early term of setup. Continuing\n", pszMe ); + set_cs_bits( UDCCS0_SSE ); /* clear setup end */ + } + + /* Be sure out packet ready, otherwise something is wrong */ + if ( (cs_reg_in & UDCCS0_OPR) == 0 ) { + /* we can get here early...if so, we'll int again in a moment */ + PRINTKD( "%ssetup begin: no OUT packet available. Exiting\n", pszMe ); + goto sh_sb_end; + } + + /* read the setup request */ + n = read_fifo( &req ); + if ( n != sizeof( req ) ) { + printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. " + " Stalling out...\n", + pszMe, sizeof( req ), n ); + /* force stall, serviced out */ + set_cs_bits( UDCCS0_FST | UDCCS0_SO ); + goto sh_sb_end; + } + + /* Is it a standard request? (not vendor or class request) */ + request_type = type_code_from_request( req.bmRequestType ); + if ( request_type != 0 ) { + printk( "%ssetup begin: unsupported bmRequestType: %d ignored\n", + pszMe, request_type ); + set_cs_bits( UDCCS0_DE | UDCCS0_SO ); + goto sh_sb_end; + } + +#if VERBOSITY + { + unsigned char * pdb = (unsigned char *) &req; + PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ", + pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7] + ); + preq( &req ); + } +#endif + + /* Handle it */ + switch( req.bRequest ) { + + /* This first bunch have no data phase */ + + case SET_ADDRESS: + address = (__u32) (req.wValue & 0x7F); + /* when SO and DE sent, UDC will enter status phase and ack, + ..propagating new address to udc core. Next control transfer + ..will be on the new address. You can't see the change in a + ..read back of CAR until then. (about 250us later, on my box). + ..The original Intel driver sets S0 and DE and code to check + ..that address has propagated here. I tried this, but it + ..would only work sometimes! The rest of the time it would + ..never propagate and we'd spin forever. So now I just set + ..it and pray... + */ + Ser0UDCAR = address; + usbd_info.address = address; + usbctl_next_state_on_event( kEvAddress ); + set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ + printk( "%sI have been assigned address: %d\n", pszMe, address ); + break; + + + case SET_CONFIGURATION: + if ( req.wValue == 1 ) { + /* configured */ + if (usbctl_next_state_on_event( kEvConfig ) != kError){ + /* (re)set the out and in max packet sizes */ + desc_t * pDesc = sa1100_usb_get_descriptor_ptr(); + __u32 out = __le16_to_cpu( pDesc->b.ep1.wMaxPacketSize ); + __u32 in = __le16_to_cpu( pDesc->b.ep2.wMaxPacketSize ); + Ser0UDCOMP = ( out - 1 ); + Ser0UDCIMP = ( in - 1 ); + printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in ); + } + } else if ( req.wValue == 0 ) { + /* de-configured */ + if (usbctl_next_state_on_event( kEvDeConfig ) != kError ) + printk( "%sDe-Configured\n", pszMe ); + } else { + printk( "%ssetup phase: Unknown " + "\"set configuration\" data %d\n", + pszMe, req.wValue ); + } + set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ + break; + + case CLEAR_FEATURE: + /* could check data length, direction...26Jan01ww */ + if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */ + int ep = windex_to_ep_num( req.wIndex ); + if ( ep == 1 ) { + printk( "%sclear feature \"endpoint halt\" " + " on receiver\n", pszMe ); + ep1_reset(); + } + else if ( ep == 2 ) { + printk( "%sclear feature \"endpoint halt\" " + "on xmitter\n", pszMe ); + ep2_reset(); + } else { + printk( "%sclear feature \"endpoint halt\" " + "on unsupported ep # %d\n", + pszMe, ep ); + } + } else { + printk( "%sUnsupported feature selector (%d) " + "in clear feature. Ignored.\n" , + pszMe, req.wValue ); + } + set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ + break; + + case SET_FEATURE: + if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */ + int ep = windex_to_ep_num( req.wValue ); + if ( ep == 1 ) { + printk( "%set feature \"endpoint halt\" " + "on receiver\n", pszMe ); + ep1_stall(); + } + else if ( ep == 2 ) { + printk( "%sset feature \"endpoint halt\" " + " on xmitter\n", pszMe ); + ep2_stall(); + } else { + printk( "%sset feature \"endpoint halt\" " + "on unsupported ep # %d\n", + pszMe, ep ); + } + } + else { + printk( "%sUnsupported feature selector " + "(%d) in set feature\n", + pszMe, req.wValue ); + } + set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ + break; + + + /* The rest have a data phase that writes back to the host */ + case GET_STATUS: + /* return status bit flags */ + status_buf[0] = status_buf[1] = 0; + n = request_target(req.bmRequestType); + switch( n ) { + case kTargetDevice: + if ( self_powered_hook() ) + status_buf[0] |= 1; + break; + case kTargetInterface: + break; + case kTargetEndpoint: + /* return stalled bit */ + n = windex_to_ep_num( req.wIndex ); + if ( n == 1 ) + status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4; + else if ( n == 2 ) + status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5; + else { + printk( "%sUnknown endpoint (%d) " + "in GET_STATUS\n", pszMe, n ); + } + break; + default: + printk( "%sUnknown target (%d) in GET_STATUS\n", + pszMe, n ); + /* fall thru */ + break; + } + cs_bits = queue_and_start_write( status_buf, + req.wLength, + sizeof( status_buf ) ); + set_cs_bits( cs_bits ); + break; + case GET_DESCRIPTOR: + get_descriptor( &req ); + break; + + case GET_CONFIGURATION: + status_buf[0] = (usbd_info.state == USB_STATE_CONFIGURED) + ? 1 + : 0; + cs_bits = queue_and_start_write( status_buf, req.wLength, 1 ); + set_cs_bits( cs_bits ); + break; + case GET_INTERFACE: + printk( "%sfixme: get interface not supported\n", pszMe ); + cs_bits = queue_and_start_write( NULL, req.wLength, 0 ); + set_cs_bits( cs_bits ); + break; + case SET_INTERFACE: + printk( "%sfixme: set interface not supported\n", pszMe ); + set_cs_bits( UDCCS0_DE | UDCCS0_SO ); + break; + default : + printk("%sunknown request 0x%x\n", pszMe, req.bRequest); + break; + } /* switch( bRequest ) */ + +sh_sb_end: + return; + +} +/* + * common_wrtie_preamble() + * Called before execution of sh_write() or sh_write_with_empty_packet() + * Handles common abort conditions. + * + */ +static void common_write_preamble( void ) +{ + /* If "setup end" has been set, the usb controller has + ..terminated a setup transaction before we set DE. This + ..happens during enumeration with some hosts. For example, + ..the host will ask for our device descriptor and specify + ..a return of 64 bytes. When we hand back the first 8, the + ..host will know our max packet size and turn around and + ..issue a new setup immediately. This causes the UDC to auto-ack + ..the new setup and set SE. We must then "unload" (process) + ..the new setup, which is what will happen after this preamble + ..is finished executing. + */ + __u32 cs_reg_in = Ser0UDCCS0; + + if ( cs_reg_in & UDCCS0_SE ) { + PRINTKD( "%swrite_preamble(): Early termination of setup\n", pszMe ); + Ser0UDCCS0 = UDCCS0_SSE; /* clear setup end */ + current_handler = sh_setup_begin; + } + + if ( cs_reg_in & UDCCS0_SST ) { + PRINTKD( "%swrite_preamble(): UDC sent stall\n", pszMe ); + Ser0UDCCS0 = UDCCS0_SST; /* clear setup end */ + current_handler = sh_setup_begin; + } + + if ( cs_reg_in & UDCCS0_OPR ) { + PRINTKD( "%swrite_preamble(): see OPR. Stopping write to " + "handle new SETUP\n", pszMe ); + /* very rarely, you can get OPR and leftover IPR. Try to clear */ + UDC_clear( Ser0UDCCS0, UDCCS0_IPR ); + current_handler = sh_setup_begin; + } +} + +/* + * sh_write() + * This is the setup handler when we are in the data return phase of + * a setup request and have as much (or more) data than the host + * requested. If we enter this routine and bytes left is zero, the + * last data packet has gone (int is because IPR was just cleared) + * so we just set DE and reset. Otheriwse, we write another packet + * and set IPR. + */ +static void sh_write() +{ + PRINTKD( "W\n" ); + + if ( Ser0UDCCS0 & UDCCS0_IPR ) { + PRINTKD( "%ssh_write(): IPR set, exiting\n", pszMe ); + return; + } + + /* If bytes left is zero, we are coming in on the + ..interrupt after the last packet went out. And + ..we know we don't have to empty packet this transfer + ..so just set DE and we are done */ + + if ( 0 == wr.bytes_left ) { + /* that's it, so data end */ + set_de(); + wr.p = NULL; /* be anal */ + current_handler = sh_setup_begin; + } else { + /* Otherwise, more data to go */ + write_fifo(); + set_ipr(); + } +} +/* + * sh_write_with_empty_packet() + * This is the setup handler when we don't have enough data to + * satisfy the host's request. After we send everything we've got + * we must send an empty packet (by setting IPR and DE) so the + * host can perform "short packet retirement" and not stall. + * + */ +static void sh_write_with_empty_packet( void ) +{ + __u32 cs_reg_out = 0; + PRINTKD( "WE\n" ); + + if ( Ser0UDCCS0 & UDCCS0_IPR ) { + PRINTKD( "%ssh_write(): IPR set, exiting\n", pszMe ); + return; + } + + /* If bytes left is zero, we are coming in on the + ..interrupt after the last packet went out. + ..we must do short packet suff, so set DE and IPR + */ + + if ( 0 == wr.bytes_left ) { + set_ipr_and_de(); + wr.p = NULL; + current_handler = sh_setup_begin; + PRINTKD( "%ssh_write empty() Sent empty packet \n", pszMe ); + } else { + write_fifo(); /* send data */ + set_ipr(); /* flag a packet is ready */ + } + Ser0UDCCS0 = cs_reg_out; +} + +/*************************************************************************** +Other Private Subroutines +***************************************************************************/ +/* + * queue_and_start_write() + * p == data to send + * req == bytes host requested + * act == bytes we actually have + * Returns: bits to be flipped in ep0 control/status register + * + * Called from sh_setup_begin() to begin a data return phase. Sets up the + * global "wr"-ite structure and load the outbound FIFO with data. + * If can't send all the data, set appropriate handler for next interrupt. + * + */ +static __u32 queue_and_start_write( void * in, int req, int act ) +{ + __u32 cs_reg_bits = UDCCS0_IPR; + unsigned char * p = (unsigned char*) in; + + PRINTKD( "Qr=%d a=%d\n",req,act ); + + /* thou shalt not enter data phase until the serviced OUT is clear */ + if ( ! clear_opr() ) { + printk( "%sSO did not clear OPR\n", pszMe ); + return ( UDCCS0_DE | UDCCS0_SO ) ; + } + wr.p = p; + wr.bytes_left = MIN( act, req ); + + write_fifo(); + + if ( 0 == wr.bytes_left ) { + cs_reg_bits |= UDCCS0_DE; /* out in 1 so data end */ + wr.p = NULL; /* be anal */ + } + else if ( act < req ) /* we are going to short-change host */ + current_handler = sh_write_with_empty_packet; /* so need nul to not stall */ + else /* we have as much or more than requested */ + current_handler = sh_write; + + return cs_reg_bits; /* note: IPR was set uncondtionally at start of routine */ +} +/* + * write_fifo() + * Stick bytes in the 8 bytes endpoint zero FIFO. + * This version uses a variety of tricks to make sure the bytes + * are written correctly. 1. The count register is checked to + * see if the byte went in, and the write is attempted again + * if not. 2. An overall counter is used to break out so we + * don't hang in those (rare) cases where the UDC reverses + * direction of the FIFO underneath us without notification + * (in response to host aborting a setup transaction early). + * + */ +static void write_fifo( void ) +{ + int bytes_this_time = MIN( wr.bytes_left, 8 ); + int bytes_written = 0; + int i=0; + + PRINTKD( "WF=%d: ", bytes_this_time ); + + while( bytes_this_time-- ) { + PRINTKD( "%2.2X ", *wr.p ); + i = 0; + do { + Ser0UDCD0 = *wr.p; + udelay( 20 ); /* voodo 28Feb01ww */ + i++; + } while( Ser0UDCWC == bytes_written && i < 10 ); + if ( i == 50 ) { + printk( "%swrite_fifo: write failure\n", pszMe ); + usbd_info.stats.ep0_fifo_write_failures++; + } + + wr.p++; + bytes_written++; + } + wr.bytes_left -= bytes_written; + + /* following propagation voodo so maybe caller writing IPR in + ..a moment might actually get it to stick 28Feb01ww */ + udelay( 300 ); + + usbd_info.stats.ep0_bytes_written += bytes_written; + PRINTKD( "L=%d WCR=%8.8X\n", wr.bytes_left, Ser0UDCWC ); +} +/* + * read_fifo() + * Read 1-8 bytes out of FIFO and put in request. + * Called to do the initial read of setup requests + * from the host. Return number of bytes read. + * + * Like write fifo above, this driver uses multiple + * reads checked agains the count register with an + * overall timeout. + * + */ +static int +read_fifo( usb_dev_request_t * request ) +{ + int bytes_read = 0; + int fifo_count; + int i; + + unsigned char * pOut = (unsigned char*) request; + + fifo_count = ( Ser0UDCWC & 0xFF ); + + ASSERT( fifo_count <= 8 ); + PRINTKD( "RF=%d ", fifo_count ); + + while( fifo_count-- ) { + i = 0; + do { + *pOut = (unsigned char) Ser0UDCD0; + udelay( 10 ); + } while( ( Ser0UDCWC & 0xFF ) != fifo_count && i < 10 ); + if ( i == 10 ) { + printk( "%sread_fifo(): read failure\n", pszMe ); + usbd_info.stats.ep0_fifo_read_failures++; + } + pOut++; + bytes_read++; + } + + PRINTKD( "fc=%d\n", bytes_read ); + usbd_info.stats.ep0_bytes_read++; + return bytes_read; +} + +/* + * get_descriptor() + * Called from sh_setup_begin to handle data return + * for a GET_DESCRIPTOR setup request. + */ +static void get_descriptor( usb_dev_request_t * pReq ) +{ + __u32 cs_bits = 0; + string_desc_t * pString; + ep_desc_t * pEndpoint; + + desc_t * pDesc = sa1100_usb_get_descriptor_ptr(); + int type = pReq->wValue >> 8; + int idx = pReq->wValue & 0xFF; + + switch( type ) { + case USB_DESC_DEVICE: + cs_bits = + queue_and_start_write( &pDesc->dev, + pReq->wLength, + pDesc->dev.bLength ); + break; + + // return config descriptor buffer, cfg, intf, 2 ep + case USB_DESC_CONFIG: + cs_bits = + queue_and_start_write( &pDesc->b, + pReq->wLength, + sizeof( struct cdb ) ); + break; + + // not quite right, since doesn't do language code checking + case USB_DESC_STRING: + pString = sa1100_usb_get_string_descriptor( idx ); + if ( pString ) { + if ( idx != 0 ) { // if not language index + printk( "%sReturn string %d: ", pszMe, idx ); + psdesc( pString ); + } + cs_bits = + queue_and_start_write( pString, + pReq->wLength, + pString->bLength ); + } + else { + printk("%sunkown string index %d Stall.\n", pszMe, idx ); + cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); + } + break; + + case USB_DESC_INTERFACE: + if ( idx == pDesc->b.intf.bInterfaceNumber ) { + cs_bits = + queue_and_start_write( &pDesc->b.intf, + pReq->wLength, + pDesc->b.intf.bLength ); + } + break; + + case USB_DESC_ENDPOINT: /* correct? 21Feb01ww */ + if ( idx == 1 ) + pEndpoint = &pDesc->b.ep1; + else if ( idx == 2 ) + pEndpoint = &pDesc->b.ep2; + else + pEndpoint = NULL; + if ( pEndpoint ) { + cs_bits = + queue_and_start_write( pEndpoint, + pReq->wLength, + pEndpoint->bLength ); + } else { + printk("%sunkown endpoint index %d Stall.\n", pszMe, idx ); + cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); + } + break; + + + default : + printk("%sunknown descriptor type %d. Stall.\n", pszMe, type ); + cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); + break; + + } + set_cs_bits( cs_bits ); +} + + +/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */ + +#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE ) +#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS )) +#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE) + +static void set_cs_bits( __u32 bits ) +{ + if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST ) ) + Ser0UDCCS0 = bits; + else if ( (bits & BOTH_BITS) == BOTH_BITS ) + set_ipr_and_de(); + else if ( bits & UDCCS0_IPR ) + set_ipr(); + else if ( bits & UDCCS0_DE ) + set_de(); +} + +static void set_de( void ) +{ + int i = 1; + while( 1 ) { + if ( OK_TO_WRITE ) { + Ser0UDCCS0 |= UDCCS0_DE; + } else { + PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe ); + break; + } + if ( Ser0UDCCS0 & UDCCS0_DE ) + break; + udelay( i ); + if ( ++i == 50 ) { + printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n", + pszMe, UDCCS0_DE, Ser0UDCCS0 ); + break; + } + } +} + +static void set_ipr( void ) +{ + int i = 1; + while( 1 ) { + if ( OK_TO_WRITE ) { + Ser0UDCCS0 |= UDCCS0_IPR; + } else { + PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe ); + break; + } + if ( Ser0UDCCS0 & UDCCS0_IPR ) + break; + udelay( i ); + if ( ++i == 50 ) { + printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n", + pszMe, UDCCS0_IPR, Ser0UDCCS0 ); + break; + } + } +} + + + +static void set_ipr_and_de( void ) +{ + int i = 1; + while( 1 ) { + if ( OK_TO_WRITE ) { + Ser0UDCCS0 |= BOTH_BITS; + } else { + PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe ); + break; + } + if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS) + break; + udelay( i ); + if ( ++i == 50 ) { + printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n", + pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 ); + break; + } + } +} + +static bool clear_opr( void ) +{ + int i = 10000; + bool is_clear; + do { + Ser0UDCCS0 = UDCCS0_SO; + is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR ); + if ( i-- <= 0 ) { + printk( "%sclear_opr(): failed\n", pszMe ); + break; + } + } while( ! is_clear ); + return is_clear; +} + + + + + +/* end usb_ep0.c */ + --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/usb_recv.c @@ -0,0 +1,205 @@ +/* + * Generic receive layer for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original version which was + * Copyright (c) Compaq Computer Corporation, 1998-1999 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is still work in progress... + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + */ + +#include +#include +#include +#include +#include + +#include "sa1100_usb.h" +#include "usb_ctl.h" + + +static char *ep1_buf; +static int ep1_len; +static usb_callback_t ep1_callback; +static char *ep1_curdmabuf; +static dma_addr_t ep1_curdmapos; +static int ep1_curdmalen; +static int ep1_remain; +static int dmachn_rx; +static int rx_pktsize; + +static int naking; + +static void +ep1_start(void) +{ + sa1100_dma_flush_all(dmachn_rx); + if (!ep1_curdmalen) { + ep1_curdmalen = rx_pktsize; + if (ep1_curdmalen > ep1_remain) + ep1_curdmalen = ep1_remain; + ep1_curdmapos = pci_map_single(NULL, ep1_curdmabuf, ep1_curdmalen, + PCI_DMA_FROMDEVICE); + } + sa1100_dma_queue_buffer(dmachn_rx, NULL, ep1_curdmapos, ep1_curdmalen); + if ( naking ) { + /* turn off NAK of OUT packets, if set */ + UDC_flip( Ser0UDCCS1, UDCCS1_RPC ); + naking = 0; + } +} + +static void +ep1_done(int flag) +{ + int size = ep1_len - ep1_remain; + + if (!ep1_len) + return; + if (ep1_curdmalen) + pci_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, + PCI_DMA_FROMDEVICE); + ep1_len = ep1_curdmalen = 0; + if (ep1_callback) { + ep1_callback(flag, size); + } +} + +void +ep1_state_change_notify( int new_state ) +{ + +} + +void +ep1_stall( void ) +{ + /* SET_FEATURE force stall at UDC */ + UDC_set( Ser0UDCCS1, UDCCS1_FST ); +} + +int +ep1_init(int chn) +{ + desc_t * pd = sa1100_usb_get_descriptor_ptr(); + rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize ); + dmachn_rx = chn; + sa1100_dma_flush_all(dmachn_rx); + ep1_done(-EAGAIN); + return 0; +} + +void +ep1_reset(void) +{ + desc_t * pd = sa1100_usb_get_descriptor_ptr(); + rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize ); + sa1100_dma_flush_all(dmachn_rx); + UDC_clear(Ser0UDCCS1, UDCCS1_FST); + ep1_done(-EINTR); +} + +void +ep1_int_hndlr(int udcsr) +{ + dma_addr_t dma_addr; + unsigned int len; + int status = Ser0UDCCS1; + + if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking ); + + if (status & UDCCS1_RPC) { + + if (!ep1_curdmalen) { + printk("usb_recv: RPC for non-existent buffer\n"); + naking=1; + return; + } + + sa1100_dma_stop(dmachn_rx); + + if (status & UDCCS1_SST) { + printk("usb_recv: stall sent OMP=%d\n",Ser0UDCOMP); + UDC_flip(Ser0UDCCS1, UDCCS1_SST); + ep1_done(-EIO); // UDC aborted current transfer, so we do + return; + } + + if (status & UDCCS1_RPE) { + printk("usb_recv: RPError %x\n", status); + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + ep1_done(-EIO); + return; + } + + sa1100_dma_get_current(dmachn_rx, NULL, &dma_addr); + pci_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, + PCI_DMA_FROMDEVICE); + len = dma_addr - ep1_curdmapos; + if (len < ep1_curdmalen) { + char *buf = ep1_curdmabuf + len; + while (Ser0UDCCS1 & UDCCS1_RNE) { + if (len >= ep1_curdmalen) { + printk("usb_recv: too much data in fifo\n"); + break; + } + *buf++ = Ser0UDCDR; + len++; + } + } else if (Ser0UDCCS1 & UDCCS1_RNE) { + printk("usb_recv: fifo screwed, shouldn't contain data\n"); + len = 0; + } + ep1_curdmalen = 0; /* dma unmap already done */ + ep1_remain -= len; + naking = 1; + ep1_done((len) ? 0 : -EPIPE); + } + /* else, you can get here if we are holding NAK */ +} + +int +sa1100_usb_recv(char *buf, int len, usb_callback_t callback) +{ + int flags; + + if (ep1_len) + return -EBUSY; + + local_irq_save(flags); + ep1_buf = buf; + ep1_len = len; + ep1_callback = callback; + ep1_remain = len; + ep1_curdmabuf = buf; + ep1_curdmalen = 0; + ep1_start(); + local_irq_restore(flags); + + return 0; +} + +EXPORT_SYMBOL(sa1100_usb_recv); + +void +sa1100_usb_recv_reset(void) +{ + ep1_reset(); +} + +EXPORT_SYMBOL(sa1100_usb_recv_reset); + +void +sa1100_usb_recv_stall(void) +{ + ep1_stall(); +} + +EXPORT_SYMBOL(sa1100_usb_recv_stall); + --- /dev/null +++ linux-2.4.27/arch/arm/mach-sa1100/usb_send.c @@ -0,0 +1,205 @@ +/* + * Generic xmit layer for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original version which was + * Copyright (c) Compaq Computer Corporation, 1998-1999 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is still work in progress... + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * 15/03/2001 - ep2_start now sets UDCAR to overcome something that is hardware + * bug, I think. green@iXcelerator.com + */ + +#include +#include +#include +#include // for the massive_attack hack 28Feb01ww +#include +#include +#include +#include + +#include "sa1100_usb.h" +#include "usb_ctl.h" + + +static char *ep2_buf; +static int ep2_len; +static usb_callback_t ep2_callback; +static dma_addr_t ep2_dma; +static dma_addr_t ep2_curdmapos; +static int ep2_curdmalen; +static int ep2_remain; +static int dmachn_tx; +static int tx_pktsize; + +/* device state is changing, async */ +void +ep2_state_change_notify( int new_state ) +{ +} + +/* set feature stall executing, async */ +void +ep2_stall( void ) +{ + UDC_set( Ser0UDCCS2, UDCCS2_FST ); /* force stall at UDC */ +} + +static void +ep2_start(void) +{ + if (!ep2_len) + return; + + ep2_curdmalen = tx_pktsize; + if (ep2_curdmalen > ep2_remain) + ep2_curdmalen = ep2_remain; + + /* must do this _before_ queue buffer.. */ + UDC_flip( Ser0UDCCS2,UDCCS2_TPC ); /* stop NAKing IN tokens */ + UDC_write( Ser0UDCIMP, ep2_curdmalen-1 ); + + /* Remove if never seen...8Mar01ww */ + { + int massive_attack = 20; + while ( Ser0UDCIMP != ep2_curdmalen-1 && massive_attack-- ) { + printk( "usbsnd: Oh no you don't! Let me spin..." ); + udelay( 500 ); + printk( "and try again...\n" ); + UDC_write( Ser0UDCIMP, ep2_curdmalen-1 ); + } + if ( massive_attack != 20 ) { + if ( Ser0UDCIMP != ep2_curdmalen-1 ) + printk( "usbsnd: Massive attack FAILED :-( %d\n", + 20 - massive_attack ); + else + printk( "usbsnd: Massive attack WORKED :-) %d\n", + 20 - massive_attack ); + } + } + /* End remove if never seen... 8Mar01ww */ + + Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug + sa1100_dma_queue_buffer(dmachn_tx, NULL, ep2_curdmapos, ep2_curdmalen); +} + +static void +ep2_done(int flag) +{ + int size = ep2_len - ep2_remain; + if (ep2_len) { + pci_unmap_single(NULL, ep2_dma, ep2_len, PCI_DMA_TODEVICE); + ep2_len = 0; + if (ep2_callback) + ep2_callback(flag, size); + } +} + +int +ep2_init(int chn) +{ + desc_t * pd = sa1100_usb_get_descriptor_ptr(); + tx_pktsize = __le16_to_cpu( pd->b.ep2.wMaxPacketSize ); + dmachn_tx = chn; + sa1100_dma_flush_all(dmachn_tx); + ep2_done(-EAGAIN); + return 0; +} + +void +ep2_reset(void) +{ + desc_t * pd = sa1100_usb_get_descriptor_ptr(); + tx_pktsize = __le16_to_cpu( pd->b.ep2.wMaxPacketSize ); + UDC_clear(Ser0UDCCS2, UDCCS2_FST); + sa1100_dma_flush_all(dmachn_tx); + ep2_done(-EINTR); +} + +void +ep2_int_hndlr(int udcsr) +{ + int status = Ser0UDCCS2; + + if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug. + Ser0UDCAR = usbd_info.address; + + UDC_flip(Ser0UDCCS2, UDCCS2_SST); + + if (status & UDCCS2_TPC) { + sa1100_dma_flush_all(dmachn_tx); + + if (status & (UDCCS2_TPE | UDCCS2_TUR)) { + printk("usb_send: transmit error %x\n", status); + ep2_done(-EIO); + } else { +#if 1 // 22Feb01ww/Oleg + ep2_curdmapos += ep2_curdmalen; + ep2_remain -= ep2_curdmalen; +#else + ep2_curdmapos += Ser0UDCIMP + 1; // this is workaround + ep2_remain -= Ser0UDCIMP + 1; // for case when setting of Ser0UDCIMP was failed +#endif + + if (ep2_remain != 0) { + ep2_start(); + } else { + ep2_done(0); + } + } + } else { + printk("usb_send: Not TPC: UDCCS2 = %x\n", status); + } +} + +int +sa1100_usb_send(char *buf, int len, usb_callback_t callback) +{ + int flags; + + if (usbd_info.state != USB_STATE_CONFIGURED) + return -ENODEV; + + if (ep2_len) + return -EBUSY; + + local_irq_save(flags); + ep2_buf = buf; + ep2_len = len; + ep2_dma = pci_map_single(NULL, buf, len, PCI_DMA_TODEVICE); + ep2_callback = callback; + ep2_remain = len; + ep2_curdmapos = ep2_dma; + ep2_start(); + local_irq_restore(flags); + + return 0; +} + + +void +sa1100_usb_send_reset(void) +{ + ep2_reset(); +} + +int sa1100_usb_xmitter_avail( void ) +{ + if (usbd_info.state != USB_STATE_CONFIGURED) + return -ENODEV; + if (ep2_len) + return -EBUSY; + return 0; +} + + +EXPORT_SYMBOL(sa1100_usb_xmitter_avail); +EXPORT_SYMBOL(sa1100_usb_send); +EXPORT_SYMBOL(sa1100_usb_send_reset); --- linux-2.4.27/arch/arm/mm/Makefile~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/Makefile @@ -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 --- linux-2.4.27/arch/arm/mm/alignment.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/alignment.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -19,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -30,13 +28,11 @@ #include #include -extern void -do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, - int error_code, struct pt_regs *regs); +#include "fault.h" /* * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 - * /proc/sys/debug/alignment, modified and integrated into + * /proc/cpu/alignment, modified and integrated into * Linux 2.1 by Russell King * * Speed optimisations and better fault handling by Russell King. @@ -130,31 +126,6 @@ return count; } -/* - * This needs to be done after sysctl_init, otherwise sys/ will be - * overwritten. Actually, this shouldn't be in sys/ at all since - * it isn't a sysctl, and it doesn't contain sysctl information. - * We now locate it in /proc/cpu/alignment instead. - */ -static int __init alignment_init(void) -{ - struct proc_dir_entry *res; - - res = proc_mkdir("cpu", NULL); - if (!res) - return -ENOMEM; - - res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res); - if (!res) - return -ENOMEM; - - res->read_proc = proc_alignment_read; - res->write_proc = proc_alignment_write; - - return 0; -} - -__initcall(alignment_init); #endif /* CONFIG_PROC_FS */ union offset_union { @@ -429,7 +400,7 @@ * For alignment faults on the ARM922T/ARM920T the MMU makes * the FSR (and hence addr) equal to the updated base address * of the multiple access rather than the restored value. - * Switch this messsage off if we've got a ARM92[02], otherwise + * Switch this message off if we've got a ARM92[02], otherwise * [ls]dm alignment faults are noisy! */ #if !(defined CONFIG_CPU_ARM922T) && !(defined CONFIG_CPU_ARM920T) @@ -486,7 +457,8 @@ return TYPE_ERROR; } -int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) +static int +do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { union offset_union offset; unsigned long instr, instrptr; @@ -541,7 +513,7 @@ case SHIFT_RORRRX: if (shiftval == 0) { offset.un >>= 1; - if (regs->ARM_cpsr & CC_C_BIT) + if (regs->ARM_cpsr & PSR_C_BIT) offset.un |= 1 << 31; } else offset.un = offset.un >> shiftval | @@ -577,7 +549,7 @@ /* * We got a fault - fix it up, or die. */ - do_bad_area(current, current->mm, addr, error_code, regs); + do_bad_area(current, current->mm, addr, fsr, regs); return 0; bad: @@ -594,8 +566,8 @@ if (ai_usermode & 1) printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%08lx " - "Address=0x%08lx Code 0x%02x\n", current->comm, - current->pid, instrptr, instr, addr, error_code); + "Address=0x%08lx FSR 0x%03x\n", current->comm, + current->pid, instrptr, instr, addr, fsr); if (ai_usermode & 2) goto fixup; @@ -607,3 +579,34 @@ return 0; } + +/* + * This needs to be done after sysctl_init, otherwise sys/ will be + * overwritten. Actually, this shouldn't be in sys/ at all since + * it isn't a sysctl, and it doesn't contain sysctl information. + * We now locate it in /proc/cpu/alignment instead. + */ +static int __init alignment_init(void) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; + + res = proc_mkdir("cpu", NULL); + if (!res) + return -ENOMEM; + + res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res); + if (!res) + return -ENOMEM; + + res->read_proc = proc_alignment_read; + res->write_proc = proc_alignment_write; +#endif + + hook_fault_code(1, do_alignment, SIGILL, "alignment exception"); + hook_fault_code(3, do_alignment, SIGILL, "alignment exception"); + + return 0; +} + +__initcall(alignment_init); --- linux-2.4.27/arch/arm/mm/fault-armv.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/fault-armv.c @@ -2,116 +2,90 @@ * linux/arch/arm/mm/fault-armv.c * * Copyright (C) 1995 Linus Torvalds - * Modifications for ARM processor (c) 1995-2001 Russell King + * Modifications for ARM processor (c) 1995-2003 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include -#include #include #include -#include -#include #include #include -#include #include -#include -#include #include #include -#include -#include #include #include +#include -extern void show_pte(struct mm_struct *mm, unsigned long addr); -extern int do_page_fault(unsigned long addr, int error_code, - struct pt_regs *regs); -extern int do_translation_fault(unsigned long addr, int error_code, - struct pt_regs *regs); -extern void do_bad_area(struct task_struct *tsk, struct mm_struct *mm, - unsigned long addr, int error_code, - struct pt_regs *regs); - -#ifdef CONFIG_ALIGNMENT_TRAP -extern int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs); -#else -#define do_alignment do_bad -#endif - +#include "fault.h" /* * Some section permission faults need to be handled gracefully. * They can happen due to a __{get,put}_user during an oops. */ static int -do_sect_fault(unsigned long addr, int error_code, struct pt_regs *regs) +do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { struct task_struct *tsk = current; - do_bad_area(tsk, tsk->active_mm, addr, error_code, regs); + do_bad_area(tsk, tsk->active_mm, addr, fsr, regs); return 0; } /* - * Hook for things that need to trap external faults. Note that - * we don't guarantee that this will be the final version of the - * interface. - */ -int (*external_fault)(unsigned long addr, struct pt_regs *regs); - -static int -do_external_fault(unsigned long addr, int error_code, struct pt_regs *regs) -{ - if (external_fault) - return external_fault(addr, regs); - return 1; -} - -/* * This abort handler always returns "fault". */ static int -do_bad(unsigned long addr, int error_code, struct pt_regs *regs) +do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { return 1; } -static const struct fsr_info { - int (*fn)(unsigned long addr, int error_code, struct pt_regs *regs); +static struct fsr_info { + int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); int sig; const char *name; } fsr_info[] = { { do_bad, SIGSEGV, "vector exception" }, - { do_alignment, SIGILL, "alignment exception" }, + { do_bad, SIGILL, "alignment exception" }, { do_bad, SIGKILL, "terminal exception" }, - { do_alignment, SIGILL, "alignment exception" }, - { do_external_fault, SIGBUS, "external abort on linefetch" }, + { do_bad, SIGILL, "alignment exception" }, + { do_bad, SIGBUS, "external abort on linefetch" }, { do_translation_fault, SIGSEGV, "section translation fault" }, - { do_external_fault, SIGBUS, "external abort on linefetch" }, + { do_bad, SIGBUS, "external abort on linefetch" }, { do_page_fault, SIGSEGV, "page translation fault" }, - { do_external_fault, SIGBUS, "external abort on non-linefetch" }, + { do_bad, SIGBUS, "external abort on non-linefetch" }, { do_bad, SIGSEGV, "section domain fault" }, - { do_external_fault, SIGBUS, "external abort on non-linefetch" }, + { do_bad, SIGBUS, "external abort on non-linefetch" }, { do_bad, SIGSEGV, "page domain fault" }, { do_bad, SIGBUS, "external abort on translation" }, { do_sect_fault, SIGSEGV, "section permission fault" }, { do_bad, SIGBUS, "external abort on translation" }, - { do_page_fault, SIGSEGV, "page permission fault" } + { do_page_fault, SIGSEGV, "page permission fault" }, }; +void __init +hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), + int sig, const char *name) +{ + if (nr >= 0 && nr < ARRAY_SIZE(fsr_info)) { + fsr_info[nr].fn = fn; + fsr_info[nr].sig = sig; + fsr_info[nr].name = name; + } +} + /* * Dispatch a data abort to the relevant handler. */ asmlinkage void -do_DataAbort(unsigned long addr, int error_code, struct pt_regs *regs, int fsr) +do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { const struct fsr_info *inf = fsr_info + (fsr & 15); - if (!inf->fn(addr, error_code, regs)) + if (!inf->fn(addr, fsr, regs)) return; printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", @@ -127,25 +101,28 @@ do_translation_fault(addr, 0, regs); } +static unsigned long shared_pte_mask = L_PTE_CACHEABLE; + /* * We take the easy way out of this problem - we make the * PTE uncacheable. However, we leave the write buffer on. */ -static void adjust_pte(struct vm_area_struct *vma, unsigned long address) +static int adjust_pte(struct vm_area_struct *vma, unsigned long address) { pgd_t *pgd; pmd_t *pmd; pte_t *pte, entry; + int ret = 0; pgd = pgd_offset(vma->vm_mm, address); if (pgd_none(*pgd)) - return; + goto no_pgd; if (pgd_bad(*pgd)) goto bad_pgd; pmd = pmd_offset(pgd, address); if (pmd_none(*pmd)) - return; + goto no_pmd; if (pmd_bad(*pmd)) goto bad_pmd; @@ -156,33 +133,38 @@ * If this page isn't present, or is already setup to * fault (ie, is old), we can safely ignore any issues. */ - if (pte_present(entry) && pte_val(entry) & L_PTE_CACHEABLE) { + if (pte_present(entry) && pte_val(entry) & shared_pte_mask) { flush_cache_page(vma, address); - pte_val(entry) &= ~L_PTE_CACHEABLE; + pte_val(entry) &= ~shared_pte_mask; set_pte(pte, entry); flush_tlb_page(vma, address); + ret = 1; } - return; + return ret; bad_pgd: pgd_ERROR(*pgd); pgd_clear(pgd); - return; +no_pgd: + return 0; bad_pmd: pmd_ERROR(*pmd); pmd_clear(pmd); - return; +no_pmd: + return 0; } static void -make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page) +make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page, int dirty) { struct vm_area_struct *mpnt; struct mm_struct *mm = vma->vm_mm; - unsigned long pgoff = (addr - vma->vm_start) >> PAGE_SHIFT; + unsigned long pgoff; int aliases = 0; + pgoff = vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT); + /* * If we have any shared mappings that are in the same mm * space, then we need to handle them specially to maintain @@ -194,7 +176,7 @@ /* * If this VMA is not in our MM, we can ignore it. - * Note that we intentionally don't mask out the VMA + * Note that we intentionally mask out the VMA * that we are fixing up. */ if (mpnt->vm_mm != mm || mpnt == vma) @@ -210,14 +192,17 @@ if (off >= (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT) continue; + off = mpnt->vm_start + (off << PAGE_SHIFT); + /* * Ok, it is within mpnt. Fix it up. */ - adjust_pte(mpnt, mpnt->vm_start + (off << PAGE_SHIFT)); - aliases ++; + aliases += adjust_pte(mpnt, off); } if (aliases) adjust_pte(vma, addr); + else if (dirty) + flush_cache_page(vma, addr); } /* @@ -242,11 +227,85 @@ return; page = pfn_to_page(pfn); if (page->mapping) { - if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) { + int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags); + + if (dirty) { unsigned long kvirt = (unsigned long)page_address(page); cpu_cache_clean_invalidate_range(kvirt, kvirt + PAGE_SIZE, 0); } - make_coherent(vma, addr, page); + make_coherent(vma, addr, page, dirty); + } +} + +/* + * Check whether the write buffer has physical address aliasing + * issues. If it has, we need to avoid them for the case where + * we have several shared mappings of the same object in user + * space. + */ +static int __init check_writebuffer(unsigned long *p1, unsigned long *p2) +{ + register unsigned long zero = 0, one = 1, val; + + mb(); + *p1 = one; + mb(); + *p2 = zero; + mb(); + val = *p1; + mb(); + return val != zero; +} + +static inline void *map_page(struct page *page) +{ + void *map; + + map = __ioremap(page_to_phys(page), PAGE_SIZE, L_PTE_BUFFERABLE); + if (map) + get_page(page); + return map; +} + +static inline void unmap_page(void *map) +{ + iounmap(map); +} + +void __init check_writebuffer_bugs(void) +{ + struct page *page; + const char *reason; + unsigned long v = 1; + + printk(KERN_INFO "CPU: Testing write buffer: "); + + page = alloc_page(GFP_KERNEL); + if (page) { + unsigned long *p1, *p2; + + p1 = map_page(page); + p2 = map_page(page); + + if (p1 && p2) { + v = check_writebuffer(p1, p2); + reason = "enabling work-around"; + } else { + reason = "unable to map memory\n"; + } + + unmap_page(p1); + unmap_page(p2); + put_page(page); + } else { + reason = "unable to grab page\n"; + } + + if (v) { + printk("FAIL - %s\n", reason); + shared_pte_mask |= L_PTE_BUFFERABLE; + } else { + printk("pass\n"); } } --- linux-2.4.27/arch/arm/mm/fault-common.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/fault-common.c @@ -11,21 +11,17 @@ #include #include #include -#include -#include #include -#include #include -#include #include #include -#include #include #include -#include #include -#include +#include + +#include "fault.h" #ifdef CONFIG_CPU_26 #define FAULT_CODE_WRITE 0x02 @@ -34,13 +30,11 @@ #define READ_FAULT(m) (!((m) & FAULT_CODE_WRITE)) #else /* - * On 32-bit processors, we define "mode" to be zero when reading, - * non-zero when writing. This now ties up nicely with the polarity - * of the 26-bit machines, and also means that we avoid the horrible - * gcc code for "int val = !other_val;". + * "code" is actually the FSR register. Bit 11 set means the + * instruction was performing a write. */ -#define DO_COW(m) (m) -#define READ_FAULT(m) (!(m)) +#define DO_COW(code) ((code) & (1 << 11)) +#define READ_FAULT(code) (!DO_COW(code)) #endif /* @@ -54,16 +48,17 @@ if (!mm) mm = &init_mm; - printk(KERN_ALERT "mm = %p pgd = %p\n", mm, mm->pgd); - fs = get_fs(); set_fs(get_ds()); + do { - pgd_t pg, *pgd = pgd_offset(mm, addr); + pgd_t pg, *pgd; pmd_t pm, *pmd; pte_t pt, *pte; - printk(KERN_ALERT "*pgd = "); + printk(KERN_ALERT "pgd = %p\n", mm->pgd); + pgd = pgd_offset(mm, addr); + printk(KERN_ALERT "[%08lx] *pgd=", addr); if (__get_user(pgd_val(pg), (unsigned long *)pgd)) { printk("(faulted)"); @@ -122,7 +117,7 @@ * Oops. The kernel tried to access some page that wasn't present. */ static void -__do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code, +__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, struct pt_regs *regs) { unsigned long fixup; @@ -148,7 +143,7 @@ "paging request", addr); show_pte(mm, addr); - die("Oops", regs, error_code); + die("Oops", regs, fsr); do_exit(SIGKILL); } @@ -157,20 +152,20 @@ * User mode accesses just cause a SIGSEGV */ static void -__do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code, - int code, struct pt_regs *regs) +__do_user_fault(struct task_struct *tsk, unsigned long addr, + unsigned int fsr, int code, struct pt_regs *regs) { struct siginfo si; #ifdef CONFIG_DEBUG_USER printk(KERN_DEBUG "%s: unhandled page fault at pc=0x%08lx, " "lr=0x%08lx (bad address=0x%08lx, code %d)\n", - tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, error_code); + tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, fsr); show_regs(regs); #endif tsk->thread.address = addr; - tsk->thread.error_code = error_code; + tsk->thread.error_code = fsr; tsk->thread.trap_no = 14; si.si_signo = SIGSEGV; si.si_errno = 0; @@ -181,20 +176,20 @@ void do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, - int error_code, struct pt_regs *regs) + unsigned int fsr, struct pt_regs *regs) { /* * If we are in kernel mode at this point, we * have no context to handle this fault with. */ if (user_mode(regs)) - __do_user_fault(tsk, addr, error_code, SEGV_MAPERR, regs); + __do_user_fault(tsk, addr, fsr, SEGV_MAPERR, regs); else - __do_kernel_fault(mm, addr, error_code, regs); + __do_kernel_fault(mm, addr, fsr, regs); } static int -__do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code, +__do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, struct task_struct *tsk) { struct vm_area_struct *vma; @@ -212,7 +207,7 @@ * memory access, so we can handle it. */ good_area: - if (READ_FAULT(error_code)) /* read? */ + if (READ_FAULT(fsr)) /* read? */ mask = VM_READ|VM_EXEC; else mask = VM_WRITE; @@ -227,7 +222,7 @@ * than endlessly redo the fault. */ survive: - fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(error_code)); + fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(fsr)); /* * Handle the "normal" cases first - successful and sigbus @@ -260,7 +255,7 @@ return fault; } -int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs) +int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { struct task_struct *tsk; struct mm_struct *mm; @@ -277,7 +272,7 @@ goto no_context; down_read(&mm->mmap_sem); - fault = __do_page_fault(mm, addr, error_code, tsk); + fault = __do_page_fault(mm, addr, fsr, tsk); up_read(&mm->mmap_sem); /* @@ -308,7 +303,7 @@ printk("VM: killing process %s\n", tsk->comm); do_exit(SIGKILL); } else - __do_user_fault(tsk, addr, error_code, fault == -1 ? + __do_user_fault(tsk, addr, fsr, fault == -1 ? SEGV_ACCERR : SEGV_MAPERR, regs); return 0; @@ -323,7 +318,7 @@ * or user mode. */ tsk->thread.address = addr; - tsk->thread.error_code = error_code; + tsk->thread.error_code = fsr; tsk->thread.trap_no = 14; force_sig(SIGBUS, tsk); #ifdef CONFIG_DEBUG_USER @@ -336,7 +331,7 @@ return 0; no_context: - __do_kernel_fault(mm, addr, error_code, regs); + __do_kernel_fault(mm, addr, fsr, regs); return 0; } @@ -357,21 +352,23 @@ * interrupt or a critical region, and should only copy the information * from the master page table, nothing more. */ -int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *regs) +int do_translation_fault(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) { struct task_struct *tsk; - struct mm_struct *mm; int offset; pgd_t *pgd, *pgd_k; pmd_t *pmd, *pmd_k; if (addr < TASK_SIZE) - return do_page_fault(addr, error_code, regs); + return do_page_fault(addr, fsr, regs); offset = __pgd_offset(addr); /* * FIXME: CP15 C1 is write only on ARMv3 architectures. + * You really need to read the value in the page table + * register, not a copy. */ pgd = cpu_get_pgd() + offset; pgd_k = init_mm.pgd + offset; @@ -395,8 +392,7 @@ bad_area: tsk = current; - mm = tsk->active_mm; - do_bad_area(tsk, mm, addr, error_code, regs); + do_bad_area(tsk, tsk->active_mm, addr, fsr, regs); return 0; } --- /dev/null +++ linux-2.4.27/arch/arm/mm/fault.h @@ -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); + --- linux-2.4.27/arch/arm/mm/init.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/init.c @@ -9,7 +9,6 @@ */ #include #include -#include #include #include #include @@ -18,7 +17,6 @@ #include #include #include -#include #include #include #include --- linux-2.4.27/arch/arm/mm/ioremap.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/ioremap.c @@ -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.. --- linux-2.4.27/arch/arm/mm/mm-armv.c~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/mm-armv.c @@ -9,7 +9,6 @@ * * Page table sludge for ARM v3 and v4 processor architectures. */ -#include #include #include #include @@ -390,6 +389,9 @@ init_maps->bufferable = 0; create_mapping(init_maps); + + flush_cache_all(); + flush_tlb_all(); } /* --- linux-2.4.27/arch/arm/mm/proc-arm1020.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-arm1020.S @@ -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 --- /dev/null +++ linux-2.4.27/arch/arm/mm/proc-arm1020E.S @@ -0,0 +1,718 @@ +/* + * linux/arch/arm/mm/proc-arm1020E.S: MMU functions for ARM1020E + * + * Copyright (C) 2000 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * These are the low level assembler for performing cache and TLB + * functions on the arm1020E. + */ +#include +#include +#include +#include +#include +#include + +/* + * This is the maximum size of an area which will be invalidated + * using the single invalidate entry instructions. Anything larger + * than this, and we go for the whole cache. + * + * This value should be chosen such that we choose the cheapest + * alternative. + */ +#define MAX_AREA_SIZE 32768 + +/* + * the cache line size of the I and D cache + */ +#define DCACHELINESIZE 32 +#define ICACHELINESIZE 32 + +/* + * and the page size + */ +#define LOG2PAGESIZE 12 /* == 4096 Bytes */ +#define PAGESIZE (1 << LOG2PAGESIZE) + +/* + * create some useful conditional macro definitions + * we often need to know if we are ((not dcache disable) and writethrough) or ((not dcache disable) and writeback) + */ +#ifdef CONFIG_CPU_DCACHE_DISABLE + #undef CONFIG_CPU_DCACHE_WRITETHROUGH + #undef CONFIG_CPU_DCACHE_WRITEBACK + #undef CONFIG_CPU_DCACHE_ENABLE +#else + #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + #undef CONFIG_CPU_DCACHE_WRITEBACK + #else + #define CONFIG_CPU_DCACHE_WRITEBACK + #endif + #define CONFIG_CPU_DCACHE_ENABLE +#endif + +#ifdef CONFIG_CPU_ICACHE_DISABLE + #undef CONFIG_CPU_ICACHE_ENABLE +#else + #define CONFIG_CPU_ICACHE_ENABLE +#endif + + .text + +/* + * cpu_arm1020E_data_abort() + * + * obtain information about current aborted instruction + * Note: we read user space. This means we might cause a data + * abort here if the I-TLB and D-TLB aren't seeing the same + * picture. Unfortunately, this does happen. We live with it. + * + * r2 = address of aborted instruction + * r3 = cpsr + * + * Returns: + * r0 = address of abort + * r1 = FSR + * r3 = corrupted + * r4 = corrupted + */ + .align 5 +ENTRY(cpu_arm1020E_data_abort) + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + tst r3, #PSR_T_BIT + ldrneh r3, [r2] @ read aborted thumb instruction + ldreq r3, [r2] @ read aborted ARM instruction + bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR + movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 + tst r3, #1 << 20 @ check write + orreq r1, r1, #1 << 11 + mov pc, lr + +/* + * cpu_arm1020E_check_bugs() + */ +ENTRY(cpu_arm1020E_check_bugs) + mrs ip, cpsr + bic ip, ip, #F_BIT + msr cpsr, ip + mov pc, lr + +/* + * cpu_arm1020E_proc_init() + */ +ENTRY(cpu_arm1020E_proc_init) + mov pc, lr + +/* + * cpu_arm1020E_proc_fin() + */ +ENTRY(cpu_arm1020E_proc_fin) + stmfd sp!, {lr} + mov ip, #F_BIT | I_BIT | SVC_MODE + msr cpsr_c, ip + bl cpu_arm1020E_cache_clean_invalidate_all + mrc p15, 0, r0, c1, c0, 0 @ ctrl register + bic r0, r0, #0x1000 @ ...i............ + bic r0, r0, #0x000e @ ............wca. + mcr p15, 0, r0, c1, c0, 0 @ disable caches + ldmfd sp!, {pc} + +/* + * cpu_arm1020E_reset(loc) + * + * Perform a soft reset of the system. Put the CPU into the + * same state as it would be if it had been reset, and branch + * to what would be the reset vector. + * + * loc: location to jump to for soft reset + */ + .align 5 +ENTRY(cpu_arm1020E_reset) + mov ip, #0 + mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches + mcr p15, 0, ip, c7, c10, 4 @ drain WB + mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs + mrc p15, 0, ip, c1, c0, 0 @ ctrl register + bic ip, ip, #0x000f @ ............wcam + bic ip, ip, #0x1100 @ ...i...s........ + mcr p15, 0, ip, c1, c0, 0 @ ctrl register + mov pc, r0 + +/* + * cpu_arm1020E_do_idle() + */ + .align 5 +ENTRY(cpu_arm1020E_do_idle) + mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt + mov pc, lr + +/* ================================= CACHE ================================ */ + + +/* + * cpu_arm1020E_cache_clean_invalidate_all() + * + * clean and invalidate all cache lines + * + * Note: + * 1. we should preserve r0 and ip at all times + */ + .align 5 +ENTRY(cpu_arm1020E_cache_clean_invalidate_all) + mov r2, #1 +cpu_arm1020E_cache_clean_invalidate_all_r2: + +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mov r1, #0x0F << 5 @ 16 segments +1: orr r3, r1, #63 << 26 @ 64 entries +2: mcr p15, 0, r3, c7, c14, 2 @ clean and invalidate D index + subs r3, r3, #1 << 26 + bcs 2b + subs r1, r1, #1 << 5 + bcs 1b @ segments 15 to 0 +#endif + + mov r1, #0 + +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r1, c7, c6, 0 @ invalidate D cache +#endif + +#ifdef CONFIG_CPU_ICACHE_ENABLE + teq r2, #0 + mcrne p15, 0, r1, c7, c5, 0 @ invalidate I cache +#endif + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1020E_cache_clean_invalidate_range(start, end, flags) + * + * clean and invalidate all cache lines associated with this area of memory + * + * start: Area start address + * end: Area end address + * flags: nonzero for I cache as well + */ + .align 5 +ENTRY(cpu_arm1020E_cache_clean_invalidate_range) + bic r0, r0, #DCACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE + bhs cpu_arm1020E_cache_clean_invalidate_all_r2 +1: +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry +#endif +#ifdef CONFIG_CPU_ICACHE_ENABLE + teq r2, #0 + mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry +#endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 + bls 1b @ unsigned lower or same - must include end point (r1)! + + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1020E_flush_ram_page(page) + * + * clean and invalidate all cache lines associated with this area of memory + * + * page: page to clean and invalidate + */ + .align 5 +ENTRY(cpu_arm1020E_flush_ram_page) + mov r1, #PAGESIZE + mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page + mov r0, r0, LSL #LOG2PAGESIZE +1: +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry +#endif +#ifdef CONFIG_CPU_ICACHE_ENABLE + mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry +#endif + add r0, r0, #DCACHELINESIZE + subs r1, r1, #DCACHELINESIZE + bne 1b + + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* ================================ D-CACHE =============================== */ + +/* + * cpu_arm1020E_dcache_invalidate_range(start, end) + * + * throw away all D-cached data in specified region without an obligation + * to write them back. Note however that we must clean the D-cached entries + * around the boundaries if the start and/or end address are not cache + * aligned. + * + * start: virtual start address + * end: virtual end address + */ + .align 5 +ENTRY(cpu_arm1020E_dcache_invalidate_range) +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + bic r0, r0, #DCACHELINESIZE - 1 +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + tst r0, #DCACHELINESIZE - 1 + bic r0, r0, #DCACHELINESIZE - 1 + mcrne p15, 0, r0, c7, c10, 1 @ clean D entry at start + tst r1, #DCACHELINESIZE - 1 + mcrne p15, 0, r1, c7, c10, 1 @ clean D entry at end +#endif + +1: +#ifdef CONFIG_CPU_DCACHE_ENABLE + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +#endif +#ifdef CONFIG_CPU_ICACHE_ENABLE + mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry +#endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 + bls 1b + + /* Even if the D cache is off still drain the write buffer */ + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer + mov pc, lr + +/* + * cpu_arm1020E_dcache_clean_range(start, end) + * + * For the specified virtual address range, ensure that all caches contain + * clean data, such that peripheral accesses to the physical RAM fetch + * correct data. + * + * start: virtual start address + * end: virtual end address + */ + .align 5 +ENTRY(cpu_arm1020E_dcache_clean_range) + + mov r2, #0 + +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + bic r0, r0, #DCACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE + bhs cpu_arm1020E_cache_clean_invalidate_all_r2 + +1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE + cmp r0, r1 + bls 1b +#endif + + mcr p15, 0, r2, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1020E_dcache_clean_page(page) + * + * Cleans a single page of dcache so that if we have any future aliased + * mappings, they will be consistent at the time that they are created. + * + * page: virtual address of page to clean from dcache + * + * Note: + * we don't invalidate the entries since when we write the page + * out to disk, the entries may get reloaded into the cache. + */ + .align 5 +ENTRY(cpu_arm1020E_dcache_clean_page) +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page + mov r0, r0, LSL #LOG2PAGESIZE + mov r1, #PAGESIZE +1: + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE + subs r1, r1, #DCACHELINESIZE + bne 1b +#endif + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1020E_dcache_clean_entry(addr) + * + * Clean the specified entry of any caches such that the MMU + * translation fetches will obtain correct data. + * + * addr: cache-unaligned virtual address + */ + .align 5 +ENTRY(cpu_arm1020E_dcache_clean_entry) +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + bic r0, r0, #DCACHELINESIZE - 1 + mcr p15, 0, r0, c7, c10, 1 @ clean single D entry +#endif + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* ================================ I-CACHE =============================== */ + +/* + * cpu_arm1020E_icache_invalidate_range(start, end) + * + * invalidate a range of virtual addresses from the Icache + * + * This is a little misleading, it is not intended to clean out + * the i-cache but to make sure that any data written to the + * range is made consistent. This means that when we execute code + * in that region, everything works as we expect. + * + * This generally means writing back data in the Dcache and + * write buffer and invalidating the Icache over that region + * + * start: virtual start address + * end: virtual end address + * + * NOTE: ICACHELINESIZE == DCACHELINESIZE (so we don't need to + * loop twice, once for i-cache, once for d-cache) + */ + .align 5 +ENTRY(cpu_arm1020E_icache_invalidate_range) + bic r0, r0, #ICACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE + movhs r2, #1 + bhs cpu_arm1020E_cache_clean_invalidate_all_r2 + +1: +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c10, 1 @ Clean D entry +#endif +#ifdef CONFIG_CPU_ICACHE_ENABLE + mcr p15, 0, r0, c7, c5, 1 @ Invalidate I entry +#endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 + bls 1b @ unsigned lower or same - includes r1 entry + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB + mov pc, lr + +ENTRY(cpu_arm1020E_icache_invalidate_page) + mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page + mov r0, r0, LSL #LOG2PAGESIZE + add r1, r0, #PAGESIZE + b cpu_arm1020E_icache_invalidate_range + +/* ================================== TLB ================================= */ + +/* + * cpu_arm1020E_tlb_invalidate_all() + * + * Invalidate all TLB entries + */ + .align 5 +ENTRY(cpu_arm1020E_tlb_invalidate_all) + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB + mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D tlbs + mov pc, lr + +/* + * cpu_arm1020E_tlb_invalidate_range(start, end) + * + * invalidate TLB entries covering the specified range + * + * start: range start address + * end: range end address + */ + .align 5 +ENTRY(cpu_arm1020E_tlb_invalidate_range) + sub r3, r1, r0 + cmp r3, #256 * PAGESIZE + bhs cpu_arm1020E_tlb_invalidate_all + mov r3, #0 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + mov r3, #PAGESIZE + sub r3, r3, #1 + bic r0, r0, r3 +1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry + add r0, r0, #PAGESIZE + cmp r0, r1 + bls 1b + mov pc, lr + +/* + * cpu_arm1020E_tlb_invalidate_page(page, flags) + * + * invalidate the TLB entries for the specified page. + * + * page: page to invalidate + * flags: non-zero if we include the I TLB + */ + .align 5 +ENTRY(cpu_arm1020E_tlb_invalidate_page) + mov r3, #0 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page + mov r0, r0, LSL #LOG2PAGESIZE + teq r1, #0 + mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry + mov pc, lr + +/* =============================== PageTable ============================== */ + +/* + * cpu_arm1020E_set_pgd(pgd) + * + * Set the translation base pointer to be as described by pgd. + * + * pgd: new page tables + */ + .align 5 +ENTRY(cpu_arm1020E_set_pgd) + stmfd sp!, {lr} + bl cpu_arm1020E_cache_clean_invalidate_all @ preserves r0 + mov r1, #0 + mcr p15, 0, r0, c2, c0, 0 @ load page table pointer + mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs + ldmfd sp!, {pc} + +/* + * cpu_arm1020E_set_pmd(pmdp, pmd) + * + * Set a level 1 translation table entry, and clean it out of + * any caches such that the MMUs can load it correctly. + * + * pmdp: pointer to PMD entry + * pmd: PMD value to store + */ + .align 5 +ENTRY(cpu_arm1020E_set_pmd) +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + eor r2, r1, #0x0a @ C & Section + tst r2, #0x0b + biceq r1, r1, #4 @ clear bufferable bit +#endif + str r1, [r0] +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c10, 1 @ clean D entry +#endif + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1020E_set_pte(ptep, pte) + * + * Set a PTE and flush it out + */ + .align 5 +ENTRY(cpu_arm1020E_set_pte) + str r1, [r0], #-1024 @ linux version + + eor r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY + + bic r2, r1, #0xff0 + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + + tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? + orreq r2, r2, #HPTE_AP_WRITE + + tst r1, #LPTE_PRESENT | LPTE_YOUNG @ Present and Young? + movne r2, #0 + +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + eor r3, r2, #0x0a @ C and Small Page? + tst r3, #0x0b @ if so.. + biceq r2, r2, #0x04 @ clear the bufferable bit +#endif + str r2, [r0] @ hardware version +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c10, 1 @ clean D entry +#endif + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + + +cpu_manu_name: + .asciz "ARM" +ENTRY(cpu_arm1020E_name) + .ascii "Arm1020E" +#ifdef CONFIG_CPU_ICACHE_ENABLE + .ascii "i" +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + .ascii "d" +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + .ascii "(wt)" +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + .ascii "(wb)" +#endif +#endif +#ifndef CONFIG_CPU_BPREDICT_DISABLE + .ascii "B" +#endif +#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN + .ascii "RR" +#endif + .ascii "\0" + .align + + .section ".text.init", #alloc, #execinstr + +__arm1020E_setup: + mov r0, #0 + mcr p15, 0, r0, c7, c7, 0 @ invalidate I,D caches on v4 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 + mcr p15, 0, r0, c8, c7, 0 @ invalidate I,D TLBs on v4 + mcr p15, 0, r4, c2, c0, 0 @ load page table pointer + mov r0, #0x1f @ Domains 0, 1 = client + mcr p15, 0, r0, c3, c0, 0 @ load domain access register + + mrc p15, 0, r0, c1, c0, 0 @ Read current control register +/* + * The only thing worth keeping from the initial control register is the endian bit + */ + + and r0, r0, #0x0080 @ ........B....... Preserve endian bit, zero all others. + orr r0, r0, #0x0070 @ .........111.... Set the SBO (Should Be One) bits +/* + * Turn on what we want. + */ + orr r0, r0, #0x0001 @ ...............M Enable MMU (Alignment is special cased elsewhere) + orr r0, r0, #0x0100 @ .......S........ Enable system MMU protection + orr r0, r0, #0x2000 @ ..V............. Enable high vectors + +#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN + orr r0, r0, #0x4000 @ .R.............. Force round-robin replacement +#endif + +#ifndef CONFIG_CPU_BPREDICT_DISABLE + orr r0, r0, #0x0800 @ ....Z........... Enable branch prediction +#endif + +#ifdef CONFIG_CPU_DCACHE_ENABLE + orr r0, r0, #0x0004 @ .............C.. Enable D cache +#endif +#ifndef CONFIG_CPU_WB_DISABLE + orr r0, r0, #0x0008 @ ............W... Write Buffer enabled +#endif + +#ifdef CONFIG_CPU_ICACHE_ENABLE + orr r0, r0, #0x1000 @ ...I............ Enable I Cache +#endif + + mov pc, lr + + .text + +/* + * Purpose : Function pointers used to access above functions - all calls + * come through these + */ + .type arm1020E_processor_functions, #object +arm1020E_processor_functions: + .word cpu_arm1020E_data_abort + .word cpu_arm1020E_check_bugs + .word cpu_arm1020E_proc_init + .word cpu_arm1020E_proc_fin + .word cpu_arm1020E_reset + .word cpu_arm1020E_do_idle + + /* cache */ + .word cpu_arm1020E_cache_clean_invalidate_all + .word cpu_arm1020E_cache_clean_invalidate_range + .word cpu_arm1020E_flush_ram_page + + /* dcache */ + .word cpu_arm1020E_dcache_invalidate_range + .word cpu_arm1020E_dcache_clean_range + .word cpu_arm1020E_dcache_clean_page + .word cpu_arm1020E_dcache_clean_entry + + /* icache */ + .word cpu_arm1020E_icache_invalidate_range + .word cpu_arm1020E_icache_invalidate_page + + /* tlb */ + .word cpu_arm1020E_tlb_invalidate_all + .word cpu_arm1020E_tlb_invalidate_range + .word cpu_arm1020E_tlb_invalidate_page + + /* pgtable */ + .word cpu_arm1020E_set_pgd + .word cpu_arm1020E_set_pmd + .word cpu_arm1020E_set_pte + .size arm1020E_processor_functions, . - arm1020E_processor_functions + + .type cpu_arm1020E_info, #object +cpu_arm1020E_info: + .long cpu_manu_name + .long cpu_arm1020E_name + .size cpu_arm1020E_info, . - cpu_arm1020E_info + + .type cpu_arch_name, #object +cpu_arch_name: + .asciz "armv5te" + .size cpu_arch_name, . - cpu_arch_name + + .type cpu_elf_name, #object +cpu_elf_name: + .asciz "v5" + .size cpu_elf_name, . - cpu_elf_name + .align + + .section ".proc.info", #alloc, #execinstr + + .type __arm1020E_proc_info,#object +__arm1020E_proc_info: + .long 0x4105a200 @ ARM 1020E (Architecture v5TE) + .long 0xff0ffff0 + .long 0x00000c1e @ mmuflags + b __arm1020E_setup + .long cpu_arch_name + .long cpu_elf_name + .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB + .long cpu_arm1020E_info + .long arm1020E_processor_functions + .size __arm1020E_proc_info, . - __arm1020E_proc_info + --- /dev/null +++ linux-2.4.27/arch/arm/mm/proc-arm1022.S @@ -0,0 +1,716 @@ +/* + * linux/arch/arm/mm/proc-arm1022.S: MMU functions for ARM1022E + * + * Copyright (C) 2000 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * These are the low level assembler for performing cache and TLB + * functions on the arm1022E. + */ +#include +#include +#include +#include +#include +#include + +/* + * This is the maximum size of an area which will be invalidated + * using the single invalidate entry instructions. Anything larger + * than this, and we go for the whole cache. + * + * This value should be chosen such that we choose the cheapest + * alternative. + */ +#define MAX_AREA_SIZE 16384 + +/* + * the cache line size of the I and D cache + */ +#define DCACHELINESIZE 32 +#define ICACHELINESIZE 32 + +/* + * and the page size + */ +#define LOG2PAGESIZE 12 /* == 4096 Bytes */ +#define PAGESIZE (1 << LOG2PAGESIZE) + +/* + * create some useful conditional macro definitions + * we often need to know if we are ((not dcache disable) and writethrough) or ((not dcache disable) and writeback) + */ +#ifdef CONFIG_CPU_DCACHE_DISABLE + #undef CONFIG_CPU_DCACHE_WRITETHROUGH + #undef CONFIG_CPU_DCACHE_WRITEBACK + #undef CONFIG_CPU_DCACHE_ENABLE +#else + #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + #undef CONFIG_CPU_DCACHE_WRITEBACK + #else + #define CONFIG_CPU_DCACHE_WRITEBACK + #endif + #define CONFIG_CPU_DCACHE_ENABLE +#endif + +#ifdef CONFIG_CPU_ICACHE_DISABLE + #undef CONFIG_CPU_ICACHE_ENABLE +#else + #define CONFIG_CPU_ICACHE_ENABLE +#endif + + .text + +/* + * cpu_arm1022_data_abort() + * + * obtain information about current aborted instruction + * Note: we read user space. This means we might cause a data + * abort here if the I-TLB and D-TLB aren't seeing the same + * picture. Unfortunately, this does happen. We live with it. + * + * r2 = address of aborted instruction + * r3 = cpsr + * + * Returns: + * r0 = address of abort + * r1 = FSR + * r3 = corrupted + * r4 = corrupted + */ + .align 5 +ENTRY(cpu_arm1022_data_abort) + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + tst r3, #PSR_T_BIT + ldrneh r3, [r2] @ read aborted thumb instruction + ldreq r3, [r2] @ read aborted ARM instruction + bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR + movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 + tst r3, #1 << 20 @ check write + orreq r1, r1, #1 << 11 + mov pc, lr + +/* + * cpu_arm1022_check_bugs() + */ +ENTRY(cpu_arm1022_check_bugs) + mrs ip, cpsr + bic ip, ip, #F_BIT + msr cpsr, ip + mov pc, lr + +/* + * cpu_arm1022_proc_init() + */ +ENTRY(cpu_arm1022_proc_init) + mov pc, lr + +/* + * cpu_arm1022_proc_fin() + */ +ENTRY(cpu_arm1022_proc_fin) + stmfd sp!, {lr} + mov ip, #F_BIT | I_BIT | SVC_MODE + msr cpsr_c, ip + bl cpu_arm1022_cache_clean_invalidate_all + mrc p15, 0, r0, c1, c0, 0 @ ctrl register + bic r0, r0, #0x1000 @ ...i............ + bic r0, r0, #0x000e @ ............wca. + mcr p15, 0, r0, c1, c0, 0 @ disable caches + ldmfd sp!, {pc} + +/* + * cpu_arm1022_reset(loc) + * + * Perform a soft reset of the system. Put the CPU into the + * same state as it would be if it had been reset, and branch + * to what would be the reset vector. + * + * loc: location to jump to for soft reset + */ + .align 5 +ENTRY(cpu_arm1022_reset) + mov ip, #0 + mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches + mcr p15, 0, ip, c7, c10, 4 @ drain WB + mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs + mrc p15, 0, ip, c1, c0, 0 @ ctrl register + bic ip, ip, #0x000f @ ............wcam + bic ip, ip, #0x1100 @ ...i...s........ + mcr p15, 0, ip, c1, c0, 0 @ ctrl register + mov pc, r0 + +/* + * cpu_arm1022_do_idle() + */ + .align 5 +ENTRY(cpu_arm1022_do_idle) + mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt + mov pc, lr + +/* ================================= CACHE ================================ */ + + +/* + * cpu_arm1022_cache_clean_invalidate_all() + * + * clean and invalidate all cache lines + * + * Note: + * 1. we should preserve r0 and ip at all times + */ + .align 5 +ENTRY(cpu_arm1022_cache_clean_invalidate_all) + mov r2, #1 +cpu_arm1022_cache_clean_invalidate_all_r2: + +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mov r1, #7 << 5 @ 8 segments +1: orr r3, r1, #63 << 26 @ 64 entries +2: mcr p15, 0, r3, c7, c14, 2 @ clean and invalidate D index + subs r3, r3, #1 << 26 + bcs 2b + subs r1, r1, #1 << 5 + bcs 1b @ segments 7 to 0 +#endif + + mov r1, #0 + +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r1, c7, c6, 0 @ invalidate D cache +#endif + +#ifdef CONFIG_CPU_ICACHE_ENABLE + teq r2, #0 + mcrne p15, 0, r1, c7, c5, 0 @ invalidate I cache +#endif + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1022_cache_clean_invalidate_range(start, end, flags) + * + * clean and invalidate all cache lines associated with this area of memory + * + * start: Area start address + * end: Area end address + * flags: nonzero for I cache as well + */ + .align 5 +ENTRY(cpu_arm1022_cache_clean_invalidate_range) + bic r0, r0, #DCACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE + bhs cpu_arm1022_cache_clean_invalidate_all_r2 +1: +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry +#endif +#ifdef CONFIG_CPU_ICACHE_ENABLE + teq r2, #0 + mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry +#endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 + bls 1b @ unsigned lower or same - must include end point (r1)! + + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1022_flush_ram_page(page) + * + * clean and invalidate all cache lines associated with this area of memory + * + * page: page to clean and invalidate + */ + .align 5 +ENTRY(cpu_arm1022_flush_ram_page) + mov r1, #PAGESIZE + mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page + mov r0, r0, LSL #LOG2PAGESIZE +1: +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry +#endif +#ifdef CONFIG_CPU_ICACHE_ENABLE + mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry +#endif + add r0, r0, #DCACHELINESIZE + subs r1, r1, #DCACHELINESIZE + bne 1b + + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* ================================ D-CACHE =============================== */ + +/* + * cpu_arm1022_dcache_invalidate_range(start, end) + * + * throw away all D-cached data in specified region without an obligation + * to write them back. Note however that we must clean the D-cached entries + * around the boundaries if the start and/or end address are not cache + * aligned. + * + * start: virtual start address + * end: virtual end address + */ + .align 5 +ENTRY(cpu_arm1022_dcache_invalidate_range) +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + bic r0, r0, #DCACHELINESIZE - 1 +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + tst r0, #DCACHELINESIZE - 1 + bic r0, r0, #DCACHELINESIZE - 1 + mcrne p15, 0, r0, c7, c10, 1 @ clean D entry at start + tst r1, #DCACHELINESIZE - 1 + mcrne p15, 0, r1, c7, c10, 1 @ clean D entry at end +#endif + +1: +#ifdef CONFIG_CPU_DCACHE_ENABLE + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +#endif +#ifdef CONFIG_CPU_ICACHE_ENABLE + mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry +#endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 + bls 1b + + /* Even if the D cache is off still drain the write buffer */ + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer + mov pc, lr + +/* + * cpu_arm1022_dcache_clean_range(start, end) + * + * For the specified virtual address range, ensure that all caches contain + * clean data, such that peripheral accesses to the physical RAM fetch + * correct data. + * + * start: virtual start address + * end: virtual end address + */ + .align 5 +ENTRY(cpu_arm1022_dcache_clean_range) + + mov r2, #0 + +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + bic r0, r0, #DCACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE + bhs cpu_arm1022_cache_clean_invalidate_all_r2 + +1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE + cmp r0, r1 + bls 1b +#endif + + mcr p15, 0, r2, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1022_dcache_clean_page(page) + * + * Cleans a single page of dcache so that if we have any future aliased + * mappings, they will be consistent at the time that they are created. + * + * page: virtual address of page to clean from dcache + * + * Note: + * we don't invalidate the entries since when we write the page + * out to disk, the entries may get reloaded into the cache. + */ + .align 5 +ENTRY(cpu_arm1022_dcache_clean_page) +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page + mov r0, r0, LSL #LOG2PAGESIZE + mov r1, #PAGESIZE +1: + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE + subs r1, r1, #DCACHELINESIZE + bne 1b +#endif + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1022_dcache_clean_entry(addr) + * + * Clean the specified entry of any caches such that the MMU + * translation fetches will obtain correct data. + * + * addr: cache-unaligned virtual address + */ + .align 5 +ENTRY(cpu_arm1022_dcache_clean_entry) +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + bic r0, r0, #DCACHELINESIZE - 1 + mcr p15, 0, r0, c7, c10, 1 @ clean single D entry +#endif + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + +/* ================================ I-CACHE =============================== */ + +/* + * cpu_arm1022_icache_invalidate_range(start, end) + * + * invalidate a range of virtual addresses from the Icache + * + * This is a little misleading, it is not intended to clean out + * the i-cache but to make sure that any data written to the + * range is made consistent. This means that when we execute code + * in that region, everything works as we expect. + * + * This generally means writing back data in the Dcache and + * write buffer and invalidating the Icache over that region + * + * start: virtual start address + * end: virtual end address + * + * NOTE: ICACHELINESIZE == DCACHELINESIZE (so we don't need to + * loop twice, once for i-cache, once for d-cache) + */ + .align 5 +ENTRY(cpu_arm1022_icache_invalidate_range) + bic r0, r0, #ICACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE + movhs r2, #1 + bhs cpu_arm1022_cache_clean_invalidate_all_r2 +1: +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c10, 1 @ Clean D entry +#endif +#ifdef CONFIG_CPU_ICACHE_ENABLE + mcr p15, 0, r0, c7, c5, 1 @ Invalidate I entry +#endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 + bls 1b @ unsigned lower or same - includes r1 entry + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB + mov pc, lr + +ENTRY(cpu_arm1022_icache_invalidate_page) + mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page + mov r0, r0, LSL #LOG2PAGESIZE + add r1, r0, #PAGESIZE + b cpu_arm1022_icache_invalidate_range + +/* ================================== TLB ================================= */ + +/* + * cpu_arm1022_tlb_invalidate_all() + * + * Invalidate all TLB entries + */ + .align 5 +ENTRY(cpu_arm1022_tlb_invalidate_all) + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB + mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D tlbs + mov pc, lr + +/* + * cpu_arm1022_tlb_invalidate_range(start, end) + * + * invalidate TLB entries covering the specified range + * + * start: range start address + * end: range end address + */ + .align 5 +ENTRY(cpu_arm1022_tlb_invalidate_range) + sub r3, r1, r0 + cmp r3, #256 * PAGESIZE + bhs cpu_arm1022_tlb_invalidate_all + mov r3, #0 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + mov r3, #PAGESIZE + sub r3, r3, #1 + bic r0, r0, r3 +1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry + add r0, r0, #PAGESIZE + cmp r0, r1 + bls 1b + mov pc, lr + +/* + * cpu_arm1022_tlb_invalidate_page(page, flags) + * + * invalidate the TLB entries for the specified page. + * + * page: page to invalidate + * flags: non-zero if we include the I TLB + */ + .align 5 +ENTRY(cpu_arm1022_tlb_invalidate_page) + mov r3, #0 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page + mov r0, r0, LSL #LOG2PAGESIZE + teq r1, #0 + mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry + mov pc, lr + +/* =============================== PageTable ============================== */ + +/* + * cpu_arm1022_set_pgd(pgd) + * + * Set the translation base pointer to be as described by pgd. + * + * pgd: new page tables + */ + .align 5 +ENTRY(cpu_arm1022_set_pgd) + stmfd sp!, {lr} + bl cpu_arm1022_cache_clean_invalidate_all @ preserves r0 + mov r1, #0 + mcr p15, 0, r0, c2, c0, 0 @ load page table pointer + mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs + ldmfd sp!, {pc} + +/* + * cpu_arm1022_set_pmd(pmdp, pmd) + * + * Set a level 1 translation table entry, and clean it out of + * any caches such that the MMUs can load it correctly. + * + * pmdp: pointer to PMD entry + * pmd: PMD value to store + */ + .align 5 +ENTRY(cpu_arm1022_set_pmd) +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + eor r2, r1, #0x0a @ C & Section + tst r2, #0x0b + biceq r1, r1, #4 @ clear bufferable bit +#endif + str r1, [r0] +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c10, 1 @ clean D entry +#endif + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB + mov pc, lr + +/* + * cpu_arm1022_set_pte(ptep, pte) + * + * Set a PTE and flush it out + */ + .align 5 +ENTRY(cpu_arm1022_set_pte) + str r1, [r0], #-1024 @ linux version + + eor r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY + + bic r2, r1, #0xff0 + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + + tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? + orreq r2, r2, #HPTE_AP_WRITE + + tst r1, #LPTE_PRESENT | LPTE_YOUNG @ Present and Young? + movne r2, #0 + +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + eor r3, r2, #0x0a @ C and Small Page? + tst r3, #0x0b @ if so.. + biceq r2, r2, #0x04 @ clear the bufferable bit +#endif + str r2, [r0] @ hardware version +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + mcr p15, 0, r0, c7, c10, 1 @ clean D entry +#endif + mov r1, #0 + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr + + +cpu_manu_name: + .asciz "ARM" +ENTRY(cpu_arm1022_name) + .ascii "Arm1022E" +#ifdef CONFIG_CPU_ICACHE_ENABLE + .ascii "i" +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + .ascii "d" +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + .ascii "(wt)" +#endif +#ifdef CONFIG_CPU_DCACHE_WRITEBACK + .ascii "(wb)" +#endif +#endif +#ifndef CONFIG_CPU_BPREDICT_DISABLE + .ascii "B" +#endif +#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN + .ascii "RR" +#endif + .ascii "\0" + .align + + .section ".text.init", #alloc, #execinstr + +__arm1022_setup: + mov r0, #0 + mcr p15, 0, r0, c7, c7, 0 @ invalidate I,D caches on v4 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 + mcr p15, 0, r0, c8, c7, 0 @ invalidate I,D TLBs on v4 + mcr p15, 0, r4, c2, c0, 0 @ load page table pointer + mov r0, #0x1f @ Domains 0, 1 = client + mcr p15, 0, r0, c3, c0, 0 @ load domain access register + + mrc p15, 0, r0, c1, c0, 0 @ Read current control register +/* + * The only thing worth keeping from the initial control register is the endian bit + */ + + and r0, r0, #0x0080 @ ........B....... Preserve endian bit, zero all others. + orr r0, r0, #0x0070 @ .........111.... Set the SBO (Should Be One) bits +/* + * Turn on what we want. + */ + orr r0, r0, #0x0001 @ ...............M Enable MMU (Alignment is special cased elsewhere) + orr r0, r0, #0x0100 @ .......S........ Enable system MMU protection + orr r0, r0, #0x2000 @ ..V............. Enable high vectors + +#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN + orr r0, r0, #0x4000 @ .R.............. Force round-robin replacement +#endif + +#ifndef CONFIG_CPU_BPREDICT_DISABLE + orr r0, r0, #0x0800 @ ....Z........... Enable branch prediction +#endif + +#ifdef CONFIG_CPU_DCACHE_ENABLE + orr r0, r0, #0x0004 @ .............C.. Enable D cache +#endif +#ifndef CONFIG_CPU_WB_DISABLE + orr r0, r0, #0x0008 @ ............W... Write Buffer enabled +#endif + +#ifdef CONFIG_CPU_ICACHE_ENABLE + orr r0, r0, #0x1000 @ ...I............ Enable I Cache +#endif + + mov pc, lr + + .text + +/* + * Purpose : Function pointers used to access above functions - all calls + * come through these + */ + .type arm1022_processor_functions, #object +arm1022_processor_functions: + .word cpu_arm1022_data_abort + .word cpu_arm1022_check_bugs + .word cpu_arm1022_proc_init + .word cpu_arm1022_proc_fin + .word cpu_arm1022_reset + .word cpu_arm1022_do_idle + + /* cache */ + .word cpu_arm1022_cache_clean_invalidate_all + .word cpu_arm1022_cache_clean_invalidate_range + .word cpu_arm1022_flush_ram_page + + /* dcache */ + .word cpu_arm1022_dcache_invalidate_range + .word cpu_arm1022_dcache_clean_range + .word cpu_arm1022_dcache_clean_page + .word cpu_arm1022_dcache_clean_entry + + /* icache */ + .word cpu_arm1022_icache_invalidate_range + .word cpu_arm1022_icache_invalidate_page + + /* tlb */ + .word cpu_arm1022_tlb_invalidate_all + .word cpu_arm1022_tlb_invalidate_range + .word cpu_arm1022_tlb_invalidate_page + + /* pgtable */ + .word cpu_arm1022_set_pgd + .word cpu_arm1022_set_pmd + .word cpu_arm1022_set_pte + .size arm1022_processor_functions, . - arm1022_processor_functions + + .type cpu_arm1022_info, #object +cpu_arm1022_info: + .long cpu_manu_name + .long cpu_arm1022_name + .size cpu_arm1022_info, . - cpu_arm1022_info + + .type cpu_arch_name, #object +cpu_arch_name: + .asciz "armv5t" + .size cpu_arch_name, . - cpu_arch_name + + .type cpu_elf_name, #object +cpu_elf_name: + .asciz "v5" + .size cpu_elf_name, . - cpu_elf_name + .align + + .section ".proc.info", #alloc, #execinstr + + .type __arm1022_proc_info,#object +__arm1022_proc_info: + .long 0x4100a220 @ ARM 1022 + .long 0xff00fff0 + .long 0x00000c1e @ mmuflags + b __arm1022_setup + .long cpu_arch_name + .long cpu_elf_name + .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB + .long cpu_arm1022_info + .long arm1022_processor_functions + .size __arm1022_proc_info, . - __arm1022_proc_info --- linux-2.4.27/arch/arm/mm/proc-arm1026.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-arm1026.S @@ -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 --- linux-2.4.27/arch/arm/mm/proc-arm6,7.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-arm6,7.S @@ -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? --- linux-2.4.27/arch/arm/mm/proc-arm720.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-arm720.S @@ -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? --- linux-2.4.27/arch/arm/mm/proc-arm920.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-arm920.S @@ -71,12 +71,16 @@ */ .align 5 ENTRY(cpu_arm920_data_abort) - mrc p15, 0, r3, c5, c0, 0 @ get FSR + mrc p15, 0, r1, c5, c0, 0 @ get FSR mrc p15, 0, r0, c6, c0, 0 @ get FAR - ldr r1, [r2] @ read aborted instruction - and r3, r3, #255 - tst r1, r1, lsr #21 @ C = bit 20 - sbc r1, r1, r1 @ r1 = C - 1 + + tst r3, #PSR_T_BIT + ldrneh r3, [r2] @ read aborted thumb instruction + ldreq r3, [r2] @ read aborted ARM instruction + bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR + movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 + tst r3, #1 << 20 @ check write + orreq r1, r1, #1 << 11 mov pc, lr /* @@ -186,10 +190,9 @@ .align 5 ENTRY(cpu_arm920_cache_clean_invalidate_range) bic r0, r0, #DCACHELINESIZE - 1 @ && added by PGM - bic r1, r1, #DCACHELINESIZE - 1 @ && added by DHM sub r3, r1, r0 cmp r3, #MAX_AREA_SIZE - bgt cpu_arm920_cache_clean_invalidate_all_r2 + bhi cpu_arm920_cache_clean_invalidate_all_r2 1: teq r2, #0 #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry @@ -207,7 +210,7 @@ add r0, r0, #DCACHELINESIZE #endif cmp r0, r1 - blt 1b + blo 1b mcr p15, 0, r1, c7, c10, 4 @ drain WB mov pc, lr @@ -253,18 +256,17 @@ */ .align 5 ENTRY(cpu_arm920_dcache_invalidate_range) -#ifndef CONFIG_CPU_ARM920_WRITETHROUGH +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH tst r0, #DCACHELINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry tst r1, #DCACHELINESIZE - 1 mcrne p15, 0, r1, c7, c10, 1 @ clean D entry #endif @ clean D entry bic r0, r0, #DCACHELINESIZE - 1 - bic r1, r1, #DCACHELINESIZE - 1 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry add r0, r0, #DCACHELINESIZE cmp r0, r1 - blt 1b + blo 1b mov pc, lr /* @@ -279,20 +281,17 @@ */ .align 5 ENTRY(cpu_arm920_dcache_clean_range) -#ifndef CONFIG_CPU_ARM920_WRITETHROUGH +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bic r0, r0, #DCACHELINESIZE - 1 sub r1, r1, r0 cmp r1, #MAX_AREA_SIZE mov r2, #0 - bgt cpu_arm920_cache_clean_invalidate_all_r2 - - bic r1, r1, #DCACHELINESIZE -1 - add r1, r1, #DCACHELINESIZE + bhi cpu_arm920_cache_clean_invalidate_all_r2 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #DCACHELINESIZE subs r1, r1, #DCACHELINESIZE - bpl 1b + bcs 1b #endif mcr p15, 0, r2, c7, c10, 4 @ drain WB mov pc, lr @@ -312,7 +311,7 @@ */ .align 5 ENTRY(cpu_arm920_dcache_clean_page) -#ifndef CONFIG_CPU_ARM920_WRITETHROUGH +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH mov r1, #PAGESIZE 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #DCACHELINESIZE @@ -333,7 +332,7 @@ */ .align 5 ENTRY(cpu_arm920_dcache_clean_entry) -#ifndef CONFIG_CPU_ARM920_WRITETHROUGH +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH mcr p15, 0, r0, c7, c10, 1 @ clean D entry #endif mcr p15, 0, r0, c7, c10, 4 @ drain WB @@ -365,16 +364,13 @@ bic r0, r0, #ICACHELINESIZE - 1 @ Safety check sub r1, r1, r0 cmp r1, #MAX_AREA_SIZE - bgt cpu_arm920_cache_clean_invalidate_all_r2 - - bic r1, r1, #ICACHELINESIZE - 1 - add r1, r1, #ICACHELINESIZE + bhi cpu_arm920_cache_clean_invalidate_all_r2 1: mcr p15, 0, r0, c7, c5, 1 @ Clean I entry mcr p15, 0, r0, c7, c10, 1 @ Clean D entry add r0, r0, #ICACHELINESIZE subs r1, r1, #ICACHELINESIZE - bne 1b + bcs 1b mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain WB @@ -418,13 +414,12 @@ mov r3, #PAGESIZE sub r3, r3, #1 bic r0, r0, r3 - bic r1, r1, r3 1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry add r0, r0, #PAGESIZE cmp r0, r1 - blt 1b + blo 1b mov pc, lr /* @@ -457,7 +452,6 @@ ENTRY(cpu_arm920_set_pgd) mov ip, #0 #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH - /* Any reason why we don't use mcr p15, 0, r0, c7, c7, 0 here? --rmk */ mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache #else @ && 'Clean & Invalidate whole DCache' @@ -514,7 +508,7 @@ bic r2, r2, #3 orr r2, r2, #HPTE_TYPE_SMALL - tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? + tst r1, #LPTE_USER @ User? orrne r2, r2, #HPTE_AP_READ tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? --- linux-2.4.27/arch/arm/mm/proc-arm922.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-arm922.S @@ -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? --- linux-2.4.27/arch/arm/mm/proc-arm925.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-arm925.S @@ -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? --- linux-2.4.27/arch/arm/mm/proc-arm926.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-arm926.S @@ -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? --- linux-2.4.27/arch/arm/mm/proc-sa110.S~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/mm/proc-sa110.S @@ -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? --- linux-2.4.27/arch/arm/tools/mach-types~2.4.27-vrs1 +++ linux-2.4.27/arch/arm/tools/mach-types @@ -6,7 +6,7 @@ # To add an entry into this database, please see Documentation/arm/README, # or contact rmk@arm.linux.org.uk # -# Last update: Sat Jun 28 12:10:54 2003 +# Last update: Mon Apr 19 21:11:35 2004 # # machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number # @@ -202,7 +202,7 @@ fester SA1100_FESTER FESTER 191 gpi ARCH_GPI GPI 192 smdk2410 ARCH_SMDK2410 SMDK2410 193 -premium ARCH_PREMIUM PREMIUM 194 +i519 ARCH_I519 I519 194 nexio SA1100_NEXIO NEXIO 195 bitbox SA1100_BITBOX BITBOX 196 g200 SA1100_G200 G200 197 @@ -228,7 +228,7 @@ arnold SA1100_ARNOLD ARNOLD 217 psiboard SA1100_PSIBOARD PSIBOARD 218 jz8028 ARCH_JZ8028 JZ8028 219 -h5400 ARCH_IPAQ3 IPAQ3 220 +h5400 ARCH_H5400 H5400 220 forte SA1100_FORTE FORTE 221 acam SA1100_ACAM ACAM 222 abox SA1100_ABOX ABOX 223 @@ -259,7 +259,7 @@ stork_egg ARCH_STORK_EGG STORK_EGG 248 wismo SA1100_WISMO WISMO 249 ezlinx ARCH_EZLINX EZLINX 250 -at91rm9200 ARCH_AT91 AT91 251 +at91rm9200 ARCH_AT91RM9200 AT91RM9200 251 orion ARCH_ORION ORION 252 neptune ARCH_NEPTUNE NEPTUNE 253 hackkit SA1100_HACKKIT HACKKIT 254 @@ -295,12 +295,12 @@ adsbitsyplus SA1100_ADSBITSYPLUS ADSBITSYPLUS 284 adsagc SA1100_ADSAGC ADSAGC 285 stp7312 ARCH_STP7312 STP7312 286 -nx_phnx ARCH_PXA255 PXA255 287 +nx_phnx MACH_NX_PHNX NX_PHNX 287 wep_ep250 ARCH_WEP_EP250 WEP_EP250 288 inhandelf3 ARCH_INHANDELF3 INHANDELF3 289 adi_coyote ARCH_ADI_COYOTE ADI_COYOTE 290 iyonix ARCH_IYONIX IYONIX 291 -damicam_sa1110 ARCH_DAMICAM_SA1110 DAMICAM_SA1110 292 +damicam1 ARCH_DAMICAM_SA1110 DAMICAM_SA1110 292 meg03 ARCH_MEG03 MEG03 293 pxa_whitechapel ARCH_PXA_WHITECHAPEL PXA_WHITECHAPEL 294 nwsc ARCH_NWSC NWSC 295 @@ -356,3 +356,172 @@ seedpxa_c2 ARCH_SEEDPXA_C2 SEEDPXA_C2 345 ixp4xx_mguardpci ARCH_IXP4XX_MGUARD_PCI IXP4XX_MGUARD_PCI 346 h1940 ARCH_H1940 H1940 347 +scorpio ARCH_SCORPIO SCORPIO 348 +viva ARCH_VIVA VIVA 349 +pxa_xcard ARCH_PXA_XCARD PXA_XCARD 350 +csb335 ARCH_CSB335 CSB335 351 +ixrd425 ARCH_IXRD425 IXRD425 352 +iq80315 ARCH_IQ80315 IQ80315 353 +nmp7312 ARCH_NMP7312 NMP7312 354 +cx861xx ARCH_CX861XX CX861XX 355 +enp2611 ARCH_ENP2611 ENP2611 356 +xda SA1100_XDA XDA 357 +csir_ims ARCH_CSIR_IMS CSIR_IMS 358 +ixp421_dnaeeth ARCH_IXP421_DNAEETH IXP421_DNAEETH 359 +pocketserv9200 ARCH_POCKETSERV9200 POCKETSERV9200 360 +toto ARCH_TOTO TOTO 361 +s3c2440 ARCH_S3C2440 S3C2440 362 +ks8695p ARCH_KS8695P KS8695P 363 +se4000 ARCH_SE4000 SE4000 364 +quadriceps ARCH_QUADRICEPS QUADRICEPS 365 +bronco ARCH_BRONCO BRONCO 366 +esl_wireless_tab ARCH_ESL_WIRELESS_TABLETESL_WIRELESS_TABLET 367 +esl_sofcomp ARCH_ESL_SOFCOMP ESL_SOFCOMP 368 +s5c7375 ARCH_S5C7375 S5C7375 369 +spearhead ARCH_SPEARHEAD SPEARHEAD 370 +pantera ARCH_PANTERA PANTERA 371 +prayoglite ARCH_PRAYOGLITE PRAYOGLITE 372 +gumstik ARCH_GUMSTIK GUMSTIK 373 +rcube ARCH_RCUBE RCUBE 374 +rea_olv ARCH_REA_OLV REA_OLV 375 +pxa_iphone ARCH_PXA_IPHONE PXA_IPHONE 376 +s3c3410 ARCH_S3C3410 S3C3410 377 +espd_4510b ARCH_ESPD_4510B ESPD_4510B 378 +mp1x ARCH_MP1X MP1X 379 +at91rm9200tb ARCH_AT91RM9200TB AT91RM9200TB 380 +adsvgx ARCH_ADSVGX ADSVGX 381 +omap_h2 ARCH_OMAP_H2 OMAP_H2 382 +pelee ARCH_PELEE PELEE 383 +e740 MACH_E740 E740 384 +iq80331 ARCH_IQ80331 IQ80331 385 +versatile_pb ARCH_VERSATILE_PB VERSATILE_PB 387 +kev7a400 MACH_KEV7A400 KEV7A400 388 +lpd7a400 MACH_LPD7A400 LPD7A400 389 +lpd7a404 MACH_LPD7A404 LPD7A404 390 +fujitsu_camelot ARCH_FUJITSU_CAMELOT FUJITSU_CAMELOT 391 +janus2m ARCH_JANUS2M JANUS2M 392 +embtf MACH_EMBTF EMBTF 393 +hpm MACH_HPM HPM 394 +smdk2410tk MACH_SMDK2410TK SMDK2410TK 395 +smdk2410aj MACH_SMDK2410AJ SMDK2410AJ 396 +streetracer MACH_STREETRACER STREETRACER 397 +eframe MACH_EFRAME EFRAME 398 +csb337 MACH_CSB337 CSB337 399 +pxa_lark MACH_PXA_LARK PXA_LARK 400 +pxa_pnp2110 MACH_PNP2110 PNP2110 401 +tcc72x MACH_TCC72X TCC72X 402 +altair MACH_ALTAIR ALTAIR 403 +kc3 MACH_KC3 KC3 404 +sinteftd MACH_SINTEFTD SINTEFTD 405 +mainstone MACH_MAINSTONE MAINSTONE 406 +aday4x MACH_ADAY4X ADAY4X 407 +lite300 MACH_LITE300 LITE300 408 +s5c7376 MACH_S5C7376 S5C7376 409 +mt02 MACH_MT02 MT02 410 +mport3s MACH_MPORT3S MPORT3S 411 +ra_alpha MACH_RA_ALPHA RA_ALPHA 412 +xcep MACH_XCEP XCEP 413 +arcom_mercury MACH_ARCOM_MERCURY ARCOM_MERCURY 414 +stargate MACH_STARGATE STARGATE 415 +armadilloj MACH_ARMADILLOJ ARMADILLOJ 416 +elroy_jack MACH_ELROY_JACK ELROY_JACK 417 +backend MACH_BACKEND BACKEND 418 +s5linbox MACH_S5LINBOX S5LINBOX 419 +nomadik MACH_NOMADIK NOMADIK 420 +ia_cpu_9200 MACH_IA_CPU_9200 IA_CPU_9200 421 +at91_bja1 MACH_AT91_BJA1 AT91_BJA1 422 +corgi MACH_CORGI CORGI 423 +poodle MACH_POODLE POODLE 424 +ten MACH_TEN TEN 425 +roverp5p MACH_ROVERP5P ROVERP5P 426 +sc2700 MACH_SC2700 SC2700 427 +ex_eagle MACH_EX_EAGLE EX_EAGLE 428 +nx_pxa12 MACH_NX_PXA12 NX_PXA12 429 +nx_pxa5 MACH_NX_PXA5 NX_PXA5 430 +blackboard2 MACH_BLACKBOARD2 BLACKBOARD2 431 +i819 MACH_I819 I819 432 +ixmb995e MACH_IXMB995E IXMB995E 433 +skyrider MACH_SKYRIDER SKYRIDER 434 +skyhawk MACH_SKYHAWK SKYHAWK 435 +enterprise MACH_ENTERPRISE ENTERPRISE 436 +dep2410 MACH_DEP2410 DEP2410 437 +armcore MACH_ARMCORE ARMCORE 438 +hobbit MACH_HOBBIT HOBBIT 439 +h7210 MACH_H7210 H7210 440 +pxa_netdcu5 MACH_PXA_NETDCU5 PXA_NETDCU5 441 +acc MACH_ACC ACC 442 +esl_sarva MACH_ESL_SARVA ESL_SARVA 443 +xm250 MACH_XM250 XM250 444 +t6tc1xb MACH_T6TC1XB T6TC1XB 445 +ess710 MACH_ESS710 ESS710 446 +mx3ads MACH_MX3ADS MX3ADS 447 +himalaya MACH_HIMALAYA HIMALAYA 448 +bolfenk MACH_BOLFENK BOLFENK 449 +at91rm9200kr MACH_AT91RM9200KR AT91RM9200KR 450 +edb9312 MACH_EDB9312 EDB9312 451 +omap_generic MACH_OMAP_GENERIC OMAP_GENERIC 452 +aximx3 MACH_AXIMX3 AXIMX3 453 +eb67xdip MACH_EB67XDIP EB67XDIP 454 +webtxs MACH_WEBTXS WEBTXS 455 +hawk MACH_HAWK HAWK 456 +ccat91sbc001 MACH_CCAT91SBC001 CCAT91SBC001 457 +expresso MACH_EXPRESSO EXPRESSO 458 +h4000 MACH_H4000 H4000 459 +dino MACH_DINO DINO 460 +ml675k MACH_ML675K ML675K 461 +edb9301 MACH_EDB9301 EDB9301 462 +edb9315 MACH_EDB9315 EDB9315 463 +reciva_tt MACH_RECIVA_TT RECIVA_TT 464 +cstcb01 MACH_CSTCB01 CSTCB01 465 +cstcb1 MACH_CSTCB1 CSTCB1 466 +shadwell MACH_SHADWELL SHADWELL 467 +goepel263 MACH_GOEPEL263 GOEPEL263 468 +acq100 MACH_ACQ100 ACQ100 469 +mx1fs2 MACH_MX1FS2 MX1FS2 470 +hiptop_g1 MACH_HIPTOP_G1 HIPTOP_G1 471 +sparky MACH_SPARKY SPARKY 472 +ns9750 MACH_NS9750 NS9750 473 +phoenix MACH_PHOENIX PHOENIX 474 +vr1000 MACH_VR1000 VR1000 475 +deisterpxa MACH_DEISTERPXA DEISTERPXA 476 +bcm1160 MACH_BCM1160 BCM1160 477 +pcm022 MACH_PCM022 PCM022 478 +adsgcx MACH_ADSGCX ADSGCX 479 +dreadnaught MACH_DREADNAUGHT DREADNAUGHT 480 +dm320 MACH_DM320 DM320 481 +markov MACH_MARKOV MARKOV 482 +cos7a400 MACH_COS7A400 COS7A400 483 +milano MACH_MILANO MILANO 484 +ue9328 MACH_UE9328 UE9328 485 +uex255 MACH_UEX255 UEX255 486 +ue2410 MACH_UE2410 UE2410 487 +a620 MACH_A620 A620 488 +ocelot MACH_OCELOT OCELOT 489 +cheetah MACH_CHEETAH CHEETAH 490 +omap_perseus2 MACH_OMAP_PERSEUS2 OMAP_PERSEUS2 491 +zvue MACH_ZVUE ZVUE 492 +roverp1 MACH_ROVERP1 ROVERP1 493 +asidial2 MACH_ASIDIAL2 ASIDIAL2 494 +s3c24a0 MACH_S3C24A0 S3C24A0 495 +e800 MACH_E800 E800 496 +e750 MACH_E750 E750 497 +s3c5500 MACH_S3C5500 S3C5500 498 +smdk5500 MACH_SMDK5500 SMDK5500 499 +signalsync MACH_SIGNALSYNC SIGNALSYNC 500 +nbc MACH_NBC NBC 501 +er4525 MACH_ER4525 ER4525 502 +netbookpro MACH_NETBOOKPRO NETBOOKPRO 503 +hw90200 MACH_HW90200 HW90200 504 +condor MACH_CONDOR CONDOR 505 +cup MACH_CUP CUP 506 +kite MACH_KITE KITE 507 +scb9328 MACH_SCB9328 SCB9328 508 +omap_h3 MACH_OMAP_H3 OMAP_H3 509 +omap_h4 MACH_OMAP_H4 OMAP_H4 510 +n10 MACH_N10 N10 511 +montajade MACH_MONTAJADE MONTAJADE 512 +sg560 MACH_SG560 SG560 513 +dp1000 MACH_DP1000 DP1000 514 +omap_osk MACH_OMAP_OSK OMAP_OSK 515 +rg100v3 MACH_RG100V3 RG100V3 516 +mx2ads MACH_MX2ADS MX2ADS 517 --- linux-2.4.27/arch/i386/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/i386/config.in @@ -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 --- linux-2.4.27/arch/i386/kernel/Makefile~2.4.27-vrs1 +++ linux-2.4.27/arch/i386/kernel/Makefile @@ -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 --- linux-2.4.27/arch/i386/kernel/apm.c~2.4.27-vrs1 +++ linux-2.4.27/arch/i386/kernel/apm.c @@ -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: --- linux-2.4.27/arch/i386/lib/Makefile~2.4.27-vrs1 +++ linux-2.4.27/arch/i386/lib/Makefile @@ -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 --- linux-2.4.27/arch/i386/math-emu/Makefile~2.4.27-vrs1 +++ linux-2.4.27/arch/i386/math-emu/Makefile @@ -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 \ --- linux-2.4.27/arch/ia64/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/ia64/config.in @@ -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 \ --- linux-2.4.27/arch/m68k/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/m68k/config.in @@ -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" --- linux-2.4.27/arch/mips/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/mips/config.in @@ -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 --- linux-2.4.27/arch/parisc/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/parisc/config.in @@ -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' --- linux-2.4.27/arch/ppc/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/ppc/config.in @@ -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" --- linux-2.4.27/arch/sh/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/sh/config.in @@ -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' --- linux-2.4.27/arch/sparc/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/sparc/config.in @@ -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' --- linux-2.4.27/arch/sparc64/config.in~2.4.27-vrs1 +++ linux-2.4.27/arch/sparc64/config.in @@ -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 --- linux-2.4.27/drivers/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/Makefile @@ -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 --- linux-2.4.27/drivers/acorn/char/i2c.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/acorn/char/i2c.c @@ -33,9 +33,13 @@ static struct i2c_client *rtc_client; static const unsigned char days_in_mon[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -static unsigned int rtc_epoch = 1900; #define CMOS_CHECKSUM (63) + +/* + * Acorn machines store the year in the static RAM at + * location 128. + */ #define CMOS_YEAR (64 + 128) static inline int rtc_command(int cmd, void *data) @@ -49,51 +53,91 @@ } /* + * Update the century + year bytes in the CMOS RAM, ensuring + * that the check byte is correctly adjusted for the change. + */ +static int rtc_update_year(unsigned int new_year) +{ + unsigned char yr[2], chk; + struct mem cmos_year = { CMOS_YEAR, sizeof(yr), yr }; + struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; + int ret; + + ret = rtc_command(MEM_READ, &cmos_check); + if (ret) + goto out; + ret = rtc_command(MEM_READ, &cmos_year); + if (ret) + goto out; + + chk -= yr[1] + yr[0]; + + yr[1] = new_year / 100; + yr[0] = new_year % 100; + + chk += yr[1] + yr[0]; + + ret = rtc_command(MEM_WRITE, &cmos_year); + if (ret == 0) + ret = rtc_command(MEM_WRITE, &cmos_check); + out: + return ret; +} + + +/* * Read the current RTC time and date, and update xtime. */ static void get_rtc_time(struct rtc_tm *rtctm, unsigned int *year) { unsigned char ctrl, yr[2]; struct mem rtcmem = { CMOS_YEAR, sizeof(yr), yr }; + int real_year, year_offset; /* * Ensure that the RTC is running. */ rtc_command(RTC_GETCTRL, &ctrl); if (ctrl & 0xc0) { - unsigned char new_ctrl; - - new_ctrl = ctrl & ~0xc0; + unsigned char new_ctrl = ctrl & ~0xc0; - printk("RTC: resetting control %02X -> %02X\n", - ctrl, new_ctrl); + printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n", + ctrl, new_ctrl); rtc_command(RTC_SETCTRL, &new_ctrl); } + if (rtc_command(RTC_GETDATETIME, rtctm) || + rtc_command(MEM_READ, &rtcmem)) + return; + + real_year = yr[0]; + /* - * Acorn machines store the year in - * the static RAM at location 192. + * The RTC year holds the LSB two bits of the current + * year, which should reflect the LSB two bits of the + * CMOS copy of the year. Any difference indicates + * that we have to correct the CMOS version. */ - if (rtc_command(MEM_READ, &rtcmem)) - return; + year_offset = rtctm->year_off - (real_year & 3); + if (year_offset < 0) + /* + * RTC year wrapped. Adjust it appropriately. + */ + year_offset += 4; - if (rtc_command(RTC_GETDATETIME, rtctm)) - return; + *year = real_year + year_offset + yr[1] * 100; - *year = yr[1] * 100 + yr[0]; } static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year) { - unsigned char yr[2], leap, chk; - struct mem cmos_year = { CMOS_YEAR, sizeof(yr), yr }; - struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; + unsigned char leap; int ret; leap = (!(year % 4) && (year % 100)) || !(year % 400); - if (rtctm->mon > 12 || rtctm->mday == 0) + if (rtctm->mon > 12 || rtctm->mon == 0 || rtctm->mday == 0) return -EINVAL; if (rtctm->mday > (days_in_mon[rtctm->mon] + (rtctm->mon == 2 && leap))) @@ -102,21 +146,16 @@ if (rtctm->hours >= 24 || rtctm->mins >= 60 || rtctm->secs >= 60) return -EINVAL; - ret = rtc_command(RTC_SETDATETIME, rtctm); - if (ret == 0) { - rtc_command(MEM_READ, &cmos_check); - rtc_command(MEM_READ, &cmos_year); - - chk -= yr[1] + yr[0]; - - yr[1] = year / 100; - yr[0] = year % 100; + /* + * The RTC's own 2-bit year must reflect the least + * significant two bits of the CMOS year. + */ + rtctm->year_off = (year % 100) & 3; - chk += yr[1] + yr[0]; + ret = rtc_command(RTC_SETDATETIME, rtctm); + if (ret == 0) + ret = rtc_update_year(year); - rtc_command(MEM_WRITE, &cmos_year); - rtc_command(MEM_WRITE, &cmos_check); - } return ret; } @@ -166,7 +205,6 @@ break; case RTC_RD_TIME: - memset(&rtctm, 0, sizeof(struct rtc_time)); get_rtc_time(&rtc_raw, &year); rtctm.tm_sec = rtc_raw.secs; rtctm.tm_min = rtc_raw.mins; @@ -188,13 +226,12 @@ rtc_raw.hours = rtctm.tm_hour; rtc_raw.mday = rtctm.tm_mday; rtc_raw.mon = rtctm.tm_mon + 1; - rtc_raw.year_off = 2; year = rtctm.tm_year + 1900; return set_rtc_time(&rtc_raw, year); break; case RTC_EPOCH_READ: - return put_user(rtc_epoch, (unsigned long *)arg); + return put_user(1900, (unsigned long *)arg); } return -EINVAL; --- linux-2.4.27/drivers/acorn/net/ether1.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/acorn/net/ether1.c @@ -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)); --- linux-2.4.27/drivers/acorn/net/ether3.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/acorn/net/ether3.c @@ -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 }, --- linux-2.4.27/drivers/acorn/net/etherh.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/acorn/net/etherh.c @@ -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 }, --- linux-2.4.27/drivers/acorn/scsi/cumana_1.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/acorn/scsi/cumana_1.c @@ -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); --- linux-2.4.27/drivers/acorn/scsi/ecoscsi.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/acorn/scsi/ecoscsi.c @@ -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); --- linux-2.4.27/drivers/acorn/scsi/oak.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/acorn/scsi/oak.c @@ -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); --- /dev/null +++ linux-2.4.27/drivers/at91/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/i2c/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/i2c/at91_i2c.c @@ -0,0 +1,257 @@ +/* + i2c Support for Atmel's AT91RM9200 Two-Wire Interface + + (c) Rick Bronson + + Borrowed heavily from original work by: + Copyright (c) 2000 Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "at91_i2c.h" + +#define DBG(x...) do {\ + if (debug > 0) \ + printk(KERN_DEBUG "i2c:" x); \ + } while(0) + +int debug = 0; + +static struct at91_i2c_local *at91_i2c_device; + +/* + * Poll the i2c status register until the specified bit is set. + * Returns 0 if timed out (100 msec) + */ +static short at91_poll_status(AT91PS_TWI twi, unsigned long bit) { + int loop_cntr = 10000; + do { + udelay(10); + } while (!(twi->TWI_SR & bit) && (--loop_cntr > 0)); + + return (loop_cntr > 0); +} + +/* + * Generic i2c master transfer entrypoint + */ +static int at91_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct at91_i2c_local *device = (struct at91_i2c_local *) adap->data; + AT91PS_TWI twi = (AT91PS_TWI) device->base_addr; + + struct i2c_msg *pmsg; + int length; + unsigned char *buf; + + /* + * i2c_smbus_xfer_emulated() in drivers/i2c/i2c-core.c states: + * "... In the case of writing, we need to use only one message; + * when reading, we need two..." + */ + + pmsg = msgs; /* look at 1st message, it contains the address/command */ + if (num >= 1 && num <= 2) { + DBG("xfer: doing %s %d bytes to 0x%02x - %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->buf[0], num); + + /* Set the TWI Master Mode Register */ + twi->TWI_MMR = (pmsg->addr << 16) | (pmsg->len << 8) + | ((pmsg + 1)->flags & I2C_M_RD ? AT91C_TWI_MREAD : 0); + + /* Set TWI Internal Address Register with first messages data field */ + if (pmsg->len == 1) + twi->TWI_IADR = pmsg->buf[0]; + else if (pmsg->len == 2) + twi->TWI_IADR = pmsg->buf[0] << 8 | pmsg->buf[1]; + else /* must be 3 */ + twi->TWI_IADR = pmsg->buf[0] << 16 | pmsg->buf[1] << 8 | pmsg->buf[2]; + + /* 1st message contains the address/command */ + if (num > 1) + pmsg++; /* go to real message */ + + length = pmsg->len; + buf = pmsg->buf; + if (length && buf) { /* sanity check */ + if (pmsg->flags & I2C_M_RD) { + twi->TWI_CR = AT91C_TWI_START; + while (length--) { + if (!length) + twi->TWI_CR = AT91C_TWI_STOP; + /* Wait until transfer is finished */ + if (!at91_poll_status(twi, AT91C_TWI_RXRDY)) { + printk(KERN_ERR "at91_i2c: timeout 1\n"); + return 0; + } + *buf++ = twi->TWI_RHR; + } + if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) { + printk(KERN_ERR "at91_i2c: timeout 2\n"); + return 0; + } + } else { + twi->TWI_CR = AT91C_TWI_START; + while (length--) { + twi->TWI_THR = *buf++; + if (!length) + twi->TWI_CR = AT91C_TWI_STOP; + if (!at91_poll_status(twi, AT91C_TWI_TXRDY)) { + printk(KERN_ERR "at91_i2c: timeout 3\n"); + return 0; + } + } + /* Wait until transfer is finished */ + if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) { + printk(KERN_ERR "at91_i2c: timeout 4\n"); + return 0; + } + } + } + DBG("transfer complete\n"); + return num; + } + else { + printk(KERN_ERR "at91_i2c: unexpected number of messages: %d\n", num); + return 0; + } +} + +/* + * Return list of supported functionality + */ +static u32 at91_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE + | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +/* + * Open + */ +static void at91_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +/* + * Close + */ +static void at91_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +/* For now, we only handle combined mode (smbus) */ +static struct i2c_algorithm at91_algorithm = { + name:"at91 i2c", + id:I2C_ALGO_SMBUS, + master_xfer:at91_xfer, + functionality:at91_func, +}; + +/* + * Main initialization routine + */ +static int __init i2c_at91_init(void) +{ + AT91PS_TWI twi = (AT91PS_TWI) AT91C_VA_BASE_TWI; + struct at91_i2c_local *device; + int rc; + + AT91_CfgPIO_TWI(); + AT91_SYS->PMC_PCER = 1 << AT91C_ID_TWI; /* enable peripheral clock */ + + twi->TWI_IDR = 0x3ff; /* Disable all interrupts */ + twi->TWI_CR = AT91C_TWI_SWRST; /* Reset peripheral */ + twi->TWI_CR = AT91C_TWI_MSEN | AT91C_TWI_SVDIS; /* Set Master mode */ + + /* Here, CKDIV = 1 and CHDIV=CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6) */ + twi->TWI_CWGR = AT91C_TWI_CKDIV1 | AT91C_TWI_CLDIV3 | (AT91C_TWI_CLDIV3 << 8); + + device = (struct at91_i2c_local *) kmalloc(sizeof(struct at91_i2c_local), GFP_KERNEL); + if (device == NULL) { + printk(KERN_ERR "at91_i2c: can't allocate inteface!\n"); + return -ENOMEM; + } + memset(device, 0, sizeof(struct at91_i2c_local)); + at91_i2c_device = device; + + sprintf(device->adapter.name, "AT91RM9200"); + device->adapter.data = (void *) device; + device->adapter.id = I2C_ALGO_SMBUS; + device->adapter.algo = &at91_algorithm; + device->adapter.algo_data = NULL; + device->adapter.inc_use = at91_inc; + device->adapter.dec_use = at91_dec; + device->adapter.client_register = NULL; + device->adapter.client_unregister = NULL; + device->base_addr = AT91C_VA_BASE_TWI; + + rc = i2c_add_adapter(&device->adapter); + if (rc) { + printk(KERN_ERR "at91_i2c: Adapter %s registration failed\n", device->adapter.name); + device->adapter.data = NULL; + kfree(device); + } + else + printk(KERN_INFO "Found AT91 i2c\n"); + return rc; +} + +/* + * Clean up routine + */ +static void __exit i2c_at91_cleanup(void) +{ + struct at91_i2c_local *device = at91_i2c_device; + int rc; + + rc = i2c_del_adapter(&device->adapter); + device->adapter.data = NULL; + kfree(device); + + AT91_SYS->PMC_PCDR = 1 << AT91C_ID_TWI; /* disable peripheral clock */ + + /* We aren't that prepared to deal with this... */ + if (rc) + printk(KERN_ERR "at91_i2c: i2c_del_adapter failed (%i), that's bad!\n", rc); +} + +module_init(i2c_at91_init); +module_exit(i2c_at91_cleanup); + +MODULE_AUTHOR("Rick Bronson"); +MODULE_DESCRIPTION("I2C driver for Atmel AT91RM9200"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); + +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/at91/i2c/at91_i2c.h @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/mtd/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/mtd/at91_dataflash.c @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include "at91_dataflash.h" +#include "../spi/at91_spi.h" + +#undef DEBUG_DATAFLASH + +/* Detected DataFlash devices */ +static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; +static int nr_devices = 0; + +/* ......................................................................... */ + +#ifdef CONFIG_MTD_PARTITIONS + +static struct mtd_partition *mtd_parts = 0; +static int mtd_parts_nr = 0; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +static struct mtd_partition static_partitions[] = +{ + { + name: "bootloader", + offset: 0, + size: 64 * 1024, /* 64 Kb */ + mask_flags: MTD_WRITEABLE /* read-only */ + }, + { + name: "kernel", + offset: MTDPART_OFS_NXTBLK, + size: 768 *1024, /* 768 Kb */ + }, + { + name: "filesystem", + offset: MTDPART_OFS_NXTBLK, + size: MTDPART_SIZ_FULL, + } +}; + +int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, const char *mtd_id); + +#endif + +/* ......................................................................... */ + +/* Allocate a single SPI transfer descriptor. We're assuming that if multiple + SPI transfers occur at the same time, spi_access_bus() will serialize them. + If this is not valid, then either (i) each dataflash 'priv' structure + needs it's own transfer descriptor, (ii) we lock this one, or (iii) use + another mechanism. */ +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") --- /dev/null +++ linux-2.4.27/drivers/at91/mtd/at91_dataflash.h @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/mtd/at91_nand.c @@ -0,0 +1,328 @@ +/* + * drivers/at91/mtd/at91_nand.c + * + * Copyright (c) 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "at91_nand.h" + +/* + * MTD structure for AT91 board + */ +static struct mtd_info *at91_mtd = NULL; +static struct nand_chip *my_nand_chip = NULL; + +static int at91_fio_base; + +#ifdef CONFIG_MTD_PARTITIONS + +/* + * Define partitions for flash devices + */ + +static struct mtd_partition partition_info32k[] = { + { name: "AT91 NAND partition 1, kernel", + offset: 0, + size: 1 * SZ_1M }, + { name: "AT91 NAND partition 2, filesystem", + offset: 1 * SZ_1M, + size: 16 * SZ_1M }, + { name: "AT91 NAND partition 3a, storage", + offset: (1 * SZ_1M) + (16 * SZ_1M), + size: 1 * SZ_1M }, + { name: "AT91 NAND partition 3b, storage", + offset: (2 * SZ_1M) + (16 * SZ_1M), + size: 1 * SZ_1M }, + { name: "AT91 NAND partition 3c, storage", + offset: (3 * SZ_1M) + (16 * SZ_1M), + size: 1 * SZ_1M }, + { name: "AT91 NAND partition 3d, storage", + offset: (4 * SZ_1M) + (16 * SZ_1M), + size: 1 * SZ_1M }, +}; + +static struct mtd_partition partition_info64k[] = { + { name: "AT91 NAND partition 1, kernel", + offset: 0, + size: 1 * SZ_1M }, + { name: "AT91 NAND partition 2, filesystem", + offset: 1 * SZ_1M, + size: 16 * SZ_1M }, + { name: "AT91 NAND partition 3, storage", + offset: (1 * SZ_1M) + (16 * SZ_1M), + size: 47 * SZ_1M }, +}; + +#endif + +/* + * Hardware specific access to control-lines + */ +static void at91_hwcontrol(int cmd) +{ + struct nand_chip *my_nand = my_nand_chip; + switch(cmd) + { + case NAND_CTL_SETCLE: + my_nand->IO_ADDR_W = at91_fio_base + AT91_SMART_MEDIA_CLE; + break; + case NAND_CTL_CLRCLE: + my_nand->IO_ADDR_W = at91_fio_base; + break; + case NAND_CTL_SETALE: + my_nand->IO_ADDR_W = at91_fio_base + AT91_SMART_MEDIA_ALE; + break; + case NAND_CTL_CLRALE: + my_nand->IO_ADDR_W = at91_fio_base; + break; + case NAND_CTL_SETNCE: + break; + case NAND_CTL_CLRNCE: + break; + } +} + +/* + * Send command to NAND device + */ +static void at91_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *my_nand = mtd->priv; + + /* Begin command latch cycle */ + register unsigned long NAND_IO_ADDR = my_nand->IO_ADDR_W + AT91_SMART_MEDIA_CLE; + + /* + * Write out the command to the device. + */ + if (command != NAND_CMD_SEQIN) + writeb (command, NAND_IO_ADDR); + else { + if (mtd->oobblock == 256 && column >= 256) { + column -= 256; + writeb (NAND_CMD_RESET, NAND_IO_ADDR); + writeb (NAND_CMD_READOOB, NAND_IO_ADDR); + writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); + } + else + if (mtd->oobblock == 512 && column >= 256) { + if (column < 512) { + column -= 256; + writeb (NAND_CMD_READ1, NAND_IO_ADDR); + writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); + } else { + column -= 512; + writeb (NAND_CMD_READOOB, NAND_IO_ADDR); + writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); + } + } else { + writeb (NAND_CMD_READ0, NAND_IO_ADDR); + writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); + } + } + + /* Set ALE and clear CLE to start address cycle */ + NAND_IO_ADDR = at91_fio_base; + + if (column != -1 || page_addr != -1) + NAND_IO_ADDR += AT91_SMART_MEDIA_ALE; + + /* Serially input address */ + if (column != -1) + writeb (column, NAND_IO_ADDR); + if (page_addr != -1) { + writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR); + writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR); + /* One more address cycle for higher density devices */ + if (mtd->size & 0x0c000000) { + writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR); + } + } + + /* wait until command is processed */ + while (!my_nand->dev_ready()) + ; +} + +/* + * Read the Device Ready pin. + */ +static int at91_device_ready(void) +{ + return AT91_PIO_SmartMedia_RDY(); +} +/* + * Main initialization routine + */ +static int __init at91_init (void) +{ + struct nand_chip *my_nand; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + at91_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); + if (!at91_mtd) { + printk ("Unable to allocate AT91 NAND MTD device structure.\n"); + err = -ENOMEM; + goto out; + } + + /* map physical adress */ + at91_fio_base = (unsigned long) ioremap(AT91_SMARTMEDIA_BASE, SZ_8M); + if(!at91_fio_base) { + printk("ioremap AT91 NAND failed\n"); + err = -EIO; + goto out_mtd; + } + + /* Get pointer to private data */ + my_nand_chip = my_nand = (struct nand_chip *) (&at91_mtd[1]); + + /* Initialize structures */ + memset((char *) at91_mtd, 0, sizeof(struct mtd_info)); + memset((char *) my_nand, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + at91_mtd->priv = my_nand; + + /* Set address of NAND IO lines */ + my_nand->IO_ADDR_R = at91_fio_base; + my_nand->IO_ADDR_W = at91_fio_base; + my_nand->hwcontrol = at91_hwcontrol; + my_nand->dev_ready = at91_device_ready; + my_nand->cmdfunc = at91_nand_command; /* we need our own */ + my_nand->eccmode = NAND_ECC_SOFT; /* enable ECC */ + /* 20 us command delay time */ + my_nand->chip_delay = 20; + + /* Setup Smart Media, first enable the address range of CS3 */ + AT91_SYS->EBI_CSA |= AT91C_EBI_CS3A_SMC_SmartMedia; + /* set the bus interface characteristics based on + tDS Data Set up Time 30 - ns + tDH Data Hold Time 20 - ns + tALS ALE Set up Time 20 - ns + 16ns at 60 MHz ~= 3 */ +#define AT91C_SM_ID_RWH (5 << 28) /* orig = 5 */ +#define AT91C_SM_RWH (1 << 28) /* orig = 1 */ +#define AT91C_SM_RWS (0 << 24) /* orig = 0 */ +#define AT91C_SM_TDF (1 << 8) /* orig = 1 */ +#define AT91C_SM_NWS (5) /* orig = 3 */ + AT91_SYS->EBI_SMC2_CSR[3] = ( AT91C_SM_RWH | AT91C_SM_RWS | + AT91C_SMC2_ACSS_STANDARD | + AT91C_SMC2_DBW_8 | AT91C_SM_TDF | + AT91C_SMC2_WSEN | AT91C_SM_NWS); + + AT91_CfgPIO_SmartMedia(); + + if (AT91_PIO_SmartMedia_CardDetect()) + printk ("No "); + printk ("SmartMedia card inserted.\n"); + + /* Scan to find existance of the device */ + if (nand_scan (at91_mtd)) { + err = -ENXIO; + goto out_ior; + } + + /* Allocate memory for internal data buffer */ + my_nand->data_buf = kmalloc (sizeof(u_char) * (at91_mtd->oobblock + at91_mtd->oobsize), GFP_KERNEL); + if (!my_nand->data_buf) { + printk ("Unable to allocate AT91 NAND data buffer.\n"); + err = -ENOMEM; + goto out_ior; + } + + /* Allocate memory for internal data buffer */ + my_nand->data_cache = kmalloc (sizeof(u_char) * (at91_mtd->oobblock + at91_mtd->oobsize), GFP_KERNEL); + if (!my_nand->data_cache) { + printk ("Unable to allocate AT91 NAND data cache.\n"); + err = -ENOMEM; + goto out_buf; + } + my_nand->cache_page = -1; + +#ifdef CONFIG_MTD_PARTITIONS + /* Register the partitions */ + switch(at91_mtd->size) + { + case SZ_32M: + err = add_mtd_partitions(at91_mtd, partition_info32k, + ARRAY_SIZE (partition_info32k)); + break; + case SZ_64M: + err = add_mtd_partitions(at91_mtd, partition_info64k, + ARRAY_SIZE (partition_info64k)); + break; + default: + printk ("Unsupported SmartMedia device\n"); + err = -ENXIO; + goto out_cac; + } +#else + err = add_mtd_device(at91_mtd); +#endif + goto out; + + out_cac: + kfree (my_nand->data_cache); + out_buf: + kfree (my_nand->data_buf); + out_ior: + iounmap((void *)at91_fio_base); + out_mtd: + kfree (at91_mtd); + out: + return err; +} + +/* + * Clean up routine + */ +static void __exit at91_cleanup (void) +{ + struct nand_chip *my_nand = (struct nand_chip *) &at91_mtd[1]; + + /* Unregister partitions */ + del_mtd_partitions(at91_mtd); + + /* Unregister the device */ + del_mtd_device (at91_mtd); + + /* Free internal data buffers */ + kfree (my_nand->data_buf); + kfree (my_nand->data_cache); + + /* unmap physical adress */ + iounmap((void *)at91_fio_base); + + /* Free the MTD device structure */ + kfree (at91_mtd); +} + +module_init(at91_init); +module_exit(at91_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rick Bronson"); +MODULE_DESCRIPTION("Glue layer for SmartMediaCard on ATMEL AT91RM9200"); --- /dev/null +++ linux-2.4.27/drivers/at91/mtd/at91_nand.h @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/net/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/net/at91_ether.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "at91_ether.h" + +static struct net_device at91_dev; + +/* ........................... PHY INTERFACE ........................... */ + +/* + * Enable the MDIO bit in MAC control register + * When not called from an interrupt-handler, access to the PHY must be + * protected by a spinlock. + */ +static void enable_mdi(AT91PS_EMAC regs) +{ + regs->EMAC_CTL |= AT91C_EMAC_MPE; /* enable management port */ +} + +/* + * Disable the MDIO bit in the MAC control register + */ +static void disable_mdi(AT91PS_EMAC regs) +{ + regs->EMAC_CTL &= ~AT91C_EMAC_MPE; /* disable management port */ +} + +/* + * Write value to the a PHY register + * Note: MDI interface is assumed to already have been enabled. + */ +static void write_phy(AT91PS_EMAC regs, unsigned char phy_addr, unsigned char address, unsigned int value) +{ + regs->EMAC_MAN = (AT91C_EMAC_HIGH | AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_W + | ((phy_addr & 0x1f) << 23) | (address << 18)) + (value & 0xffff); + + /* Wait until IDLE bit in Network Status register is cleared */ + // TODO: Enforce some maximum loop-count? + while (!(regs->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); } +} + +/* + * Read value stored in a PHY register. + * Note: MDI interface is assumed to already have been enabled. + */ +static void read_phy(AT91PS_EMAC regs, unsigned char phy_addr, unsigned char address, unsigned int *value) +{ + regs->EMAC_MAN = AT91C_EMAC_HIGH | AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_R + | ((phy_addr & 0x1f) << 23) | (address << 18); + + /* Wait until IDLE bit in Network Status register is cleared */ + // TODO: Enforce some maximum loop-count? + while (!(regs->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); } + + *value = (regs->EMAC_MAN & 0x0000ffff); +} + +/* ........................... PHY MANAGEMENT .......................... */ + +/* + * Access the PHY to determine the current Link speed and Mode, and update the + * MAC accordingly. + * If no link or auto-negotiation is busy, then no changes are made. + * Returns: 0 : OK + * -1 : No link + * -2 : AutoNegotiation still in progress + */ +static int update_linkspeed(struct net_device *dev, AT91PS_EMAC regs) { + unsigned int bmsr, bmcr, lpa, mac_cfg; + unsigned int speed, duplex; + + /* Link status is latched, so read twice to get current value */ + read_phy(regs, 0, MII_BMSR, &bmsr); + read_phy(regs, 0, MII_BMSR, &bmsr); + if (!(bmsr & BMSR_LSTATUS)) return -1; /* no link */ + + read_phy(regs, 0, MII_BMCR, &bmcr); + if (bmcr & BMCR_ANENABLE) { /* AutoNegotiation is enabled */ + if (!(bmsr & BMSR_ANEGCOMPLETE)) return -2; /* auto-negotitation in progress */ + + read_phy(regs, 0, MII_LPA, &lpa); + if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) speed = SPEED_100; + else speed = SPEED_10; + if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) duplex = DUPLEX_FULL; + else duplex = DUPLEX_HALF; + } else { + speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; + duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; + } + + /* Update the MAC */ + mac_cfg = regs->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); + if (speed == SPEED_100) { + if (duplex == DUPLEX_FULL) /* 100 Full Duplex */ + regs->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD | AT91C_EMAC_FD; + else /* 100 Half Duplex */ + regs->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD; + } else { + if (duplex == DUPLEX_FULL) /* 10 Full Duplex */ + regs->EMAC_CFG = mac_cfg | AT91C_EMAC_FD; + else /* 10 Half Duplex */ + regs->EMAC_CFG = mac_cfg; + } + + printk(KERN_INFO "%s: Link now %i-%s\n", dev->name, speed, (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); + return 0; +} + +/* + * Handle interrupts from the PHY + */ +static void at91ether_phy_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct at91_private *lp = (struct at91_private *) dev->priv; + AT91PS_EMAC emac = (AT91PS_EMAC) dev->base_addr; + int status; + unsigned int phy; + + enable_mdi(emac); + if (lp->phy_type == MII_DM9161_ID) + read_phy(emac, 0, MII_DSINTR_REG, &phy); /* ack interrupt in Davicom PHY */ + else if (lp->phy_type == MII_LXT971A_ID) + read_phy(emac, 0, MII_ISINTS_REG, &phy); /* ack interrupt in Intel PHY */ + + status = AT91_SYS->PIOC_ISR; /* acknowledge interrupt in PIO */ + + status = update_linkspeed(dev, emac); + if (status == -1) { /* link is down */ + netif_carrier_off(dev); + printk(KERN_INFO "%s: Link down.\n", dev->name); + } else if (status == -2) { /* auto-negotiation in progress */ + /* Do nothing - another interrupt generated when negotiation complete */ + } else { /* link is operational */ + netif_carrier_on(dev); + } + disable_mdi(emac); +} + +/* + * Initialize and enable the PHY interrupt when link-state changes + */ +static void enable_phyirq(struct net_device *dev, AT91PS_EMAC regs) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned int dsintr, status; + + // TODO: Check error code. Really need a generic PIO (interrupt) + // layer since we're really only interested in the PC4 (DK) or PC2 (CSB337) line. + (void) request_irq(AT91C_ID_PIOC, at91ether_phy_interrupt, 0, dev->name, dev); + + status = AT91_SYS->PIOC_ISR; /* clear any pending PIO interrupts */ +#ifdef CONFIG_MACH_CSB337 + AT91_SYS->PIOC_IER = AT91C_PIO_PC2; /* Enable interrupt */ +#else + AT91_SYS->PIOC_IER = AT91C_PIO_PC4; /* Enable interrupt */ +#endif + + spin_lock_irq(&lp->lock); + enable_mdi(regs); + + if (lp->phy_type == MII_DM9161_ID) { /* for Davicom PHY */ + read_phy(regs, 0, MII_DSINTR_REG, &dsintr); + dsintr = dsintr & ~0xf00; /* clear bits 8..11 */ + write_phy(regs, 0, MII_DSINTR_REG, dsintr); + } + else if (lp->phy_type == MII_LXT971A_ID) { /* for Intel PHY */ + read_phy(regs, 0, MII_ISINTE_REG, &dsintr); + dsintr = dsintr | 0xf2; /* set bits 1, 4..7 */ + write_phy(regs, 0, MII_ISINTE_REG, dsintr); + } + + disable_mdi(regs); + spin_unlock_irq(&lp->lock); +} + +/* + * Disable the PHY interrupt + */ +static void disable_phyirq(struct net_device *dev, AT91PS_EMAC regs) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned int dsintr; + + spin_lock_irq(&lp->lock); + enable_mdi(regs); + + if (lp->phy_type == MII_DM9161_ID) { /* for Davicom PHY */ + read_phy(regs, 0, MII_DSINTR_REG, &dsintr); + dsintr = dsintr | 0xf00; /* set bits 8..11 */ + write_phy(regs, 0, MII_DSINTR_REG, dsintr); + } + else if (lp->phy_type == MII_LXT971A_ID) { /* for Intel PHY */ + read_phy(regs, 0, MII_ISINTE_REG, &dsintr); + dsintr = dsintr & ~0xf2; /* clear bits 1, 4..7 */ + write_phy(regs, 0, MII_ISINTE_REG, dsintr); + } + + disable_mdi(regs); + spin_unlock_irq(&lp->lock); + +#ifdef CONFIG_MACH_CSB337 + AT91_SYS->PIOC_IDR = AT91C_PIO_PC2; /* Disable interrupt */ +#else + AT91_SYS->PIOC_IDR = AT91C_PIO_PC4; /* Disable interrupt */ +#endif + free_irq(AT91C_ID_PIOC, dev); /* Free interrupt handler */ +} + +/* + * Perform a software reset of the PHY. + */ +static void reset_phy(struct net_device *dev, AT91PS_EMAC regs) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + unsigned int bmcr; + + spin_lock_irq(&lp->lock); + enable_mdi(regs); + + /* Perform PHY reset */ + write_phy(regs, 0, MII_BMCR, BMCR_RESET); + + /* Wait until PHY reset is complete */ + do { + read_phy(regs, 0, MII_BMCR, &bmcr); + } while (!(bmcr && BMCR_RESET)); + + disable_mdi(regs); + spin_unlock_irq(&lp->lock); +} + + +/* ......................... ADDRESS MANAGEMENT ........................ */ + +/* + * Set the ethernet MAC address in dev->dev_addr + */ +static void get_mac_address(struct net_device *dev) { + AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; + char addr[6]; + unsigned int hi, lo; + + /* Check if bootloader set address in Specific-Address 1 */ + hi = regs->EMAC_SA1H; + lo = regs->EMAC_SA1L; + addr[0] = (lo & 0xff); + addr[1] = (lo & 0xff00) >> 8; + addr[2] = (lo & 0xff0000) >> 16; + addr[3] = (lo & 0xff000000) >> 24; + addr[4] = (hi & 0xff); + addr[5] = (hi & 0xff00) >> 8; + + if (is_valid_ether_addr(addr)) { + memcpy(dev->dev_addr, &addr, 6); + return; + } + + /* Check if bootloader set address in Specific-Address 2 */ + hi = regs->EMAC_SA2H; + lo = regs->EMAC_SA2L; + addr[0] = (lo & 0xff); + addr[1] = (lo & 0xff00) >> 8; + addr[2] = (lo & 0xff0000) >> 16; + addr[3] = (lo & 0xff000000) >> 24; + addr[4] = (hi & 0xff); + addr[5] = (hi & 0xff00) >> 8; + + if (is_valid_ether_addr(addr)) { + memcpy(dev->dev_addr, &addr, 6); + return; + } +} + +/* + * Program the hardware MAC address from dev->dev_addr. + */ +static void update_mac_address(struct net_device *dev) +{ + AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; + + regs->EMAC_SA1L = (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) | (dev->dev_addr[1] << 8) | (dev->dev_addr[0]); + regs->EMAC_SA1H = (dev->dev_addr[5] << 8) | (dev->dev_addr[4]); +} + +/* + * 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"); --- /dev/null +++ linux-2.4.27/drivers/at91/net/at91_ether.h @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/rtc/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/rtc/at91_rtc.c @@ -0,0 +1,441 @@ +/* + * Real Time Clock interface for Linux on Atmel AT91RM9200 + * + * Copyright (c) 2002 Rick Bronson + * + * Based on sa1100-rtc.c by Nils Faerber + * Based on rtc.c by Paul Gortmaker + * Date/time conversion routines taken from arch/arm/kernel/time.c + * by Linus Torvalds and Russell King + * and the GNU C Library + * ( ... I love the GPL ... just take what you need! ;) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AT91_RTC_FREQ 1 +#define EPOCH 1970 + +/* Those are the bits from a classic RTC we want to mimic */ +#define AT91_RTC_IRQF 0x80 /* any of the following 3 is active */ +#define AT91_RTC_PF 0x40 +#define AT91_RTC_AF 0x20 +#define AT91_RTC_UF 0x10 + +#define BCD2BIN(val) (((val)&15) + ((val)>>4)*10) +#define BIN2BCD(val) ((((val)/10)<<4) + (val)%10) + +static unsigned long rtc_status = 0; +static unsigned long rtc_irq_data; +static unsigned int at91_alarm_year = EPOCH; + +static struct fasync_struct *at91_rtc_async_queue; +static DECLARE_WAIT_QUEUE_HEAD(at91_rtc_wait); +static DECLARE_WAIT_QUEUE_HEAD(at91_rtc_update); +static spinlock_t at91_rtc_updlock; /* some spinlocks for saving/restoring interrupt levels */ +extern spinlock_t at91_rtc_lock; + +static const unsigned char days_in_mo[] = + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +#define is_leap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + +static const unsigned short int __mon_yday[2][13] = +{ + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +/* + * Returns day since start of the year [0-365] + * (from drivers/char/efirtc.c) + */ +static inline int compute_yday(int year, int month, int day) +{ + return __mon_yday[is_leap(year)][month] + day-1; +} + +/* + * Set current time and date in RTC + */ +static void at91_rtc_settime(struct rtc_time *tval) +{ + unsigned long flags; + + /* Stop Time/Calendar from counting */ + AT91_SYS->RTC_CR |= (AT91C_RTC_UPDCAL | AT91C_RTC_UPDTIM); + + spin_lock_irqsave(&at91_rtc_updlock, flags); /* stop int's else we wakeup b4 we sleep */ + AT91_SYS->RTC_IER = AT91C_RTC_ACKUPD; + interruptible_sleep_on(&at91_rtc_update); /* wait for ACKUPD interrupt to hit */ + spin_unlock_irqrestore(&at91_rtc_updlock, flags); + AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD; + + AT91_SYS->RTC_TIMR = BIN2BCD(tval->tm_sec) << 0 + | BIN2BCD(tval->tm_min) << 8 + | BIN2BCD(tval->tm_hour) << 16; + + AT91_SYS->RTC_CALR = BIN2BCD((tval->tm_year + 1900) / 100) /* century */ + | BIN2BCD(tval->tm_year % 100) << 8 /* year */ + | BIN2BCD(tval->tm_mon + 1) << 16 /* tm_mon starts at zero */ + | BIN2BCD(tval->tm_wday + 1) << 21 /* day of the week [0-6], Sunday=0 */ + | BIN2BCD(tval->tm_mday) << 24; + + /* Restart Time/Calendar */ + AT91_SYS->RTC_CR &= ~(AT91C_RTC_UPDCAL | AT91C_RTC_UPDTIM); +} + +/* + * Decode time/date into rtc_time structure + */ +static void at91_rtc_decodetime(AT91_REG *timereg, AT91_REG *calreg, struct rtc_time *tval) +{ + unsigned int time, date; + + do { /* must read twice in case it changes */ + time = *timereg; + date = *calreg; + } while ((time != *timereg) || (date != *calreg)); + + tval->tm_sec = BCD2BIN((time & AT91C_RTC_SEC) >> 0); + tval->tm_min = BCD2BIN((time & AT91C_RTC_MIN) >> 8); + tval->tm_hour = BCD2BIN((time & AT91C_RTC_HOUR) >> 16); + + /* The Calendar Alarm register does not have a field for + the year - so these will return an invalid value. When an + alarm is set, at91_alarm_year wille store the current year. */ + tval->tm_year = BCD2BIN(date & AT91C_RTC_CENT) * 100; /* century */ + tval->tm_year += BCD2BIN((date & AT91C_RTC_YEAR) >> 8); /* year */ + + tval->tm_wday = BCD2BIN((date & AT91C_RTC_DAY) >> 21) - 1; /* day of the week [0-6], Sunday=0 */ + tval->tm_mon = BCD2BIN(((date & AT91C_RTC_MONTH) >> 16) - 1); + tval->tm_mday = BCD2BIN((date & AT91C_RTC_DATE) >> 24); +} + +/* + * IRQ handler for the RTC + */ +static void at91_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int rtsr = AT91_SYS->RTC_SR & AT91_SYS->RTC_IMR; + + /* update irq data & counter */ + if (rtsr) { /* this interrupt is shared! Is it ours? */ + if (rtsr & AT91C_RTC_ALARM) + rtc_irq_data |= (AT91_RTC_AF | AT91_RTC_IRQF); + if (rtsr & AT91C_RTC_SECEV) + rtc_irq_data |= (AT91_RTC_UF | AT91_RTC_IRQF); + if (rtsr & AT91C_RTC_ACKUPD) + wake_up_interruptible(&at91_rtc_update); + rtc_irq_data += 0x100; + AT91_SYS->RTC_SCCR = rtsr; /* clear status reg */ + + /* wake up waiting process */ + wake_up_interruptible(&at91_rtc_wait); + kill_fasync(&at91_rtc_async_queue, SIGIO, POLL_IN); + } +} + +static int at91_rtc_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, &rtc_status)) + return -EBUSY; + rtc_irq_data = 0; + return 0; +} + +static int at91_rtc_release(struct inode *inode, struct file *file) +{ + rtc_status = 0; + return 0; +} + +static int at91_rtc_fasync(int fd, struct file *filp, int on) +{ + return fasync_helper(fd, filp, on, &at91_rtc_async_queue); +} + +static unsigned int at91_rtc_poll(struct file *file, poll_table * wait) +{ + poll_wait(file, &at91_rtc_wait, wait); + return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM; +} + +static ssize_t at91_rtc_read(struct file * file, char *buf, size_t count, loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t retval; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&at91_rtc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + for (;;) { + spin_lock_irq(&at91_rtc_lock); + data = rtc_irq_data; + if (data != 0) { + rtc_irq_data = 0; + break; + } + spin_unlock_irq(&at91_rtc_lock); + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + spin_unlock_irq(&at91_rtc_lock); + + data -= 0x100; /* the first IRQ wasn't actually missed */ + retval = put_user(data, (unsigned long *) buf); + if (!retval) + retval = sizeof(unsigned long); + +out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&at91_rtc_wait, &wait); + return retval; +} + +/* + * Handle commands from user-space + */ +static int at91_rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct rtc_time tm, tm2; + int ret = 0; + + spin_lock_irq(&at91_rtc_lock); + switch (cmd) { + case RTC_AIE_OFF: /* alarm off */ + AT91_SYS->RTC_IDR = AT91C_RTC_ALARM; + rtc_irq_data = 0; + break; + case RTC_AIE_ON: /* alarm on */ + AT91_SYS->RTC_IER = AT91C_RTC_ALARM; + rtc_irq_data = 0; + break; + case RTC_UIE_OFF: /* update off */ + AT91_SYS->RTC_IDR = AT91C_RTC_SECEV; + rtc_irq_data = 0; + break; + case RTC_UIE_ON: /* update on */ + AT91_SYS->RTC_IER = AT91C_RTC_SECEV; + rtc_irq_data = 0; + break; + case RTC_PIE_OFF: /* periodic off */ + AT91_SYS->RTC_IDR = AT91C_RTC_SECEV; + rtc_irq_data = 0; + break; + case RTC_PIE_ON: /* periodic on */ + AT91_SYS->RTC_IER = AT91C_RTC_SECEV; + rtc_irq_data = 0; + break; + case RTC_ALM_READ: /* read alarm */ + memset(&tm, 0, sizeof(struct rtc_time)); + at91_rtc_decodetime(&(AT91_SYS->RTC_TIMALR), &(AT91_SYS->RTC_CALALR), &tm); + tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday); + tm.tm_year = at91_alarm_year - 1900; + ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0; + break; + case RTC_ALM_SET: /* set alarm */ + if (copy_from_user(&tm2, (struct rtc_time *) arg, sizeof(tm2))) + ret = -EFAULT; + else { + at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); + at91_alarm_year = tm.tm_year; + if ((unsigned) tm2.tm_hour < 24) /* do some range checking */ + tm.tm_hour = tm2.tm_hour; + if ((unsigned) tm2.tm_min < 60) + tm.tm_min = tm2.tm_min; + if ((unsigned) tm2.tm_sec < 60) + tm.tm_sec = tm2.tm_sec; + AT91_SYS->RTC_TIMALR = BIN2BCD(tm.tm_sec) << 0 + | BIN2BCD(tm.tm_min) << 8 + | BIN2BCD(tm.tm_hour) << 16 + | AT91C_RTC_HOUREN | AT91C_RTC_MINEN + | AT91C_RTC_SECEN; + AT91_SYS->RTC_CALALR = BIN2BCD(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */ + | BIN2BCD(tm.tm_mday) << 24 + | AT91C_RTC_DATEEN | AT91C_RTC_MONTHEN; + } + break; + case RTC_RD_TIME: /* read time */ + memset(&tm, 0, sizeof(struct rtc_time)); + at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); + tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday); + tm.tm_year = tm.tm_year - 1900; + ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0; + break; + case RTC_SET_TIME: /* set time */ + if (!capable(CAP_SYS_TIME)) + ret = -EACCES; + else { + if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(tm))) + ret = -EFAULT; + else { + int tm_year = tm.tm_year + 1900; + if (tm_year < EPOCH + || (unsigned) tm.tm_mon >= 12 + || tm.tm_mday < 1 + || tm.tm_mday > (days_in_mo[tm.tm_mon] + (tm.tm_mon == 1 && is_leap(tm_year))) + || (unsigned) tm.tm_hour >= 24 + || (unsigned) tm.tm_min >= 60 + || (unsigned) tm.tm_sec >= 60) + ret = -EINVAL; + else + at91_rtc_settime(&tm); + } + } + break; + case RTC_IRQP_READ: /* read periodic alarm frequency */ + ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg); + break; + case RTC_IRQP_SET: /* set periodic alarm frequency */ + if (arg != AT91_RTC_FREQ) + ret = -EINVAL; + break; + case RTC_EPOCH_READ: /* read epoch */ + ret = put_user(EPOCH, (unsigned long *) arg); + break; + default: + ret = -EINVAL; + break; + } + spin_unlock_irq(&at91_rtc_lock); + return ret; +} + +/* + * Provide RTC information in /proc/driver/rtc + */ +static int at91_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + int len; + struct rtc_time tm; + + at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); + p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, EPOCH); + at91_rtc_decodetime(&(AT91_SYS->RTC_TIMALR), &(AT91_SYS->RTC_CALALR), &tm); + p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" + "alrm_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + at91_alarm_year, tm.tm_mon + 1, tm.tm_mday); + p += sprintf(p, "alarm_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_ALARM) ? "yes" : "no"); + p += sprintf(p, "update_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_ACKUPD) ? "yes" : "no"); + p += sprintf(p, "periodic_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_SECEV) ? "yes" : "no"); + p += sprintf(p, "periodic_freq\t: %ld\n", (unsigned long) AT91_RTC_FREQ); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static struct file_operations at91_rtc_fops = { + owner:THIS_MODULE, + llseek:no_llseek, + read:at91_rtc_read, + poll:at91_rtc_poll, + ioctl:at91_rtc_ioctl, + open:at91_rtc_open, + release:at91_rtc_release, + fasync:at91_rtc_fasync, +}; + +static struct miscdevice at91_rtc_miscdev = { + minor:RTC_MINOR, + name:"rtc", + fops:&at91_rtc_fops, +}; + +/* + * Initialize and install RTC driver + */ +static int __init at91_rtc_init(void) +{ + int ret; + + AT91_SYS->RTC_CR = 0; + AT91_SYS->RTC_MR = 0; /* put in 24 hour format */ + /* Disable all interrupts */ + AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD | AT91C_RTC_ALARM | AT91C_RTC_SECEV | AT91C_RTC_TIMEV | AT91C_RTC_CALEV; + + spin_lock_init(&at91_rtc_updlock); + spin_lock_init(&at91_rtc_lock); + + misc_register(&at91_rtc_miscdev); + create_proc_read_entry("driver/rtc", 0, 0, at91_rtc_read_proc, NULL); + ret = request_irq(AT91C_ID_SYS, at91_rtc_interrupt, SA_SHIRQ, + "at91_rtc", &rtc_status); + if (ret) { + printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", AT91C_ID_SYS); + remove_proc_entry("driver/rtc", NULL); + misc_deregister(&at91_rtc_miscdev); + return ret; + } + + printk(KERN_INFO "AT91 Real Time Clock driver\n"); + return 0; +} + +/* + * Disable and remove the RTC driver + */ +static void __exit at91_rtc_exit(void) +{ + /* Disable all interrupts */ + AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD | AT91C_RTC_ALARM | AT91C_RTC_SECEV | AT91C_RTC_TIMEV | AT91C_RTC_CALEV; + free_irq(AT91C_ID_SYS, &rtc_status); + + rtc_status = 0; + remove_proc_entry("driver/rtc", NULL); + misc_deregister(&at91_rtc_miscdev); +} + +module_init(at91_rtc_init); +module_exit(at91_rtc_exit); + +MODULE_AUTHOR("Rick Bronson"); +MODULE_DESCRIPTION("AT91 Realtime Clock Driver (AT91_RTC)"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/at91/serial/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/serial/at91_serial.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#if defined(CONFIG_SERIAL_AT91_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#define SERIAL_AT91_MAJOR TTY_MAJOR +#define CALLOUT_AT91_MAJOR TTYAUX_MAJOR +#define MINOR_START 64 + +#define AT91C_VA_BASE_DBGU ((unsigned long) &(AT91_SYS->DBGU_CR)) +#define AT91_ISR_PASS_LIMIT 256 + +#define UART_PUT_CR(port,v) ((AT91PS_USART)(port)->membase)->US_CR = v +#define UART_GET_MR(port) ((AT91PS_USART)(port)->membase)->US_MR +#define UART_PUT_MR(port,v) ((AT91PS_USART)(port)->membase)->US_MR = v +#define UART_PUT_IER(port,v) ((AT91PS_USART)(port)->membase)->US_IER = v +#define UART_PUT_IDR(port,v) ((AT91PS_USART)(port)->membase)->US_IDR = v +#define UART_GET_IMR(port) ((AT91PS_USART)(port)->membase)->US_IMR +#define UART_GET_CSR(port) ((AT91PS_USART)(port)->membase)->US_CSR +#define UART_GET_CHAR(port) ((AT91PS_USART)(port)->membase)->US_RHR +#define UART_PUT_CHAR(port,v) ((AT91PS_USART)(port)->membase)->US_THR = v +#define UART_GET_BRGR(port) ((AT91PS_USART)(port)->membase)->US_BRGR +#define UART_PUT_BRGR(port,v) ((AT91PS_USART)(port)->membase)->US_BRGR = v +#define UART_PUT_RTOR(port,v) ((AT91PS_USART)(port)->membase)->US_RTOR = v + +// #define UART_GET_CR(port) ((AT91PS_USART)(port)->membase)->US_CR // is write-only + + /* PDC registers */ +#define UART_PUT_PTCR(port,v) ((AT91PS_USART)(port)->membase)->US_PTCR = v +#define UART_PUT_RPR(port,v) ((AT91PS_USART)(port)->membase)->US_RPR = v +#define UART_PUT_RCR(port,v) ((AT91PS_USART)(port)->membase)->US_RCR = v +#define UART_GET_RCR(port) ((AT91PS_USART)(port)->membase)->US_RCR +#define UART_PUT_RNPR(port,v) ((AT91PS_USART)(port)->membase)->US_RNPR = v +#define UART_PUT_RNCR(port,v) ((AT91PS_USART)(port)->membase)->US_RNCR = v + +static struct tty_driver normal, callout; +static struct tty_struct *at91_table[AT91C_NR_UART]; +static struct termios *at91_termios[AT91C_NR_UART], *at91_termios_locked[AT91C_NR_UART]; + +const int at91_serialmap[AT91C_NR_UART] = AT91C_UART_MAP; + +static int (*at91_open)(struct uart_port *); +static void (*at91_close)(struct uart_port *); + +#ifdef SUPPORT_SYSRQ +static struct console at91_console; +#endif + +/* + * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. + */ +static u_int at91_tx_empty(struct uart_port *port) +{ + return UART_GET_CSR(port) & AT91C_US_TXEMPTY ? TIOCSER_TEMT : 0; +} + +/* + * Set state of the modem control output lines + */ +static void at91_set_mctrl(struct uart_port *port, u_int mctrl) +{ + unsigned int control = 0; + + /* + * 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"); --- /dev/null +++ linux-2.4.27/drivers/at91/spi/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/spi/at91_spi.c @@ -0,0 +1,275 @@ +/* + * Serial Peripheral Interface (SPI) driver for the Atmel AT91RM9200 (Thunder) + * + * (c) SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "at91_spi.h" + +#undef DEBUG_SPI + +static struct spi_local spi_dev[NR_SPI_DEVICES]; /* state of the SPI devices */ +static int spi_enabled = 0; +static struct semaphore spi_lock; /* protect access to SPI bus */ +static int current_device = -1; /* currently selected SPI device */ + +DECLARE_COMPLETION(transfer_complete); + +/* SPI controller device */ +static AT91PS_SPI controller = (AT91PS_SPI) AT91C_VA_BASE_SPI; + +/* ......................................................................... */ + +/* + * Access and enable the SPI bus. + * This MUST be called before any transfers are performed. + */ +void spi_access_bus(short device) +{ + /* Ensure that requested device is valid */ + if ((device < 0) || (device >= NR_SPI_DEVICES)) + panic("at91_spi: spi_access_bus called with invalid device"); + + if (spi_enabled == 0) { + AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI; /* Enable Peripheral clock */ + controller->SPI_CR = AT91C_SPI_SPIEN; /* Enable SPI */ +#ifdef DEBUG_SPI + printk("SPI on\n"); +#endif + } + MOD_INC_USE_COUNT; + spi_enabled++; + + /* Lock the SPI bus */ + down(&spi_lock); + current_device = device; + + /* Enable PIO */ + if (!spi_dev[device].pio_enabled) { + switch (device) { + case 0: AT91_CfgPIO_SPI_CS0(); 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") --- /dev/null +++ linux-2.4.27/drivers/at91/spi/at91_spi.h @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/spi/at91_spidev.c @@ -0,0 +1,226 @@ +/* + * User-space interface to the SPI bus on Atmel AT91RM9200 + * + * (c) SAN People (Pty) Ltd + * + * Based on SPI driver by Rick Bronson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_DEVFS_FS +#include +#endif + +#include "at91_spi.h" + +#undef DEBUG_SPIDEV + +#ifdef CONFIG_DEVFS_FS +static devfs_handle_t devfs_handle = NULL; +static devfs_handle_t devfs_spi[NR_SPI_DEVICES]; +#endif + +/* ......................................................................... */ + +/* + * Read or Write to SPI bus. + */ +static ssize_t spidev_rd_wr(struct file *file, char *buf, size_t count, loff_t *offset) +{ + unsigned int spi_device = (unsigned int) file->private_data; + struct kiobuf *iobuf; + unsigned int ofs, pagelen; + int res, i; + + struct spi_transfer_list* list = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL); + if (!list) + return -ENOMEM; + + res = alloc_kiovec(1, &iobuf); + if (res) { + kfree(list); + return res; + } + + res = map_user_kiobuf(READ, iobuf, (unsigned long) buf, count); + if (res) { + free_kiovec(1, &iobuf); + kfree(list); + return res; + } + + /* More pages than transfer slots in spi_transfer_list */ + if (iobuf->nr_pages >= MAX_SPI_TRANSFERS) { + unmap_kiobuf(iobuf); + free_kiovec(1, &iobuf); + kfree(list); + return -EFBIG; + } + +#ifdef DEBUG_SPIDEV + printk("spidev_rd_rw: %i %i\n", count, iobuf->nr_pages); +#endif + + /* Set default return value = transfer length */ + res = count; + + /* + * At this point, the virtual area buf[0] .. buf[count-1] will have + * corresponding pages mapped in the physical memory and locked until + * we unmap the kiobuf. The pages cannot be swapped out or moved + * around. + */ + ofs = iobuf->offset; + pagelen = PAGE_SIZE - iobuf->offset; + if (count < pagelen) + pagelen = count; + + for (i = 0; i < iobuf->nr_pages; i++) { + list->tx[i] = list->rx[i] = page_address(iobuf->maplist[i]) + ofs; + list->txlen[i] = list->rxlen[i] = pagelen; + +#ifdef DEBUG_SPIDEV + printk(" %i: %x (%i)\n", i, list->tx[i], list->txlen[i]); +#endif + + ofs = 0; /* all subsequent transfers start at beginning of a page */ + count = count - pagelen; + pagelen = (count < PAGE_SIZE) ? count : PAGE_SIZE; + } + list->nr_transfers = iobuf->nr_pages; + + /* Perform transfer on SPI bus */ + spi_access_bus(spi_device); + spi_transfer(list); + spi_release_bus(spi_device); + + unmap_kiobuf(iobuf); + free_kiovec(1, &iobuf); + kfree(list); + + return res; +} + +static int spidev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int spi_device = MINOR(inode->i_rdev); + + if (spi_device >= NR_SPI_DEVICES) + return -ENODEV; + + // TODO: This interface can be used to configure the SPI bus. + // Configurable options could include: Speed, Clock Polarity, Clock Phase + + switch(cmd) { + default: + return -ENOIOCTLCMD; + } +} + +/* + * Open the SPI device + */ +static int spidev_open(struct inode *inode, struct file *file) +{ + unsigned int spi_device = MINOR(inode->i_rdev); + + if (spi_device >= NR_SPI_DEVICES) + return -ENODEV; + + MOD_INC_USE_COUNT; + + /* + * 'private_data' is actually a pointer, but we overload it with the + * value we want to store. + */ + (unsigned int) file->private_data = spi_device; + + return 0; +} + +/* + * Close the SPI device + */ +static int spidev_close(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* ......................................................................... */ + +static struct file_operations spidev_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: spidev_rd_wr, + write: spidev_rd_wr, + ioctl: spidev_ioctl, + open: spidev_open, + release: spidev_close, +}; + +/* + * Install the SPI /dev interface driver + */ +static int __init at91_spidev_init(void) +{ + int i; + char name[3]; + +#ifdef CONFIG_DEVFS_FS + if (devfs_register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) { +#else + if (register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) { +#endif + printk(KERN_ERR "at91_spidev: Unable to get major %d for SPI bus\n", SPI_MAJOR); + return -EIO; + } + +#ifdef CONFIG_DEVFS_FS + devfs_handle = devfs_mk_dir(NULL, "spi", NULL); + + for (i = 0; i < NR_SPI_DEVICES; i++) { + sprintf (name, "%d", i); + devfs_spi[i] = devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, SPI_MAJOR, i, S_IFCHR | S_IRUSR | S_IWUSR, + &spidev_fops, NULL); + } +#endif + printk(KERN_INFO "AT91 SPI driver loaded\n"); + + return 0; +} + +/* + * Remove the SPI /dev interface driver + */ +static void __exit at91_spidev_exit(void) +{ +#ifdef CONFIG_DEVFS_FS + devfs_unregister(devfs_handle); + if (devfs_unregister_chrdev(SPI_MAJOR, "spi")) { +#else + if (unregister_chrdev(SPI_MAJOR,"spi")) { +#endif + printk(KERN_ERR "at91_spidev: Unable to release major %d for SPI bus\n", SPI_MAJOR); + return; + } +} + +module_init(at91_spidev_init); +module_exit(at91_spidev_exit); + +MODULE_LICENSE("GPL") +MODULE_AUTHOR("Andrew Victor") +MODULE_DESCRIPTION("SPI /dev interface for Atmel AT91RM9200") --- /dev/null +++ linux-2.4.27/drivers/at91/usb/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/usb/at91_usb-ohci.c @@ -0,0 +1,85 @@ +/* + * linux/drivers/at91/usb/at91_usb_ohci-at91.c + * + * (c) Rick Bronson + * + * The outline of this code was taken from Brad Parkers + * original OHCI driver modifications, and reworked into a cleaner form + * by Russell King . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + NOTE: + The following is so that we don't have to include usb-ohci.h or pci.h as the + usb-ohci.c driver needs these routines even when the architecture + has no PCI bus... +*/ + +extern int __devinit hc_add_ohci(struct pci_dev *dev, int irq, void *membase, + unsigned long flags, void *ohci, const char *name, + const char *slot_name); +extern void hc_remove_ohci(void *ohci); + +static void *at91_ohci; +AT91PS_UHP ohci_regs; + +static int __init at91_ohci_init(void) +{ + int ret; + + ohci_regs = ioremap(AT91_UHP_BASE, SZ_4K); + if (!ohci_regs) { + printk(KERN_ERR "at91_usb-ohci: ioremap failed\n"); + return -EIO; + } + + /* Enable PLLB */ + AT91_SYS->CKGR_PLLBR = AT91_PLLB_INIT; + while ((AT91_SYS->PMC_SR & 4) == 0); + + /* Now, enable the USB clock */ + AT91_SYS->PMC_SCER = AT91C_PMC_UHP; /* enable system clock */ + AT91_SYS->PMC_PCER = 1 << AT91C_ID_UHP; /* enable peripheral clock */ + + /* Take Hc out of reset */ + ohci_regs->UHP_HcControl = 2 << 6; + + /* Initialise the generic OHCI driver. */ + ret = hc_add_ohci((struct pci_dev *) 1, AT91C_ID_UHP, + (void *)ohci_regs, 0, &at91_ohci, + "usb-ohci", "at91"); + if (ret) + iounmap(ohci_regs); + + return ret; +} + +static void __exit at91_ohci_exit(void) +{ + hc_remove_ohci(at91_ohci); + + /* Force UHP_Hc to reset */ + ohci_regs->UHP_HcControl = 0; + + /* Stop the USB clock. */ + AT91_SYS->PMC_SCDR = AT91C_PMC_UHP; /* disable system clock */ + AT91_SYS->PMC_PCDR = 1 << AT91C_ID_UHP; /* disable peripheral clock */ + + iounmap(ohci_regs); +} + +module_init(at91_ohci_init); +module_exit(at91_ohci_exit); --- /dev/null +++ linux-2.4.27/drivers/at91/watchdog/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/at91/watchdog/at91_wdt.c @@ -0,0 +1,193 @@ +/* + * Watchdog driver for Atmel AT91RM9200 (Thunder) + * + * (c) SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#define WDT_DEFAULT_TIME 5 /* 5 seconds */ +#define WDT_MAX_TIME 256 /* 256 seconds */ + +static int at91wdt_time = WDT_DEFAULT_TIME; +static int at91wdt_busy; + +/* ......................................................................... */ + +/* + * Disable the watchdog. + */ +static void at91_wdt_stop(void) +{ + AT91_SYS->ST_WDMR = AT91C_ST_EXTEN; +} + +/* + * Enable and reset the watchdog. + */ +static void at91_wdt_start(void) +{ + AT91_SYS->ST_WDMR = AT91C_ST_EXTEN | AT91C_ST_RSTEN | (((65536 * at91wdt_time) >> 8) & AT91C_ST_WDV); + AT91_SYS->ST_CR = AT91C_ST_WDRST; +} + +/* ......................................................................... */ + +/* + * Watchdog device is opened, and watchdog starts running. + */ +static int at91_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, &at91wdt_busy)) + return -EBUSY; + MOD_INC_USE_COUNT; + + /* + * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz + * + * Since WDV is a 16-bit counter, the maximum period is + * 65536 / 0.256 = 256 seconds. + */ + + at91_wdt_start(); + return 0; +} + +/* + * Close the watchdog device. + * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also + * disabled. + */ +static int at91_wdt_close(struct inode *inode, struct file *file) +{ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + /* Disable the watchdog when file is closed */ + at91_wdt_stop(); +#endif + + at91wdt_busy = 0; + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Handle commands from user-space. + */ +static int at91_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int new_value; + static struct watchdog_info info = { + identity: "at91 watchdog", + options: WDIOF_SETTIMEOUT, + }; + + switch(cmd) { + case WDIOC_KEEPALIVE: + AT91_SYS->ST_CR = AT91C_ST_WDRST; /* Pat the watchdog */ + return 0; + + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &info, sizeof(info)); + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, (int *)arg)) + return -EFAULT; + if ((new_value <= 0) || (new_value > WDT_MAX_TIME)) + return -EINVAL; + + /* Restart watchdog with new time */ + at91wdt_time = new_value; + at91_wdt_start(); + + /* Return current value */ + return put_user(at91wdt_time, (int *)arg); + + case WDIOC_GETTIMEOUT: + return put_user(at91wdt_time, (int *)arg); + + case WDIOC_GETSTATUS: + return put_user(0, (int *)arg); + + case WDIOC_SETOPTIONS: + if (get_user(new_value, (int *)arg)) + return -EFAULT; + if (new_value & WDIOS_DISABLECARD) + at91_wdt_stop(); + if (new_value & WDIOS_ENABLECARD) + at91_wdt_start(); + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +/* + * Pat the watchdog whenever device is written to. + */ +static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (len) { + AT91_SYS->ST_CR = AT91C_ST_WDRST; /* Pat the watchdog */ + return len; + } + + return 0; +} + +/* ......................................................................... */ + +static struct file_operations at91wdt_fops = +{ + .owner = THIS_MODULE, + .ioctl = at91_wdt_ioctl, + .open = at91_wdt_open, + .release = at91_wdt_close, + .write = at91_wdt_write, +}; + +static struct miscdevice at91wdt_miscdev = +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &at91wdt_fops, +}; + +static int __init at91_wdt_init(void) +{ + int res; + + res = misc_register(&at91wdt_miscdev); + if (res) + return res; + + printk("AT91 Watchdog Timer enabled (%d seconds)\n", WDT_DEFAULT_TIME); + return 0; +} + +static void __exit at91_wdt_exit(void) +{ + misc_deregister(&at91wdt_miscdev); +} + +module_init(at91_wdt_init); +module_exit(at91_wdt_exit); + +MODULE_LICENSE("GPL") +MODULE_AUTHOR("Andrew Victor") +MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200") --- linux-2.4.27/drivers/block/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/block/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o -obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o +obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o @@ -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 --- linux-2.4.27/drivers/block/ll_rw_blk.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/block/ll_rw_blk.c @@ -32,6 +32,19 @@ #include #include +/* Maybe something to cleanup in 2.3? + * We shouldn't touch 0x3f2 on machines which don't have a PC floppy controller + * - it may contain something else which could cause a system hang. This is + * now selected by a configuration option, but maybe it ought to be in the + * floppy code itself? - rmk + */ +#if defined(__i386__) || (defined(__arm__) && defined(CONFIG_ARCH_ACORN)) +#define FLOPPY_BOOT_DISABLE +#endif +#ifdef CONFIG_BLK_DEV_FD +#undef FLOPPY_BOOT_DISABLE +#endif + /* * MAC Floppy IWM hooks */ @@ -524,7 +537,7 @@ elevator_init(&q->elevator, ELEVATOR_LINUS); blk_init_free_list(q); q->request_fn = rfn; - q->back_merge_fn = ll_back_merge_fn; + q->back_merge_fn = ll_back_merge_fn; q->front_merge_fn = ll_front_merge_fn; q->merge_requests_fn = ll_merge_requests_fn; q->make_request_fn = __make_request; @@ -1549,7 +1562,7 @@ mfm_init(); #endif #ifdef CONFIG_PARIDE - { extern void paride_init(void); paride_init(); }; + { extern void paride_init(void); paride_init(); } #endif #ifdef CONFIG_MAC_FLOPPY swim3_init(); @@ -1563,12 +1576,14 @@ #ifdef CONFIG_ATARI_FLOPPY atari_floppy_init(); #endif +#ifdef CONFIG_BLK_DEV_FD1772 + fd1772_init(); +#endif #ifdef CONFIG_BLK_DEV_FD floppy_init(); -#else -#if defined(__i386__) /* Do we even need this? */ - outb_p(0xc, 0x3f2); #endif +#ifdef FLOPPY_BOOT_DISABLE + outb_p(0xc, 0x3f2); #endif #ifdef CONFIG_CDU31A cdu31a_init(); @@ -1626,7 +1641,7 @@ jsfd_init(); #endif return 0; -}; +} EXPORT_SYMBOL(io_request_lock); EXPORT_SYMBOL(end_that_request_first); --- linux-2.4.27/drivers/cdrom/cdrom.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/cdrom/cdrom.c @@ -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) */ --- linux-2.4.27/drivers/char/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/Config.in @@ -20,10 +20,10 @@ if [ "$CONFIG_IA64" = "y" ]; then bool ' Support for serial port described by EFI HCDP table' CONFIG_SERIAL_HCDP fi - if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL - tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL - fi +fi +if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + dep_tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL $CONFIG_SERIAL + dep_tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL $CONFIG_SERIAL fi dep_mbool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED $CONFIG_SERIAL if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then @@ -132,18 +132,6 @@ bool ' SGI SN2 IOC4 serial port support' CONFIG_SGI_IOC4_SERIAL fi fi -fi -if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then - tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232 -fi -if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then - bool 'DC21285 serial port support' CONFIG_SERIAL_21285 - if [ "$CONFIG_SERIAL_21285" = "y" ]; then - if [ "$CONFIG_OBSOLETE" = "y" ]; then - bool ' Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD - fi - bool ' Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE - fi if [ "$CONFIG_PARISC" = "y" ]; then bool ' PDC software console support' CONFIG_PDC_CONSOLE fi @@ -168,6 +156,16 @@ if [ "$CONFIG_CPU_VR41XX" = "y" ]; then bool 'NEC VR4100 series Keyboard Interface Unit Support ' CONFIG_VR41XX_KIU fi +if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + tristate 'AT91RM9200 SPI device interface' CONFIG_AT91_SPIDEV +fi + +source drivers/serial/Config.in + +if [ "$CONFIG_ARCH_ANAKIN" = "y" ]; then + tristate 'Anakin touchscreen support' CONFIG_TOUCHSCREEN_ANAKIN +fi + bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 @@ -190,6 +188,12 @@ source drivers/i2c/Config.in +if [ "$CONFIG_I2C" != "n" ]; then + dep_tristate ' DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C +fi + +source drivers/l3/Config.in + mainmenu_option next_comment comment 'Mice' tristate 'Bus Mouse Support' CONFIG_BUSMOUSE @@ -245,11 +249,13 @@ tristate ' ALi M7101 PMU Watchdog Timer' CONFIG_ALIM7101_WDT tristate ' AMD "Elan" SC520 Watchdog Timer' CONFIG_SC520_WDT tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG - if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then - tristate ' DC21285 watchdog' CONFIG_21285_WATCHDOG - if [ "$CONFIG_ARCH_NETWINDER" = "y" ]; then - tristate ' NetWinder WB83C977 watchdog' CONFIG_977_WATCHDOG - fi + if [ "$CONFIG_ARM" = "y" ]; then + dep_tristate ' DC21285 watchdog' CONFIG_21285_WATCHDOG $CONFIG_FOOTBRIDGE + dep_tristate ' NetWinder WB83C977 watchdog' CONFIG_977_WATCHDOG $CONFIG_ARCH_NETWINDER + dep_tristate ' SA1100 watchdog' CONFIG_SA1100_WATCHDOG $CONFIG_ARCH_SA1100 + dep_tristate ' EPXA watchdog' CONFIG_EPXA_WATCHDOG $CONFIG_ARCH_CAMELOT + dep_tristate ' Omaha watchdog' CONFIG_OMAHA_WATCHDOG $CONFIG_ARCH_OMAHA + dep_tristate ' AT91RM9200 watchdog' CONFIG_AT91_WATCHDOG $CONFIG_ARCH_AT91RM9200 fi tristate ' Eurotech CPU-1220/1410 Watchdog Timer' CONFIG_EUROTECH_WDT tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT @@ -325,6 +331,15 @@ if [ "$CONFIG_TOSHIBA_RBTX4927" = "y" -o "$CONFIG_TOSHIBA_JMR3927" = "y" ]; then tristate 'Dallas DS1742 RTC support' CONFIG_DS1742 fi +if [ "$CONFIG_ARCH_SA1100" = "y" ]; then + tristate 'SA1100 Real Time Clock' CONFIG_SA1100_RTC +fi +if [ "$CONFIG_ARCH_OMAHA" = "y" ]; then + tristate 'Omaha Real Time Clock' CONFIG_OMAHA_RTC +fi +if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + tristate 'AT91RM9200 Real Time Clock' CONFIG_AT91_RTC +fi tristate 'Double Talk PC internal speech card support' CONFIG_DTLK tristate 'Siemens R3964 line discipline' CONFIG_R3964 --- linux-2.4.27/drivers/char/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/Makefile @@ -29,7 +29,7 @@ mod-subdirs := joystick ftape drm drm-4.0 pcmcia -list-multi := +list-multi := KEYMAP =defkeymap.o KEYBD =pc_keyb.o @@ -106,11 +106,39 @@ endif ifeq ($(ARCH),arm) - ifneq ($(CONFIG_PC_KEYMAP),y) - KEYMAP = + KEYMAP := + KEYBD := + ifeq ($(CONFIG_PC_KEYMAP),y) + KEYMAP := defkeymap.o endif - ifneq ($(CONFIG_PC_KEYB),y) - KEYBD = + ifeq ($(CONFIG_PC_KEYB),y) + KEYBD += pc_keyb.o + endif + ifeq ($(CONFIG_KMI_KEYB),y) + KEYBD += amba_kmi_keyb.o + endif + ifeq ($(CONFIG_SA1111),y) + KEYBD += sa1111_keyb.o + endif + ifeq ($(CONFIG_ARCH_EDB7211),y) + KEYBD += edb7211_keyb.o + endif + ifeq ($(CONFIG_ARCH_AUTCPU12),y) + KEYMAP := defkeymap.o + KEYBD += clps711x_keyb.o + endif + ifeq ($(CONFIG_SA1100_GRAPHICSCLIENT),y) + KEYMAP = gckeymap.o + KEYBD += gc_keyb.o + endif + ifeq ($(CONFIG_SA1100_CERF_CPLD),y) + KEYBD += cerf_keyb.o + endif + ifeq ($(CONFIG_ARCH_FORTUNET),y) + KEYMAP := defkeymap.o + endif + ifeq ($(CONFIG_ARCH_GUIDEA07),y) + KEYMAP := defkeymap.o endif endif @@ -173,11 +201,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 +280,8 @@ obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_MIPS_RTC) += mips_rtc.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o +obj-$(CONFIG_SA1100_RTC) += sa1100-rtc.o +obj-$(CONFIG_OMAHA_RTC) += omaha-rtc.o ifeq ($(CONFIG_PPC),) obj-$(CONFIG_NVRAM) += nvram.o endif @@ -292,6 +320,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 +349,27 @@ obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o +obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o +obj-$(CONFIG_EPXA_WATCHDOG) += epxa_wdt.o +obj-$(CONFIG_OMAHA_WATCHDOG) += omaha_wdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o +# I2C char devices +obj-$(CONFIG_I2C_DS1307) += ds1307.o + subdir-$(CONFIG_MWAVE) += mwave ifeq ($(CONFIG_MWAVE),y) obj-y += mwave/mwave.o endif +ifeq ($(CONFIG_ARCH_ACORN),y) +mod-subdirs += ../acorn/char +subdir-y += ../acorn/char +obj-y += ../acorn/char/acorn-char.o +endif + subdir-$(CONFIG_IPMI_HANDLER) += ipmi ifeq ($(CONFIG_IPMI_HANDLER),y) obj-y += ipmi/ipmi.o --- /dev/null +++ linux-2.4.27/drivers/char/amba_kmi_keyb.c @@ -0,0 +1,999 @@ +/* + * linux/drivers/char/amba_kmi_keyb.c + * + * AMBA Keyboard and Mouse Interface Driver + * + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This keyboard driver drives a PS/2 keyboard and mouse connected + * to the KMI interfaces. The KMI interfaces are nothing more than + * a uart; there is no inteligence in them to do keycode translation. + * We leave all that up to the keyboard itself. + * + * FIXES: + * dirk.uffmann@nokia.com: enabled PS/2 reconnection + */ +#include +#include +#include +#include /* for in_interrupt */ +#include +#include +#include /* for udelay */ +#include /* for keyboard_tasklet */ +#include + +#include +#include +#include +#include + +//#define DEBUG(s) printk s +#define DEBUG(s) do { } while (0) + +#define CONFIG_AMBA_PS2_RECONNECT + +#define KMI_BASE (kmi->base) + +#define KMI_RESET 0x00 +#define KMI_RESET_POR 0x01 +#define KMI_RESET_DONE 0x02 + +#define KMI_NO_ACK 0xffff + +#define PS2_O_RESET 0xff +#define PS2_O_RESEND 0xfe +#define PS2_O_DISABLE 0xf5 +#define PS2_O_ENABLE 0xf4 +#define PS2_O_ECHO 0xee + +/* + * Keyboard + */ +#define PS2_O_SET_DEFAULT 0xf6 +#define PS2_O_SET_RATE_DELAY 0xf3 +#define PS2_O_SET_SCANSET 0xf0 +#define PS2_O_INDICATORS 0xed + +/* + * Mouse + */ +#define PS2_O_SET_SAMPLE 0xf3 +#define PS2_O_SET_STREAM 0xea +#define PS2_O_SET_RES 0xe8 +#define PS2_O_SET_SCALE21 0xe7 +#define PS2_O_SET_SCALE11 0xe6 +#define PS2_O_REQ_STATUS 0xe9 + +/* + * Responses + */ +#define PS2_I_RESEND 0xfe +#define PS2_I_DIAGFAIL 0xfc +#define PS2_I_ACK 0xfa +#define PS2_I_BREAK 0xf0 +#define PS2_I_ECHO 0xee +#define PS2_I_BAT_OK 0xaa + +static char *kmi_type[] = { "Keyboard", "Mouse" }; + +static struct kmi_info *kmi_keyb; +static struct kmi_info *kmi_mouse; + +static inline void __kmi_send(struct kmi_info *kmi, u_int val) +{ + u_int status; + + do { + status = __raw_readb(KMISTAT); + } while (!(status & KMISTAT_TXEMPTY)); + + kmi->resend_count += 1; + __raw_writeb(val, KMIDATA); +} + +static void kmi_send(struct kmi_info *kmi, u_int val) +{ + kmi->last_tx = val; + kmi->resend_count = -1; + __kmi_send(kmi, val); +} + +static u_int kmi_send_and_wait(struct kmi_info *kmi, u_int val, u_int timeo) +{ + DECLARE_WAITQUEUE(wait, current); + + if (kmi->present == 0) + return KMI_NO_ACK; + + kmi->res = KMI_NO_ACK; + kmi->last_tx = val; + kmi->resend_count = -1; + + if (current->pid != 0 && !in_interrupt()) { + add_wait_queue(&kmi->wait_q, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + __kmi_send(kmi, val); + schedule_timeout(timeo); + current->state = TASK_RUNNING; + remove_wait_queue(&kmi->wait_q, &wait); + } else { + int i; + + __kmi_send(kmi, val); + for (i = 0; i < 1000; i++) { + if (kmi->res != KMI_NO_ACK) + break; + udelay(100); + } + } + + return kmi->res; +} + +/* + * This lot should probably be separated into a separate file... + */ +#ifdef CONFIG_KMI_MOUSE + +#include /* for struct file_ops */ +#include /* for poll_table */ +#include /* for struct miscdev */ +#include /* for add_mouse_randomness */ +#include /* for kmalloc */ +#include /* for {un,}lock_kernel */ +#include + +#include + +#define BUF_SZ 2048 + +static spinlock_t kmi_mouse_lock; +static int kmi_mouse_count; +static struct queue { + u_int head; + u_int tail; + struct fasync_struct *fasync; + unsigned char buf[BUF_SZ]; +} *queue; + +#define queue_empty() (queue->head == queue->tail) + +static u_char get_from_queue(void) +{ + unsigned long flags; + u_char res; + + spin_lock_irqsave(&kmi_mouse_lock, flags); + res = queue->buf[queue->tail]; + queue->tail = (queue->tail + 1) & (BUF_SZ-1); + spin_unlock_irqrestore(&kmi_mouse_lock, flags); + + return res; +} + +static ssize_t +kmi_mouse_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + ssize_t i = count; + + if (queue_empty()) { + int ret; + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(kmi_mouse->wait_q, !queue_empty()); + if (ret) + return ret; + } + while (i > 0 && !queue_empty()) { + u_char c; + c = get_from_queue(); + put_user(c, buf++); + i--; + } + if (count - i) + file->f_dentry->d_inode->i_atime = CURRENT_TIME; + return count - i; +} + +static ssize_t +kmi_mouse_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + ssize_t retval = 0; + + if (count > 32) + count = 32; + + do { + char c; + get_user(c, buf++); + kmi_send_and_wait(kmi_mouse, c, HZ); + retval++; + } while (--count); + + if (retval) + file->f_dentry->d_inode->i_mtime = CURRENT_TIME; + + return retval; +} + +static unsigned int +kmi_mouse_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &kmi_mouse->wait_q, wait); + return (!queue_empty()) ? POLLIN | POLLRDNORM : 0; +} + +static int +kmi_mouse_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + fasync_helper(-1, file, 0, &queue->fasync); + if (--kmi_mouse_count == 0) + kmi_send_and_wait(kmi_mouse, PS2_O_DISABLE, HZ); + unlock_kernel(); + return 0; +} + +static int +kmi_mouse_open(struct inode *inode, struct file *file) +{ + if (kmi_mouse_count++) + return 0; + queue->head = queue->tail = 0; + kmi_send_and_wait(kmi_mouse, PS2_O_ENABLE, HZ); + return 0; +} + +static int +kmi_mouse_fasync(int fd, struct file *filp, int on) +{ + int retval = fasync_helper(fd, filp, on, &queue->fasync); + if (retval > 0) + retval = 0; + return retval; +} + +static struct file_operations ps_fops = { + read: kmi_mouse_read, + write: kmi_mouse_write, + poll: kmi_mouse_poll, + open: kmi_mouse_open, + release: kmi_mouse_release, + fasync: kmi_mouse_fasync, +}; + +static struct miscdevice ps_mouse = { + minor: PSMOUSE_MINOR, + name: "psaux", + fops: &ps_fops, +}; + +static u_char kmi_mse_init_string[] = { + PS2_O_DISABLE, + PS2_O_SET_SAMPLE, 100, + PS2_O_SET_RES, 3, + PS2_O_SET_SCALE21 +}; + +/* + * The "normal" mouse scancode processing + */ +static void kmi_mse_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs) +{ + u_int head; + + add_mouse_randomness(val); + +#ifdef CONFIG_AMBA_PS2_RECONNECT + /* Try to detect a hot-plug event on the PS/2 mouse port */ + switch (kmi->hotplug_state) { + case 0: + /* Maybe we lost contact... */ + if (val == PS2_I_BAT_OK) { + kmi->hotplug_state++; + DEBUG(("%s: Saw 0xAA. Going to hotplug state %d\n", kmi->name, kmi->hotplug_state)); + } + break; + + case 1: + /* Again, maybe (but only maybe) we lost contact... */ + if (val == 0) { + kmi->hotplug_state++; + kmi_send(kmi, PS2_O_REQ_STATUS); + DEBUG(("%s: Got 0xAA 0x00. Sent Status Request\n", kmi->name)); + } else { + kmi->hotplug_state = 0; + DEBUG(("%s: No 0x00 followed 0xAA. No reconnect.\n", kmi->name)); + } + break; + + case 2: + /* Eat up acknowledge */ + if (val == PS2_I_ACK) + kmi->hotplug_state++; + else { + kmi->hotplug_state = 0; + DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); + } + break; + + case 3: + /* check if data reporting is still enabled, then no POR has happend */ + kmi->reconnect = !(val & 1<<5); + DEBUG(("%s: Data reporting disabled?: (%d)\n", kmi->name, kmi->reconnect)); + kmi->hotplug_state++; + DEBUG(("%s: Going to hotplug state %d\n", kmi->name, kmi->hotplug_state)); + break; + + case 4: + /* Eat up one status byte */ + kmi->hotplug_state++; + DEBUG(("%s: Going to hotplug state %d\n", kmi->name, kmi->hotplug_state)); + break; + + case 5: + /* Eat up another status byte */ + if (kmi->reconnect) { + kmi->config_num = 0; + kmi_send(kmi, kmi_mse_init_string[kmi->config_num]); + kmi->config_num++; + kmi->hotplug_state++; + DEBUG(("%s: Sending byte %d of PS/2 init string.\n", kmi->name, kmi->config_num)); + } else { + kmi->hotplug_state = 0; + DEBUG(("%s: False Alarm...\n", kmi->name)); + } + break; + + case 6: + if (val == PS2_I_ACK && kmi->config_num < sizeof(kmi_mse_init_string)) { + kmi_send(kmi, kmi_mse_init_string[kmi->config_num]); + kmi->config_num++; + DEBUG(("%s: Sending byte %d of PS/2 init string.\n", kmi->name, kmi->config_num)); + } else { + if (val == PS2_I_ACK) { + DEBUG(("%s: Now enable the mouse again...\n", kmi->name)); + queue->head = queue->tail = 0; + kmi_send(kmi, PS2_O_ENABLE); + kmi->hotplug_state++; + } else { + kmi->hotplug_state = 0; + DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); + } + } + break; + + case 7: + /* Eat up last acknowledge from enable */ + if (val == PS2_I_ACK) + printk(KERN_ERR "%s: reconnected\n", kmi->name); + else + DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); + + kmi->hotplug_state = 0; + break; + + } /* switch (kmi->hotplug_state) */ + + /* while inside hotplug mechanism, don't misinterpret values */ + if (kmi->hotplug_state > 2) + return; +#endif + + /* We are waiting for the mouse to respond to a kmi_send_and_wait() */ + if (kmi->res == KMI_NO_ACK) { + if (val == PS2_I_RESEND) { + if (kmi->resend_count < 5) + __kmi_send(kmi, kmi->last_tx); + else { + printk(KERN_ERR "%s: too many resends\n", kmi->name); + return; + } + } + + if (val == PS2_I_ACK) { + kmi->res = val; + wake_up(&kmi->wait_q); + } + return; + } + + /* The mouse autonomously send new data, so wake up mouse_read() */ + if (queue) { + head = queue->head; + queue->buf[head] = val; + head = (head + 1) & (BUF_SZ - 1); + if (head != queue->tail) { + queue->head = head; + kill_fasync(&queue->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&kmi->wait_q); + } + } +} + +static int kmi_init_mouse(struct kmi_info *kmi) +{ + u_int ret, i; + + if (kmi->present) { + kmi->rx = kmi_mse_intr; + + for (i = 0; i < sizeof(kmi_mse_init_string); i++) { + ret = kmi_send_and_wait(kmi, kmi_mse_init_string[i], HZ); + if (ret != PS2_I_ACK) + printk("%s: didn't get ack (0x%2.2x)\n", + kmi->name, ret); + } + } + + queue = kmalloc(sizeof(*queue), GFP_KERNEL); + if (queue) { + memset(queue, 0, sizeof(*queue)); + misc_register(&ps_mouse); + ret = 0; + } else + ret = -ENOMEM; + + return ret; +} +#endif /* CONFIG_KMI_MOUSE */ + +/* + * The "program" we send to the keyboard to set it up how we want it: + * - default typematic delays + * - scancode set 1 + */ +static u_char kmi_kbd_init_string[] = { + PS2_O_DISABLE, + PS2_O_SET_DEFAULT, + PS2_O_SET_SCANSET, 0x01, + PS2_O_ENABLE +}; + +static void kmi_kbd_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs); + +static int __kmi_init_keyboard(struct kmi_info *kmi) +{ + u_int ret, i; + + if (!kmi->present) + return 0; + + kmi->rx = kmi_kbd_intr; + + for (i = 0; i < sizeof(kmi_kbd_init_string); i++) { + ret = kmi_send_and_wait(kmi, kmi_kbd_init_string[i], HZ); + if (ret != PS2_I_ACK) + printk("%s: didn't ack (0x%2.2x)\n", + kmi->name, ret); + } + + return 0; +} + +static void kmi_kbd_init_tasklet(unsigned long k) +{ + struct kmi_info *kmi = (struct kmi_info *)k; + __kmi_init_keyboard(kmi); +} + +static DECLARE_TASKLET_DISABLED(kmikbd_init_tasklet, kmi_kbd_init_tasklet, 0); + +/* + * The "normal" keyboard scancode processing + */ +static void kmi_kbd_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs) +{ +#ifdef CONFIG_AMBA_PS2_RECONNECT + /* Try to detect a hot-plug event on the PS/2 keyboard port */ + switch (kmi->hotplug_state) { + case 0: + /* Maybe we lost contact... */ + if (val == PS2_I_BAT_OK) { + kmi_send(kmi, PS2_O_SET_SCANSET); + kmi->hotplug_state++; + DEBUG(("%s: Saw 0xAA. Going to hotplug state %d\n", kmi->name, kmi->hotplug_state)); + } + break; + + case 1: + /* Eat up acknowledge */ + if (val == PS2_I_ACK) { + /* Request scan code set: '2' if POR has happend, '1' is false alarm */ + kmi_send(kmi, 0); + kmi->hotplug_state++; + } + else { + kmi->hotplug_state = 0; + DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); + } + break; + + case 2: + /* Eat up acknowledge */ + if (val == PS2_I_ACK) + kmi->hotplug_state++; + else { + kmi->hotplug_state = 0; + DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); + } + break; + + case 3: + kmi->hotplug_state = 0; + if (val == 2) { + DEBUG(("%s: POR detected. Scan code is: (%d)\n", kmi->name, val)); + kmi->present = 1; + tasklet_schedule(&kmikbd_init_tasklet); + printk(KERN_ERR "%s: reconnected\n", kmi->name); + return; + } + else + DEBUG(("%s: False Alarm...\n", kmi->name)); + break; + + } /* switch (kmi->hotplug_state) */ +#endif + + if (val == PS2_I_DIAGFAIL) { + printk(KERN_ERR "%s: diagnostic failed\n", kmi->name); + return; + } + + /* We are waiting for the keyboard to respond to a kmi_send_and_wait() */ + if (kmi->res == KMI_NO_ACK) { + if (val == PS2_I_RESEND) { + if (kmi->resend_count < 5) + __kmi_send(kmi, kmi->last_tx); + else { + printk(KERN_ERR "%s: too many resends\n", kmi->name); + return; + } + } + + if (val >= 0xee) { + kmi->res = val; + wake_up(&kmi->wait_q); + } + return; + } + +#ifdef CONFIG_VT + kbd_pt_regs = regs; + handle_scancode(val, !(val & 0x80)); + tasklet_schedule(&keyboard_tasklet); +#endif +} + +static void kmi_intr(int nr, void *devid, struct pt_regs *regs) +{ + struct kmi_info *kmi = devid; + u_int status = __raw_readb(KMIIR); + + if (status & KMIIR_RXINTR) { + u_int val = __raw_readb(KMIDATA); + + if (kmi->rx) + kmi->rx(kmi, val, regs); + } +} + +static int kmi_init_keyboard(struct kmi_info *kmi) +{ + kmikbd_init_tasklet.data = (unsigned long)kmi; + tasklet_enable(&kmikbd_init_tasklet); + + return __kmi_init_keyboard(kmi); +} + +/* + * Reset interrupt handler + */ +static void __init +kmi_reset_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs) +{ + if (kmi->state == KMI_RESET) { + if (val == PS2_I_ACK) + kmi->state = KMI_RESET_POR; + else { + val = KMI_NO_ACK; + goto finished; + } + } else if (kmi->state == KMI_RESET_POR) { +finished: + kmi->res = val; + kmi->state = KMI_RESET_DONE; + kmi->rx = NULL; + wake_up(&kmi->wait_q); + } +} + +/* + * Reset the device plugged into this interface + */ +static int __init kmi_reset(struct kmi_info *kmi) +{ + u_int res; + int ret = 0; + + kmi->state = KMI_RESET; + kmi->rx = kmi_reset_intr; + res = kmi_send_and_wait(kmi, PS2_O_RESET, HZ); + kmi->rx = NULL; + + if (res != PS2_I_BAT_OK) { + printk(KERN_ERR "%s: reset failed; ", kmi->name); + if (kmi->res != KMI_NO_ACK) + printk("code 0x%2.2x\n", kmi->res); + else + printk("no ack\n"); + ret = -EINVAL; + } + return ret; +} + +static int __init kmi_init_one_interface(struct kmi_info *kmi) +{ + u_int stat; + int ret = -ENODEV; + + init_waitqueue_head(&kmi->wait_q); + + printk(KERN_INFO "%s at 0x%8.8x on irq %d (%s)\n", kmi->name, + kmi->base, kmi->irq, kmi_type[kmi->type]); + + /* + * Initialise the KMI interface + */ + __raw_writeb(kmi->divisor, KMICLKDIV); + __raw_writeb(KMICR_EN, KMICR); + + /* + * Check that the data and clock lines are OK. + */ + stat = __raw_readb(KMISTAT); + if ((stat & (KMISTAT_IC|KMISTAT_ID)) != (KMISTAT_IC|KMISTAT_ID)) { + printk(KERN_ERR "%s: %s%s%sline%s stuck low\n", kmi->name, + (stat & KMISTAT_IC) ? "" : "clock ", + (stat & (KMISTAT_IC | KMISTAT_ID)) ? "" : "and ", + (stat & KMISTAT_ID) ? "" : "data ", + (stat & (KMISTAT_IC | KMISTAT_ID)) ? "" : "s"); + goto bad; + } + + /* + * Claim the appropriate interrupts + */ + ret = request_irq(kmi->irq, kmi_intr, 0, kmi->name, kmi); + if (ret) + goto bad; + + /* + * Enable the receive interrupt, and reset the device. + */ + __raw_writeb(KMICR_EN | KMICR_RXINTREN, KMICR); + kmi->present = 1; + kmi->present = kmi_reset(kmi) == 0; + + switch (kmi->type) { + case KMI_KEYBOARD: + ret = kmi_init_keyboard(kmi); + break; + +#ifdef CONFIG_KMI_MOUSE + case KMI_MOUSE: + ret = kmi_init_mouse(kmi); + break; +#endif + } + + return ret; + +bad: + /* + * Oh dear, the interface was bad, disable it. + */ + __raw_writeb(0, KMICR); + return ret; +} + +#ifdef CONFIG_VT +/* + * The fragment between #ifdef above and #endif * CONFIG_VT * + * is from the pc_keyb.c driver. It is not copyrighted under the + * above notice. This code is by various authors; please see + * drivers/char/pc_keyb.c for further information. + */ + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static u_char e0_keys[128] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + E0_KPENTER, E0_RCTRL, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, E0_KPSLASH, 0, E0_PRSCR, + E0_RALT, 0, 0, 0, + 0, E0_F13, E0_F14, E0_HELP, + E0_DO, E0_F17, 0, 0, + 0, 0, E0_BREAK, E0_HOME, + E0_UP, E0_PGUP, 0, E0_LEFT, + E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END, + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, + 0, 0, 0, 0, + 0, 0, 0, E0_MSLW, + E0_MSRW, E0_MSTM, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, E0_MACRO, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +#ifdef CONFIG_MAGIC_SYSRQ +u_char kmi_kbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +int kmi_kbd_setkeycode(u_int scancode, u_int keycode) +{ + if (scancode < 128 || scancode > 255 || keycode > 127) + return -EINVAL; + e0_keys[scancode - 128] = keycode; + return 0; +} + +int kmi_kbd_getkeycode(u_int scancode) +{ + if (scancode < 128 || scancode > 255) + return -EINVAL; + return e0_keys[scancode - 128]; +} + +int kmi_kbd_translate(u_char scancode, u_char *keycode, char raw_mode) +{ + static int prev_scancode = 0; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + /* 0xff is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + int old_scancode = prev_scancode; + + prev_scancode = 0; + switch (old_scancode) { + case 0xe0: + /* + * The keyboard maintains its own internal caps lock + * and num lock status. In caps lock mode, E0 AA + * precedes make code and E0 2A follows break code. + * In numlock mode, E0 2A precedes make code, and + * E0 AA follows break code. We do our own book- + * keeping, so we will just ignore these. + * + * For my keyboard there is no caps lock mode, but + * there are both Shift-L and Shift-R modes. The + * former mode generates E0 2A / E0 AA pairs, the + * latter E0 B6 / E0 36 pairs. So, we should also + * ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { + if (!raw_mode) + printk(KERN_INFO "kbd: unknown " + "scancode e0 %02x\n", + scancode); + return 0; + } + break; + + case 0xe1: + if (scancode == 0x1d) + prev_scancode = 0x100; + else { + if (!raw_mode) + printk(KERN_INFO "kbd: unknown " + "scancode e1 %02x\n", + scancode); + return 0; + } + break; + + case 0x100: + if (scancode == 0x45) + *keycode = E1_PAUSE; + else { + if (!raw_mode) + printk(KERN_INFO "kbd: unknown " + "scan code e1 1d %02x\n", + scancode); + return 0; + } + break; + } + } else + *keycode = scancode; + return 1; +} + +char kmi_kbd_unexpected_up(u_char keycode) +{ + return 0x80; +} + +void kmi_kbd_leds(u_char leds) +{ + struct kmi_info *kmi = kmi_keyb; + u_int ret; + + if (kmi) { + ret = kmi_send_and_wait(kmi, PS2_O_INDICATORS, HZ); + if (ret != KMI_NO_ACK) + ret = kmi_send_and_wait(kmi, leds, HZ); + if (ret == KMI_NO_ACK) + kmi->present = 0; + } +} + +int __init kmi_kbd_init(void) +{ + int ret = -ENODEV; + + if (kmi_keyb) { + strcpy(kmi_keyb->name, "kmikbd"); + ret = kmi_init_one_interface(kmi_keyb); + } + + if (ret == 0) { + k_setkeycode = kmi_kbd_setkeycode; + k_getkeycode = kmi_kbd_getkeycode; + k_translate = kmi_kbd_translate; + k_unexpected_up = kmi_kbd_unexpected_up; + k_leds = kmi_kbd_leds; +#ifdef CONFIG_MAGIC_SYSRQ + k_sysrq_xlate = kmi_kbd_sysrq_xlate; + k_sysrq_key = 0x54; +#endif + } + + return ret; +} + +#endif /* CONFIG_VT */ + +int register_kmi(struct kmi_info *kmi) +{ + struct kmi_info **kmip = NULL; + int ret; + + if (kmi->type == KMI_KEYBOARD) + kmip = &kmi_keyb; + else if (kmi->type == KMI_MOUSE) + kmip = &kmi_mouse; + + ret = -EINVAL; + if (kmip) { + ret = -EBUSY; + if (!*kmip) { + *kmip = kmi; + ret = 0; + } + } + + return ret; +} + +#ifdef CONFIG_KMI_MOUSE +static int __init kmi_init(void) +{ + int ret = -ENODEV; + + if (kmi_mouse) { + strcpy(kmi_mouse->name, "kmimouse"); + ret = kmi_init_one_interface(kmi_mouse); + } + + return ret; +} + +__initcall(kmi_init); +#endif --- /dev/null +++ linux-2.4.27/drivers/char/anakin_ts.c @@ -0,0 +1,208 @@ +/* + * linux/drivers/char/anakin_ts.c + * + * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 18-Apr-2001 TTC Created + * 23-Oct-2001 dwmw2 Cleanup + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * TSBUF_SIZE must be a power of two + */ +#define ANAKIN_TS_MINOR 16 +#define TSBUF_SIZE 256 +#define NEXT(index) (((index) + 1) & (TSBUF_SIZE - 1)) + +static unsigned short buffer[TSBUF_SIZE][4]; +static int head, tail; +static DECLARE_WAIT_QUEUE_HEAD(queue); +static DECLARE_MUTEX(open_sem); +static spinlock_t tailptr_lock = SPIN_LOCK_UNLOCKED; +static struct fasync_struct *fasync; + +/* + * Interrupt handler and standard file operations + */ +static void +anakin_ts_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int status = __raw_readl(IO_BASE + IO_CONTROLLER + 0x24); + + /* + * iPAQ format (u16 pressure, x, y, millisecs) + */ + switch (status >> 20 & 3) { + case 0: + return; + case 2: + buffer[head][0] = 0; + break; + default: + buffer[head][0] = 0x7f; + } + + if (unlikely((volatile int)tail == NEXT(head))) { + /* Run out of space in the buffer. Move the tail pointer */ + spin_lock(&tailptr_lock); + + if ((volatile int)tail == NEXT(head)) { + tail = NEXT(NEXT(head)); + } + spin_unlock(&tailptr_lock); + } + + buffer[head][1] = status >> 2 & 0xff; + buffer[head][2] = status >> 12 & 0xff; + buffer[head][3] = jiffies; + mb(); + head = NEXT(head); + + wake_up_interruptible(&queue); + kill_fasync(&fasync, SIGIO, POLL_IN); + +} + +static ssize_t +anakin_ts_read(struct file *filp, char *buf, size_t count, loff_t *l) +{ + unsigned short data[4]; + ssize_t written = 0; + + if (head == tail) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + if (wait_event_interruptible(queue, (volatile int)head != (volatile int)tail)) + return -ERESTARTSYS; + } + + while ((volatile int)head != (volatile int)tail && count >= sizeof data) { + /* Copy the data out with the spinlock held, so the + interrupt can't fill the buffer and move the tail + pointer while we're doing it */ + spin_lock_irq(&tailptr_lock); + + memcpy(data, buffer[tail], sizeof data); + tail = NEXT(tail); + + spin_unlock_irq(&tailptr_lock); + + if (copy_to_user(buf, data, sizeof data)) + return -EFAULT; + count -= sizeof data; + buf += sizeof data; + written += sizeof data; + } + return written ? written : -EINVAL; +} + +static unsigned int +anakin_ts_poll(struct file *filp, poll_table *wait) +{ + poll_wait(filp, &queue, wait); + return head != tail ? POLLIN | POLLRDNORM : 0; +} + +static int +anakin_ts_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + /* + * Future ioctl goes here + */ + return 0; +} + +static int +anakin_ts_open(struct inode *inode, struct file *filp) +{ + if (down_trylock(&open_sem)) + return -EBUSY; + return 0; +} + +static int +anakin_ts_fasync(int fd, struct file *filp, int on) +{ + return fasync_helper(fd, filp, on, &fasync); +} + +static int +anakin_ts_release(struct inode *inode, struct file *filp) +{ + anakin_ts_fasync(-1, filp, 0); + up(&open_sem); + return 0; +} + +static struct file_operations anakin_ts_fops = { + owner: THIS_MODULE, + read: anakin_ts_read, + poll: anakin_ts_poll, + ioctl: anakin_ts_ioctl, + open: anakin_ts_open, + release: anakin_ts_release, + fasync: anakin_ts_fasync, +}; + +static struct miscdevice anakin_ts_miscdev = { + ANAKIN_TS_MINOR, + "anakin_ts", + &anakin_ts_fops +}; + +/* + * Initialization and exit routines + */ +int __init +anakin_ts_init(void) +{ + int retval; + + if ((retval = request_irq(IRQ_TOUCHSCREEN, anakin_ts_handler, + SA_INTERRUPT, "anakin_ts", 0))) { + printk(KERN_WARNING "anakin_ts: failed to get IRQ\n"); + return retval; + } + __raw_writel(1, IO_BASE + IO_CONTROLLER + 8); + misc_register(&anakin_ts_miscdev); + + printk(KERN_NOTICE "Anakin touchscreen driver initialised\n"); + + return 0; +} + +void __exit +anakin_ts_exit(void) +{ + __raw_writel(0, IO_BASE + IO_CONTROLLER + 8); + free_irq(IRQ_TOUCHSCREEN, 0); + misc_deregister(&anakin_ts_miscdev); +} + +module_init(anakin_ts_init); +module_exit(anakin_ts_exit); + +MODULE_AUTHOR("Tak-Shing Chan "); +MODULE_DESCRIPTION("Anakin touchscreen driver"); +MODULE_SUPPORTED_DEVICE("touchscreen/anakin"); --- /dev/null +++ linux-2.4.27/drivers/char/cerf_keyb.c @@ -0,0 +1,380 @@ +/* + cerf_keyb.c: This is the end. Daniel is writing a device driver!!! +*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define KBD_REPORT_UNKN + +#define KBD_REPORT_ERR /* Report keyboard errors */ +#define KBD_REPORT_UNKN /* Report unknown scan codes */ +#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ +#define KBD_NO_DATA (-1) /* No data */ +#define KBD_REPEAT_START (0x20) +#define KBD_REPEAT_CONTINUE (0x05) +#define KBD_KEY_DOWN_MAX (0x10) +#define UINT_LEN (20) +#define SC_LIM (69) +#define KBD_ROWS (5) +#define KBD_COLUMNS (8) + +#define KBD_KEYUP (0x80) +#define KBD_MODESCAN (0x7f) +#define KBD_CAPSSCAN (0x3a) +#define KBD_SHIFTSCAN (0x2a) +#define KBD_NUMCURSCAN (0x7c) +#define KBD_CTRLSCAN (0x1d) +#define KBD_ALTSCAN (0x38) + +#define KBD_UP_OFF (0) +#define KBD_UP_ON (1) +#define KBD_DOWN (2) +#define KBD_DOWN_HOLD (3) + + + +static unsigned char handle_kbd_event(void); +static unsigned char kbd_read_input(void); +static void column_set(unsigned int column); +static int scancodes(unsigned char codeval[KBD_ROWS][KBD_COLUMNS]); + +static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +static struct timer_list kbd_timer; + +static short mode_ena = 0; +static short numcur_ena = 0; +static short shift_ena = 0; + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 +#define E1_PAUSE 119 +#define E0_MACRO 112 +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +#define E0_OK 124 +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +static unsigned char cerf_normal_map[KBD_ROWS][KBD_COLUMNS] = { + {KBD_ALTSCAN, KBD_MODESCAN, 0x1e, 0x30, 0x2e, 0x20, 0x00, 0x00}, + {0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x00}, + {0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x00}, + {0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x00}, + {0x2c, KBD_SHIFTSCAN, KBD_CTRLSCAN, 0x39, KBD_NUMCURSCAN, 0x2b, 0x1c, 0x00} +}; + +static unsigned char cerf_mode_map[KBD_ROWS][KBD_COLUMNS] = { + {0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00}, + {0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00}, // + {0x0d, 0x0c, 0x37, 0x35, 0x0d, 0x48, 0x28, 0x00}, + {0x01, 0x33, 0x34, 0x00, 0x4b, 0x27, 0x4d, 0x00}, // + {0x0f, 0x00, KBD_CAPSSCAN, 0x0e, 0x00, 0x50, 0x00, 0x00} +}; + +static unsigned char cerf_numcur_map[KBD_ROWS][KBD_COLUMNS] = { + {0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00}, + {0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00}, + {0x0d, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x4d, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00} +}; + +static void column_set(unsigned int column) +{ + if (column < 0) + { + CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_A, 0xFF, 0xFF); + CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_B, 0xFF, 0xFF); + } + else + { + if(column < 4) + { + CERF_PDA_CPLD_Set(CERF_PDA_CPLD_KEYPAD_A, 1 << (column % 4), 0xFF); + CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_B, 0xFF, 0xFF); + } + else + { + CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_A, 0xFF, 0xFF); + CERF_PDA_CPLD_Set(CERF_PDA_CPLD_KEYPAD_B, 1 << (column % 4), 0xFF); + } + } +} + +static int scancodes(unsigned char codeval[KBD_ROWS][KBD_COLUMNS]) +{ + int i, j; + + for(i = 0; i < KBD_COLUMNS; i++) + { + column_set(i); + udelay(50); + for(j = 0; j < KBD_ROWS; j++) + { + if(mode_ena) + codeval[j][i] = (GPLR & (1 << (20 + j)))?(cerf_mode_map[j][i]?cerf_mode_map[j][i]:cerf_normal_map[j][i]):0; + else if(numcur_ena) + codeval[j][i] = (GPLR & (1 << (20 + j)))?(cerf_numcur_map[j][i]?cerf_numcur_map[j][i]:cerf_normal_map[j][i]):0; + else + codeval[j][i] = (GPLR & (1 << (20 + j)))?cerf_normal_map[j][i]:0; + } + } + column_set(-1); + + return 0; +} + +static unsigned char kbd_read_input(void) +{ + int i, j, k, l; + unsigned char prev; + static unsigned char count = 0; + + static unsigned char oldcodes[KBD_ROWS][KBD_COLUMNS]={{0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}}; + unsigned char inputcode[KBD_ROWS][KBD_COLUMNS]; + + memset(inputcode, 0, sizeof(unsigned char) * (KBD_ROWS * KBD_COLUMNS)); + scancodes(inputcode); + + for(i = 0; i < KBD_COLUMNS; i++) + { + for(j = 0; j < KBD_ROWS; j++) + { +// if(oldcodes[j][i] == 0xe0) +// oldcodes[j][i] = + if(oldcodes[j][i] != inputcode[j][i]) + { + // Value of the key before entering this function + prev = oldcodes[j][i]; + + // KEYUP + if(inputcode[j][i] == 0 && oldcodes[j][i] != 0 && !(oldcodes[j][i] & KBD_KEYUP)) + { + oldcodes[j][i] |= KBD_KEYUP; + + if(mode_ena == KBD_UP_ON) + mode_ena = KBD_UP_OFF; + if(prev == KBD_MODESCAN) + if(mode_ena == KBD_DOWN_HOLD) + mode_ena = KBD_UP_OFF; + else if(mode_ena == KBD_DOWN) + mode_ena = KBD_UP_ON; + if(mode_ena == KBD_DOWN) + mode_ena = KBD_DOWN_HOLD; + } + // RESET KEYUP + else if(oldcodes[j][i] & KBD_KEYUP) + oldcodes[j][i] = 0; + // KEY DOWN + else + { + oldcodes[j][i] = inputcode[j][i]; + + // Parse out mode modifiers before the keyboard interpreter can touch them + if(inputcode[j][i] == KBD_MODESCAN) + { + if(!mode_ena) + mode_ena = KBD_DOWN; + continue; + } + if(inputcode[j][i] == KBD_NUMCURSCAN) + { + numcur_ena = numcur_ena?0:1; + continue; + } + } + //printk("Modified: (%#x,%#x), ipv:%#x, To: (%#.2x), From: (%#.2x), Flags:%d,%d,%d\r\n", j, i, inputcode[j][i], oldcodes[j][i], prev, mode_ena, shift_ena, numcur_ena); + return oldcodes[j][i]; + } + } + } + + return (unsigned char)(KBD_NO_DATA); +} + +int cerf_kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode; + + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + if (scancode == 0x2a || scancode == 0x36) + return 0; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else + *keycode = scancode; + return 1; +} + +static inline void handle_keyboard_event(unsigned char scancode) +{ + if(scancode != (unsigned char)(KBD_NO_DATA)) + { +#ifdef CONFIG_VT + handle_scancode(scancode, !(scancode & KBD_KEYUP)); +#endif + tasklet_schedule(&keyboard_tasklet); + } +} + +static unsigned char handle_kbd_event(void) +{ + unsigned char scancode; + + scancode = kbd_read_input(); + handle_keyboard_event(scancode); + + return 0; +} + +/* Handle the automatic interrupts handled by the timer */ +static void keyboard_interrupt(unsigned long foo) +{ + spin_lock_irq(&kbd_controller_lock); + handle_kbd_event(); + spin_unlock_irq(&kbd_controller_lock); + + kbd_timer.expires = 8 + jiffies; + kbd_timer.data = 0x00000000; + kbd_timer.function = (void(*)(unsigned long))&keyboard_interrupt; + + add_timer(&kbd_timer); +} + +void cerf_leds(unsigned char leds) +{ +} +char cerf_unexpected_up(unsigned char keycode) +{ +return 0; +} +int cerf_getkeycode(unsigned int scancode) +{ +return 0; +} +int cerf_setkeycode(unsigned int scancode, unsigned int keycode) +{ +return 0; +} + +void cerf_kbd_init_hw(void) +{ + printk("Starting Cerf PDA Keyboard Driver... "); + + k_setkeycode = cerf_setkeycode; + k_getkeycode = cerf_getkeycode; + k_translate = cerf_kbd_translate; + k_unexpected_up = cerf_unexpected_up; + k_leds = cerf_leds; + + GPDR &= ~(GPIO_GPIO(20) | GPIO_GPIO(21) | GPIO_GPIO(22) | GPIO_GPIO(23) | GPIO_GPIO(24)); + kbd_timer.expires = 40 + jiffies; + kbd_timer.data = 0x00000000; + kbd_timer.function = (void(*)(unsigned long))&keyboard_interrupt; + + add_timer(&kbd_timer); + + printk("Done\r\n"); +} --- /dev/null +++ linux-2.4.27/drivers/char/clps711x_keyb.c @@ -0,0 +1,547 @@ +/* + * drivers/char/clps711x_keyb.c + * + * Copyright (C) 2001 Thomas Gleixner + * + * based on drivers/edb7211_keyb.c, which is copyright (C) 2000 Bluemug Inc. + * + * Keyboard driver for ARM Linux on EP7xxx and CS89712 processors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See the file COPYING + * in the main directory of this archive for more details. + * + * + * Hardware: + * + * matrix scan keyboards based on EP7209,7211,7212,7312 and CS89712 + * on chip keyboard scanner. + * Adaption for different machines is done in init function. + * + * Basic Function: + * + * Basicly the driver is interrupt driven. It sets all column drivers + * high. If any key is pressed, a interrupt occures. Now a seperate scan of + * each column is done. This scan is timer based, because we use a keyboard + * interface with decoupling capacitors (neccecary if you want to survive + * EMC compliance tests). Always one line is set high. When next timer event + * occures the scan data on port A are valid. This makes also sure, that no + * spurious keys are scanned. The kbd int on these CPU's is not deglitched! + * After scanning all columns, we switch back to int mode, if no key is + * pressed. If any is pressed we reschedule the scan within a programmable + * delay. If we would switch back to interrupt mode as long as a key is pressed, + * we come right back to the interrupt, because the int. is level triggered ! + * The timer based scan of the seperate columns can also be done in one + * timer event (set fastscan to 1). + * + * Summary: + * The design of this keyboard controller chip is stupid at all ! + * + * Matrix translation: + * The matrix translation table is based on standard XT scancodes. Maybe + * you have to adjust the KEYISPRINTABLE macro if you set other codes. + * + * HandyKey: + * + * On small matrix keyboards you don't have enough keys for operation. + * The intention was to implement a operation mode as it's used on handys. + * You can rotate trough four scancode levels and produce e.g. with a 4x3 + * matrix 4*3*4 = 48 different keycodes. That's basicly enough for editing + * filenames or things like that. The HandyKey function takes care about + * nonprintable keys like cursors, backspace, del ... + * If a key is pressed and is a printable keycode, the code is put to the + * main keyboard handler and a cursor left is applied. If you press the same + * key again, the current character is deleted and the next level character + * is applied. (e.g. 1, a, b, c, 1 ....). If you press a different key, the + * driver applies cursor right, before processing the new key. + * The autocomplete feature moves the cursor right, if you do not press a + * key within a programmable time. + * If HandyKey is off, the keyboard behaviour is that of a standard keyboard + * HandyKey can be en/disabled from userspace with the proc/keyboard entry + * + * proc/keyboard: + * + * Read access gives back the actual state of the HandyKey function + * h:0 Disabled + * h:1 Enabled + * Write access has two functions. Changing the HandyKey mode and applying + * a different scancode translation table. + * Syntax is: h:0 disable Handykey + * h:1 enabled Handykey + * t:array[256] of bytes Transfer translation table + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +void clps711x_kbd_init_hw(void); + +/* + * Values for the keyboard column scan control register. + */ +#define KBSC_HI 0x0 /* All driven high */ +#define KBSC_LO 0x1 /* All driven low */ +#define KBSC_X 0x2 /* All high impedance */ +#define KBSC_COL0 0x8 /* Column 0 high, others high impedance */ +#define KBSC_COL1 0x9 /* Column 1 high, others high impedance */ +#define KBSC_COL2 0xa /* Column 2 high, others high impedance */ +#define KBSC_COL3 0xb /* Column 3 high, others high impedance */ +#define KBSC_COL4 0xc /* Column 4 high, others high impedance */ +#define KBSC_COL5 0xd /* Column 5 high, others high impedance */ +#define KBSC_COL6 0xe /* Column 6 high, others high impedance */ +#define KBSC_COL7 0xf /* Column 7 high, others high impedance */ + +/* +* Keycodes for cursor left/right and delete (used by HandyKey) +*/ +#define KEYCODE_CLEFT 0x4b +#define KEYCODE_CRIGHT 0x4d +#define KEYCODE_DEL 0x53 +#define KEYISPRINTABLE(code) ( (code > 0x01 && code < 0x37 && code != 0x1c \ + && code != 0x0e) || code == 0x39) + +/* Simple translation table for the SysRq keys */ +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char clps711x_kbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +/* + * This table maps row/column keyboard matrix positions to XT scancodes. + * It's a default table, which can be overriden by writing to proc/keyboard + */ +#ifdef CONFIG_ARCH_AUTCPU12 +static unsigned char autcpu12_scancode[256] = +{ +/* Column: + Row 0 1 2 3 4 5 6 7 */ +/* A0 */ 0x08, 0x09, 0x0a, 0x0e, 0x05, 0x06, 0x00, 0x00, +/* A1 */ 0x07, 0x53, 0x02, 0x03, 0x04, 0x0f, 0x00, 0x00, +/* A2 */ 0x0c, 0x0b, 0x33, 0x1c, 0xff, 0x4b, 0x00, 0x00, +/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, +/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +/* A0 */ 0x1e, 0x20, 0x22, 0x0e, 0x24, 0x32, 0x00, 0x00, +/* A1 */ 0x19, 0x53, 0x1f, 0x2f, 0x15, 0x0f, 0x00, 0x00, +/* A2 */ 0x0c, 0x39, 0x34, 0x1c, 0xff, 0x4b, 0x00, 0x00, +/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, +/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +/* A0 */ 0x30, 0x12, 0x23, 0x0e, 0x25, 0x31, 0x00, 0x00, +/* A1 */ 0x10, 0x53, 0x14, 0x11, 0x2c, 0x0f, 0x00, 0x00, +/* A2 */ 0x0c, 0x0b, 0x27, 0x1c, 0xff, 0x4b, 0x00, 0x00, +/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, +/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +/* A0 */ 0x2e, 0x21, 0x17, 0x0e, 0x26, 0x18, 0x00, 0x00, +/* A1 */ 0x13, 0x53, 0x16, 0x2D, 0x04, 0x0f, 0x00, 0x00, +/* A2 */ 0x0c, 0x39, 0x35, 0x1c, 0xff, 0x4b, 0x00, 0x00, +/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, +/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +#endif + +static int keys[8]; +static int new_keys[8]; +static int previous_keys[8]; + +static int fastscan; +static int scan_interval; +static int scan_delay; +static int last_column; +static int key_is_pressed; + +static unsigned char *act_scancode; + +static struct kbd_handy_key { + int ena; + int code; + int shift; + int autocomplete; + unsigned long expires; + unsigned long delay; + unsigned char left; + unsigned char right; + unsigned char del; +} khandy; + +static struct tq_struct kbd_process_task; +static struct timer_list clps711x_kbd_timer; +static struct timer_list clps711x_kbdhandy_timer; +static struct proc_dir_entry *clps711x_keyboard_proc_entry = NULL; + +/* + * Translate a raw keycode to an XT keyboard scancode. + */ +static int clps711x_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + *keycode = act_scancode[scancode]; + return 1; +} + +/* +* Initialize handykey structure +* clear code, clear shift +* scan scancode for cursor right/left and delete +*/ +static void clps711x_handykey_init(void) { + + int i; + + khandy.ena = 0; + khandy.code = 0; + khandy.shift = 0; + khandy.autocomplete = 0; + for(i = 0; i < 64; i++) { + switch(act_scancode[i]) { + case KEYCODE_CLEFT: khandy.left = i; break; + case KEYCODE_CRIGHT: khandy.right = i; break; + case KEYCODE_DEL: khandy.del = i; break; + } + } +} + +/* +* Check for handy key and process it +*/ +void inline clps711x_checkhandy(int col, int row) { + + int scode, down; + unsigned char kcode; + + scode = (row<<3) + col; + down = keys[col]>>row & 0x01; + kcode = act_scancode[scode]; + + if (!khandy.ena) { + if (khandy.code) { + handle_scancode(khandy.right,1); + handle_scancode(khandy.right,0); + } + khandy.code = 0; + khandy.shift = 0; + khandy.autocomplete = 0; + } + + if(!kcode) + return; + + if (!down || !khandy.ena) { + if (khandy.ena && KEYISPRINTABLE(act_scancode[scode])) + khandy.autocomplete = 1; + else + handle_scancode(scode + khandy.shift, down); + return; + } + + khandy.autocomplete = 0; + if (KEYISPRINTABLE(kcode)) { + if (khandy.code) { + if(khandy.code == (scode|0x100)) { + handle_scancode(khandy.del,1); + handle_scancode(khandy.del,0); + khandy.shift = khandy.shift < 3*64 ? khandy.shift + 64 : 0 ; + } else { + handle_scancode(khandy.right,1); + handle_scancode(khandy.right,0); + khandy.shift = 0; + } + } + handle_scancode(scode + khandy.shift, 1); + handle_scancode(scode + khandy.shift, 0); + khandy.code = scode | 0x100; + handle_scancode(khandy.left,1); + handle_scancode(khandy.left,0); + } else { + if (khandy.code) { + khandy.code = 0; + handle_scancode(khandy.right,1); + handle_scancode(khandy.right,0); + } + khandy.shift = 0; + handle_scancode(scode, down); + } +} + + +/* + * Process the new key data + */ +static void clps711x_kbd_process(void* data) +{ + int col,row,res; + + for (col = 0; col < 8; col++) { + if (( res = previous_keys[col] ^ keys[col]) == 0) + continue; + for(row = 0; row < 8; row++) { + if ( ((res >> row) & 0x01) != 0) + clps711x_checkhandy(col,row); + } + } + /* Update the state variables. */ + memcpy(previous_keys, keys, 8 * sizeof(int)); + + /* reschedule, if autocomplete pending */ + if (khandy.autocomplete) { + khandy.expires = jiffies + khandy.delay; + mod_timer(&clps711x_kbdhandy_timer,khandy.expires); + } + +} + +static char clps711x_unexpected_up(unsigned char scancode) +{ + return 0200; +} + +/* +* Handle timer event, for autocomplete function +* Reschedule keyboard process task +*/ +static void clps711x_kbdhandy_timeout(unsigned long data) +{ + if(khandy.autocomplete) { + khandy.code = 0; + khandy.shift = 0; + khandy.autocomplete = 0; + handle_scancode(khandy.right,1); + handle_scancode(khandy.right,0); + } +} + +/* +* Handle timer event, while in pollmode +*/ +static void clps711x_kbd_timeout(unsigned long data) +{ + int i; + unsigned long flags; + /* + * read bits of actual column or all columns in fastscan-mode + */ + for (i = 0; i < 8; i++) { + new_keys[last_column - KBSC_COL0] = clps_readb(PADR) & 0xff; + key_is_pressed |= new_keys[last_column - KBSC_COL0]; + last_column = last_column < KBSC_COL7 ? last_column + 1 : KBSC_COL0; + local_irq_save(flags); + clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) + | last_column, SYSCON1); + local_irq_restore(flags); + /* + * For fastscan, apply a short delay to settle scanlines + * else break and wait for next timeout + */ + if (fastscan) + udelay(5); + else + break; + } + + if (key_is_pressed) + khandy.autocomplete = 0; + + /* + * switch to interupt mode, if all columns scanned and no key pressed + * else reschedule scan + */ + if (last_column == KBSC_COL0) { + if (!key_is_pressed) { + local_irq_save(flags); + clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) + | KBSC_HI, SYSCON1); + local_irq_restore(flags); + clps_writel(0,KBDEOI); + enable_irq(IRQ_KBDINT); + } else { + clps711x_kbd_timer.expires = jiffies + scan_interval; + add_timer(&clps711x_kbd_timer); + } + key_is_pressed = 0; + memcpy(keys, new_keys, 8 * sizeof(int)); + for (i = 0; i < 8; i++) { + if (previous_keys[i] != keys[i]) { + queue_task(&kbd_process_task, &tq_timer); + return; + } + } + } else { + clps711x_kbd_timer.expires = jiffies + scan_delay; + add_timer(&clps711x_kbd_timer); + } +} + +/* +* Keyboard interrupt, change to scheduling mode +*/ +static void clps711x_kbd_int(int irq, void *dev_id, struct pt_regs *regs) +{ + +#ifdef CONFIG_VT + kbd_pt_regs = regs; +#endif + disable_irq(IRQ_KBDINT); + khandy.autocomplete = 0; + clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) + | KBSC_COL0, SYSCON1); + clps711x_kbd_timer.expires = jiffies + scan_delay; + add_timer(&clps711x_kbd_timer); +} + + +static int clps711x_kbd_proc_keyboard_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < 2) + return -EINVAL; + + return sprintf(page,"h:%d\n",khandy.ena); +} + +static int clps711x_kbd_proc_keyboard_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned char buf[260]; + + if (count < 3|| count > 258) + return -EINVAL; + if (copy_from_user(buf, buffer, count)) + return -EFAULT; + if (buf[1] != ':') + return -EINVAL; + + if (buf[0] == 'h') { + switch (buf[2]) { + case '0': + case '1': + case '2': khandy.ena = buf[2]-'0'; return count; + } + } + + if (buf[0] == 't' && count == 258) { + memcpy(act_scancode,buf+2,256); + /* rescan cursor left/right and del */ + clps711x_handykey_init(); + return count; + } + + return -EINVAL; +} + + +/* + * Initialize the keyboard hardware. + * Set all columns high + * Install interrupt handler + * + * Machine dependent parameters: + * + * fastscan: 0 = timer based scan for each column + * 1 = full scan is done in one timer event + * scan_delay: time between column scans + * setup even if you use fastscan (leeds to timer mode) + * scan_interval: time between full scans + * handy.delay: timeout before last entry get's automatically valid + * + */ +void __init clps711x_kbd_init_hw(void) +{ + + /* + * put here machine dependent init stuff + */ + if (machine_is_autcpu12()) { + fastscan = 0; + scan_interval = 50*HZ/1000; + scan_delay = 20*HZ/1000; + khandy.delay = 750*HZ/1000; + act_scancode = autcpu12_scancode; + } else { + printk("No initialization, keyboard killed\n"); + return; + } + + last_column = KBSC_COL0; + key_is_pressed = 0; + + clps711x_handykey_init(); + + /* Register the /proc entry */ + clps711x_keyboard_proc_entry = create_proc_entry("keyboard", 0444, + &proc_root); + if (clps711x_keyboard_proc_entry == NULL) + printk("Couldn't create the /proc entry for the keyboard\n"); + else { + clps711x_keyboard_proc_entry->read_proc = + &clps711x_kbd_proc_keyboard_read; + clps711x_keyboard_proc_entry->write_proc = + &clps711x_kbd_proc_keyboard_write; + } + + /* Initialize the matrix processing task. */ + k_translate = clps711x_translate; + k_unexpected_up = clps711x_unexpected_up; + kbd_process_task.routine = clps711x_kbd_process; + kbd_process_task.data = 0; + + /* Setup the timer for keyboard polling, after kbd int */ + init_timer(&clps711x_kbd_timer); + clps711x_kbd_timer.function = clps711x_kbd_timeout; + clps711x_kbd_timer.data = 0; + init_timer(&clps711x_kbdhandy_timer); + clps711x_kbdhandy_timer.function = clps711x_kbdhandy_timeout; + clps711x_kbdhandy_timer.data = 1; + + /* Initialise scan hardware, request int */ + clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) + | KBSC_HI, SYSCON1); + request_irq(IRQ_KBDINT, clps711x_kbd_int, 0,"keyboard", NULL); + + printk("clps711x keyboard init done\n"); + +} --- linux-2.4.27/drivers/char/console.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/console.c @@ -72,8 +72,14 @@ * * Removed console_lock, enabled interrupts across all console operations * 13 March 2001, Andrew Morton + * + * Split out con_write_ctrl_* functions from do_con_write & changed + * vc_state to function pointer + * by Russell King , July 1998 */ +#define CONSOLE_WIP + #include #include #include @@ -228,7 +234,7 @@ static inline unsigned short *screenpos(int currcons, int offset, int viewed) { unsigned short *p; - + if (!viewed) p = (unsigned short *)(origin + offset); else if (!sw->con_screen_pos) @@ -729,7 +735,7 @@ else { unsigned short *p = (unsigned short *) kmalloc(ss, GFP_USER); if (!p) { - for (i = first; i < currcons; i++) + for (i = first; i< currcons; i++) if (newscreens[i]) kfree(newscreens[i]); return -ENOMEM; @@ -847,7 +853,7 @@ /* the default colour table, for VGA+ colour systems */ int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa, 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff}; -int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa, +int default_grn[] = {0x00,0x00,0xaa,0xaa,0x00,0x00,0xaa,0xaa, 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff}; int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa, 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff}; @@ -1393,6 +1399,19 @@ need_wrap = 0; } +static int con_write_ctrl_ESnormal(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESesc(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESnonstd(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESpalette(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESsquare(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESgetpars(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESgotpars(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESpercent(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESfunckey(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_EShash(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESsetG0(int, struct tty_struct *, unsigned int); +static int con_write_ctrl_ESsetG1(int, struct tty_struct *, unsigned int); + enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, ESpalette }; @@ -1402,7 +1421,7 @@ { top = 0; bottom = video_num_lines; - vc_state = ESnormal; + vc_state = con_write_ctrl_ESnormal; ques = 0; translate = set_translate(LAT1_MAP,currcons); G0_charset = LAT1_MAP; @@ -1500,328 +1519,426 @@ disp_ctrl = 0; return; case 24: case 26: - vc_state = ESnormal; + vc_state = con_write_ctrl_ESnormal; return; case 27: - vc_state = ESesc; + vc_state = con_write_ctrl_ESesc; return; case 127: del(currcons); return; case 128+27: - vc_state = ESsquare; + vc_state = con_write_ctrl_ESsquare; return; } - switch(vc_state) { - case ESesc: - vc_state = ESnormal; - switch (c) { - case '[': - vc_state = ESsquare; - return; - case ']': - vc_state = ESnonstd; - return; - case '%': - vc_state = ESpercent; - return; - case 'E': - cr(currcons); - lf(currcons); - return; - case 'M': - ri(currcons); - return; - case 'D': - lf(currcons); - return; - case 'H': - tab_stop[x >> 5] |= (1 << (x & 31)); - return; - case 'Z': - respond_ID(tty); - return; - case '7': - save_cur(currcons); - return; - case '8': - restore_cur(currcons); - return; - case '(': - vc_state = ESsetG0; - return; - case ')': - vc_state = ESsetG1; - return; - case '#': - vc_state = EShash; - return; - case 'c': - reset_terminal(currcons,1); - return; - case '>': /* Numeric keypad */ - clr_kbd(kbdapplic); - return; - case '=': /* Appl. keypad */ - set_kbd(kbdapplic); - return; + vc_state(currcons, tty, c); +} + +static int con_write_utf(int currcons, int c) +{ + unsigned int chr; + + /* Combine UTF-8 into Unicode */ + /* Incomplete characters silently ignored */ + if (c < 0x80) { + utf_count = 0; + return c; + } + + if (utf_count > 0 && (c & 0xc0) == 0x80) { + chr = (utf_char << 6) | (c & 0x3f); + utf_count--; + if (utf_count == 0) + return chr; + } else { + unsigned int count; + if ((c & 0xe0) == 0xc0) { + count = 1; + chr = (c & 0x1f); + } else if ((c & 0xf0) == 0xe0) { + count = 2; + chr = (c & 0x0f); + } else if ((c & 0xf8) == 0xf0) { + count = 3; + chr = (c & 0x07); + } else if ((c & 0xfc) == 0xf8) { + count = 4; + chr = (c & 0x03); + } else if ((c & 0xfe) == 0xfc) { + count = 5; + chr = (c & 0x01); + } else { + count = 0; + chr = 0; } - return; - case ESnonstd: - if (c=='P') { /* palette escape sequence */ - for (npar=0; npar='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { - par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ; - if (npar==7) { - int i = par[0]*3, j = 1; - palette[i] = 16*par[j++]; - palette[i++] += par[j++]; - palette[i] = 16*par[j++]; - palette[i++] += par[j++]; - palette[i] = 16*par[j++]; - palette[i] += par[j]; - set_palette(currcons); - vc_state = ESnormal; - } - } else - vc_state = ESnormal; - return; - case ESsquare: - for(npar = 0 ; npar < NPAR ; npar++) - par[npar] = 0; - npar = 0; - vc_state = ESgetpars; - if (c == '[') { /* Function key */ - vc_state=ESfunckey; - return; + utf_count = count; + } + + utf_char = chr; + return -1; +} + +static int con_write_ctrl_ESnormal(int currcons, struct tty_struct *tty, unsigned int c) +{ + return 0; +} + +static int con_write_ctrl_ESesc(int currcons, struct tty_struct *tty, unsigned int c) +{ + vc_state = con_write_ctrl_ESnormal; + switch (c) { + case '[': + vc_state = con_write_ctrl_ESsquare; + break; + case ']': + vc_state = con_write_ctrl_ESnonstd; + break; + case '%': + vc_state = con_write_ctrl_ESpercent; + break; + case 'E': + cr(currcons); + lf(currcons); + break; + case 'M': + ri(currcons); + break; + case 'D': + lf(currcons); + break; + case 'H': + tab_stop[x >> 5] |= (1 << (x & 31)); + break; + case 'Z': + respond_ID(tty); + break; + case '7': + save_cur(currcons); + break; + case '8': + restore_cur(currcons); + return 1; + case '(': + vc_state = con_write_ctrl_ESsetG0; + break; + case ')': + vc_state = con_write_ctrl_ESsetG1; + break; + case '#': + vc_state = con_write_ctrl_EShash; + break; + case 'c': + reset_terminal(currcons,1); + return 1; + case '>': /* Numeric keypad */ + clr_kbd(kbdapplic); + break; + case '=': /* Appl. keypad */ + set_kbd(kbdapplic); + break; + } + return 0; +} + +static int con_write_ctrl_ESnonstd(int currcons, struct tty_struct *tty, unsigned int c) +{ + switch (c) { + case 'P': /* palette escape sequence */ + for (npar=0; npar='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { + par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ; + if (npar==7) { + int i = par[0]*3, j = 1; + palette[i] = 16*par[j++]; + palette[i++] += par[j++]; + palette[i] = 16*par[j++]; + palette[i++] += par[j++]; + palette[i] = 16*par[j++]; + palette[i] += par[j]; + set_palette(currcons); + vc_state = con_write_ctrl_ESnormal; } - ques = (c=='?'); - if (ques) - return; - case ESgetpars: - if (c==';' && npar='0' && c<='9') { - par[npar] *= 10; - par[npar] += c-'0'; - return; - } else vc_state=ESgotpars; - case ESgotpars: - vc_state = ESnormal; - switch(c) { - case 'h': - set_mode(currcons,1); - return; - case 'l': - set_mode(currcons,0); - return; - case 'c': - if (ques) { - if (par[0]) - cursor_type = par[0] | (par[1]<<8) | (par[2]<<16); - else - cursor_type = CUR_DEFAULT; - return; - } - break; - case 'm': - if (ques) { - clear_selection(); - if (par[0]) - complement_mask = par[0]<<8 | par[1]; - else - complement_mask = s_complement_mask; - return; - } - break; - case 'n': - if (!ques) { - if (par[0] == 5) - status_report(tty); - else if (par[0] == 6) - cursor_report(currcons,tty); - } - return; + } else + vc_state = con_write_ctrl_ESnormal; + return 0; +} + +static int con_write_ctrl_ESsquare(int currcons, struct tty_struct *tty, unsigned int c) +{ + for(npar = 0 ; npar < NPAR ; npar++) + par[npar] = 0; + npar = 0; + vc_state = con_write_ctrl_ESgetpars; + if (c == '[') { /* Function key */ + vc_state = con_write_ctrl_ESfunckey; + return 0; + } + ques = (c=='?'); + if (ques) + return 0; + return con_write_ctrl_ESgetpars(currcons, tty, c); +} + +static int con_write_ctrl_ESgetpars(int currcons, struct tty_struct *tty, unsigned int c) +{ + if (c==';' && npar='0' && c<='9') { + par[npar] *= 10; + par[npar] += c-'0'; + return 0; + } else vc_state = con_write_ctrl_ESgotpars; + return con_write_ctrl_ESgotpars(currcons, tty, c); +} + +static int con_write_ctrl_ESgotpars(int currcons, struct tty_struct *tty, unsigned int c) +{ + vc_state = con_write_ctrl_ESnormal; + switch(c) { + case 'h': + set_mode(currcons,1); + return 0; + case 'l': + set_mode(currcons,0); + return 0; + case 'c': + if (ques) { + if (par[0]) + cursor_type = par[0] | (par[1]<<8) | (par[2]<<16); + else + cursor_type = CUR_DEFAULT; + return 0; } + break; + case 'm': if (ques) { - ques = 0; - return; + clear_selection(); + if (par[0]) + complement_mask = par[0]<<8 | par[1]; + else + complement_mask = s_complement_mask; + return 0; } - switch(c) { - case 'G': case '`': - if (par[0]) par[0]--; - gotoxy(currcons,par[0],y); - return; - case 'A': - if (!par[0]) par[0]++; - gotoxy(currcons,x,y-par[0]); - return; - case 'B': case 'e': - if (!par[0]) par[0]++; - gotoxy(currcons,x,y+par[0]); - return; - case 'C': case 'a': - if (!par[0]) par[0]++; - gotoxy(currcons,x+par[0],y); - return; - case 'D': - if (!par[0]) par[0]++; - gotoxy(currcons,x-par[0],y); - return; - case 'E': - if (!par[0]) par[0]++; - gotoxy(currcons,0,y+par[0]); - return; - case 'F': - if (!par[0]) par[0]++; - gotoxy(currcons,0,y-par[0]); - return; - case 'd': - if (par[0]) par[0]--; - gotoxay(currcons,x,par[0]); - return; - case 'H': case 'f': - if (par[0]) par[0]--; - if (par[1]) par[1]--; - gotoxay(currcons,par[1],par[0]); - return; - case 'J': - csi_J(currcons,par[0]); - return; - case 'K': - csi_K(currcons,par[0]); - return; - case 'L': - csi_L(currcons,par[0]); - return; - case 'M': - csi_M(currcons,par[0]); - return; - case 'P': - csi_P(currcons,par[0]); - return; - case 'c': - if (!par[0]) - respond_ID(tty); - return; - case 'g': - if (!par[0]) - tab_stop[x >> 5] &= ~(1 << (x & 31)); - else if (par[0] == 3) { - tab_stop[0] = - tab_stop[1] = - tab_stop[2] = - tab_stop[3] = - tab_stop[4] = 0; - } - return; - case 'm': - csi_m(currcons); - return; - case 'q': /* DECLL - but only 3 leds */ - /* map 0,1,2,3 to 0,1,2,4 */ - if (par[0] < 4) - setledstate(kbd_table + currcons, - (par[0] < 3) ? par[0] : 4); - return; - case 'r': - if (!par[0]) - par[0]++; - if (!par[1]) - par[1] = video_num_lines; - /* Minimum allowed region is 2 lines */ - if (par[0] < par[1] && - par[1] <= video_num_lines) { - top=par[0]-1; - bottom=par[1]; - gotoxay(currcons,0,0); - } - return; - case 's': - save_cur(currcons); - return; - case 'u': - restore_cur(currcons); - return; - case 'X': - csi_X(currcons, par[0]); - return; - case '@': - csi_at(currcons,par[0]); - return; - case ']': /* setterm functions */ - setterm_command(currcons); - return; + break; + case 'n': + if (!ques) { + if (par[0] == 5) + status_report(tty); + else if (par[0] == 6) + cursor_report(currcons,tty); } - return; - case ESpercent: - vc_state = ESnormal; - switch (c) { - case '@': /* defined in ISO 2022 */ - utf = 0; - return; - case 'G': /* prelim official escape code */ - case '8': /* retained for compatibility */ - utf = 1; - return; + return 0; + } + if (ques) { + ques = 0; + return 0; + } + switch(c) { + case 'G': case '`': + if (par[0]) par[0]--; + gotoxy(currcons,par[0],y); + break; + case 'A': + if (!par[0]) par[0]++; + gotoxy(currcons,x,y-par[0]); + break; + case 'B': case 'e': + if (!par[0]) par[0]++; + gotoxy(currcons,x,y+par[0]); + break; + case 'C': case 'a': + if (!par[0]) par[0]++; + gotoxy(currcons,x+par[0],y); + break; + case 'D': + if (!par[0]) par[0]++; + gotoxy(currcons,x-par[0],y); + break; + case 'E': + if (!par[0]) par[0]++; + gotoxy(currcons,0,y+par[0]); + break; + case 'F': + if (!par[0]) par[0]++; + gotoxy(currcons,0,y-par[0]); + break; + case 'd': + if (par[0]) par[0]--; + gotoxay(currcons,x,par[0]); + break; + case 'H': case 'f': + if (par[0]) par[0]--; + if (par[1]) par[1]--; + gotoxay(currcons,par[1],par[0]); + break; + case 'J': + csi_J(currcons,par[0]); + break; + case 'K': + csi_K(currcons,par[0]); + break; + case 'L': + csi_L(currcons,par[0]); + break; + case 'M': + csi_M(currcons,par[0]); + break; + case 'P': + csi_P(currcons,par[0]); + break; + case 'c': + if (!par[0]) + respond_ID(tty); + break; + case 'g': + if (!par[0]) + tab_stop[x >> 5] &= ~(1 << (x & 31)); + else if (par[0] == 3) { + tab_stop[0] = + tab_stop[1] = + tab_stop[2] = + tab_stop[3] = + tab_stop[4] = 0; } - return; - case ESfunckey: - vc_state = ESnormal; - return; - case EShash: - vc_state = ESnormal; - if (c == '8') { - /* DEC screen alignment test. kludge :-) */ - video_erase_char = - (video_erase_char & 0xff00) | 'E'; - csi_J(currcons, 2); - video_erase_char = - (video_erase_char & 0xff00) | ' '; - do_update_region(currcons, origin, screenbuf_size/2); + break; + case 'm': + csi_m(currcons); + return 1; + case 'q': /* DECLL - but only 3 leds */ + /* map 0,1,2,3 to 0,1,2,4 */ + if (par[0] < 4) + setledstate(kbd_table + currcons, + (par[0] < 3) ? par[0] : 4); + break; + case 'r': + if (!par[0]) + par[0]++; + if (!par[1]) + par[1] = video_num_lines; + /* Minimum allowed region is 2 lines */ + if (par[0] < par[1] && + par[1] <= video_num_lines) { + top=par[0]-1; + bottom=par[1]; + gotoxay(currcons,0,0); } - return; - case ESsetG0: - if (c == '0') - G0_charset = GRAF_MAP; - else if (c == 'B') - G0_charset = LAT1_MAP; - else if (c == 'U') - G0_charset = IBMPC_MAP; - else if (c == 'K') - G0_charset = USER_MAP; - if (charset == 0) - translate = set_translate(G0_charset,currcons); - vc_state = ESnormal; - return; - case ESsetG1: - if (c == '0') - G1_charset = GRAF_MAP; - else if (c == 'B') - G1_charset = LAT1_MAP; - else if (c == 'U') - G1_charset = IBMPC_MAP; - else if (c == 'K') - G1_charset = USER_MAP; - if (charset == 1) - translate = set_translate(G1_charset,currcons); - vc_state = ESnormal; - return; - default: - vc_state = ESnormal; + break; + case 's': + save_cur(currcons); + break; + case 'u': + restore_cur(currcons); + return 1; + case 'X': + csi_X(currcons, par[0]); + break; + case '@': + csi_at(currcons,par[0]); + break; + case ']': /* setterm functions */ + setterm_command(currcons); + break; + } + return 0; +} + +static int con_write_ctrl_ESpercent(int currcons, struct tty_struct *tty, unsigned int c) +{ + vc_state = con_write_ctrl_ESnormal; + switch (c) { + case '@': /* defined in ISO 2022 */ + utf = 0; + break; + case 'G': /* prelim official escape code */ + case '8': /* retained for compatibility */ + utf = 1; + break; + } + return 0; +} + +static int con_write_ctrl_ESfunckey(int currcons, struct tty_struct *tty, unsigned int c) +{ + vc_state = con_write_ctrl_ESnormal; + return 0; +} + +static int con_write_ctrl_EShash(int currcons, struct tty_struct *tty, unsigned int c) +{ + vc_state = con_write_ctrl_ESnormal; + if (c == '8') { + /* DEC screen alignment test. kludge :-) */ + video_erase_char = + (video_erase_char & 0xff00) | 'E'; + csi_J(currcons, 2); + video_erase_char = + (video_erase_char & 0xff00) | ' '; + do_update_region(currcons, origin, screenbuf_size/2); + } + return 0; +} + +static int con_write_ctrl_ESsetG0(int currcons, struct tty_struct *tty, unsigned int c) +{ + switch (c) { + case '0': + G0_charset = GRAF_MAP; + break; + case 'B': + G0_charset = LAT1_MAP; + break; + case 'U': + G0_charset = IBMPC_MAP; + break; + case 'K': + G0_charset = USER_MAP; + break; + } + if (charset == 0) { + translate = set_translate(G0_charset,currcons); + return 1; + } + vc_state = con_write_ctrl_ESnormal; + return 0; +} + +static int con_write_ctrl_ESsetG1(int currcons, struct tty_struct *tty, unsigned int c) +{ + switch (c) { + case '0': + G1_charset = GRAF_MAP; + break; + case 'B': + G1_charset = LAT1_MAP; + break; + case 'U': + G1_charset = IBMPC_MAP; + break; + case 'K': + G1_charset = USER_MAP; + break; + } + if (charset == 1) { + translate = set_translate(G1_charset,currcons); + return 1; } + vc_state = con_write_ctrl_ESnormal; + return 0; } /* This is a temporary buffer used to prepare a tty console write @@ -1855,7 +1972,7 @@ unsigned long draw_from = 0, draw_to = 0; struct vt_struct *vt = (struct vt_struct *)tty->driver_data; u16 himask, charmask; - const unsigned char *orig_buf = NULL; + const unsigned char *orig_buf; int orig_count; if (in_interrupt()) @@ -1913,42 +2030,12 @@ count--; if (utf) { - /* Combine UTF-8 into Unicode */ - /* Incomplete characters silently ignored */ - if(c > 0x7f) { - if (utf_count > 0 && (c & 0xc0) == 0x80) { - utf_char = (utf_char << 6) | (c & 0x3f); - utf_count--; - if (utf_count == 0) - tc = c = utf_char; - else continue; - } else { - if ((c & 0xe0) == 0xc0) { - utf_count = 1; - utf_char = (c & 0x1f); - } else if ((c & 0xf0) == 0xe0) { - utf_count = 2; - utf_char = (c & 0x0f); - } else if ((c & 0xf8) == 0xf0) { - utf_count = 3; - utf_char = (c & 0x07); - } else if ((c & 0xfc) == 0xf8) { - utf_count = 4; - utf_char = (c & 0x03); - } else if ((c & 0xfe) == 0xfc) { - utf_count = 5; - utf_char = (c & 0x01); - } else - utf_count = 0; + tc = con_write_utf(currcons, c); + if (tc < 0) continue; - } - } else { - tc = c; - utf_count = 0; - } - } else { /* no utf */ - tc = translate[toggle_meta ? (c|0x80) : c]; - } + c = tc; + } else /* no utf */ + tc = translate[toggle_meta ? (c|0x80) : c]; /* If the original code was a control character we * only allow a glyph to be displayed if the code is @@ -1966,7 +2053,7 @@ && (c != 127 || disp_ctrl) && (c != 128+27); - if (vc_state == ESnormal && ok) { + if (vc_state == con_write_ctrl_ESnormal && ok) { /* Now try to find out how to display it */ tc = conv_uni_to_pc(vc_cons[currcons].d, tc); if ( tc == -4 ) { @@ -2112,7 +2199,7 @@ if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1)) currcons = kmsg_redirect - 1; - /* read `x' only after setting currecons properly (otherwise + /* read `x' only after setting currcons properly (otherwise the `x' macro will read the x of the foreground console). */ myx = x; @@ -2475,8 +2562,8 @@ console_driver.init_termios = tty_std_termios; console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; /* Tell tty_register_driver() to skip consoles because they are - * registered before kmalloc() is ready. We'll patch them in later. - * See comments at console_init(); see also con_init_devfs(). + * registered before kmalloc() is ready. We'll patch them in later. + * See comments at console_init(); see also con_init_devfs(). */ console_driver.flags |= TTY_DRIVER_NO_DEVFS; console_driver.refcount = &console_refcount; @@ -2719,7 +2806,7 @@ if (console_blank_hook && console_blank_hook(1)) return; - if (vesa_blank_mode) + if (vesa_blank_mode) sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); } @@ -2893,7 +2980,7 @@ if (!op->height) { /* Need to guess font height [compat] */ int h, i; u8 *charmap = op->data, tmp; - + /* If from KDFONTOP ioctl, don't allow things which can be done in userland, so that we can get rid of this soon */ if (!(op->flags & KD_FONT_FLAG_OLD)) @@ -2940,18 +3027,18 @@ op->data = old_op.data; if (!rc && !set) { int c = (op->width+7)/8 * 32 * op->charcount; - + if (op->data && op->charcount > old_op.charcount) rc = -ENOSPC; if (!(op->flags & KD_FONT_FLAG_OLD)) { - if (op->width > old_op.width || + if (op->width > old_op.width || op->height > old_op.height) rc = -ENOSPC; } else { if (op->width != 8) rc = -EIO; else if ((old_op.height && op->height > old_op.height) || - op->height > 32) + op->height > 32) rc = -ENOSPC; } if (!rc && op->data && copy_to_user(op->data, temp, c)) --- /dev/null +++ linux-2.4.27/drivers/char/ds1307.c @@ -0,0 +1,604 @@ +/* + * ds1307.c + * + * Device driver for Dallas Semiconductor's Real Time Controller DS1307. + * + * Copyright (C) 2002 Intrinsyc Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ds1307.h" + +#define DEBUG 0 + +#if DEBUG +static unsigned int rtc_debug = DEBUG; +#else +#define rtc_debug 0 /* gcc will remove all the debug code for us */ +#endif + +static unsigned short slave_address = DS1307_I2C_SLAVE_ADDR; + +struct i2c_driver ds1307_driver; +struct i2c_client *ds1307_i2c_client = 0; + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short normal_addr[] = { DS1307_I2C_SLAVE_ADDR, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + normal_i2c: normal_addr, + normal_i2c_range: ignore, + probe: ignore, + probe_range: ignore, + ignore: ignore, + ignore_range: ignore, + force: ignore, +}; + +static int ds1307_rtc_ioctl( struct inode *, struct file *, unsigned int, unsigned long); +static int ds1307_rtc_open(struct inode *inode, struct file *file); +static int ds1307_rtc_release(struct inode *inode, struct file *file); + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + ioctl: ds1307_rtc_ioctl, + open: ds1307_rtc_open, + release: ds1307_rtc_release, +}; + +static struct miscdevice ds1307_rtc_miscdev = { + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int ds1307_probe(struct i2c_adapter *adap); +static int ds1307_detach(struct i2c_client *client); +static int ds1307_command(struct i2c_client *client, unsigned int cmd, void *arg); + +struct i2c_driver ds1307_driver = { + name: "DS1307", + id: I2C_DRIVERID_DS1307, + flags: I2C_DF_NOTIFY, + attach_adapter: ds1307_probe, + detach_client: ds1307_detach, + command: ds1307_command +}; + +static spinlock_t ds1307_rtc_lock = SPIN_LOCK_UNLOCKED; + +#define DAT(x) ((unsigned int)((x)->data)) /* keep the control register info */ + +static int +ds1307_readram( char *buf, int len) +{ + unsigned long flags; + unsigned char ad[1] = { 0 }; + int ret; + struct i2c_msg msgs[2] = { + { ds1307_i2c_client->addr , 0, 1, ad }, + { ds1307_i2c_client->addr , I2C_M_RD, len, buf } }; + + spin_lock_irqsave(&ds1307_rtc_lock, flags); + ret = i2c_transfer(ds1307_i2c_client->adapter, msgs, 2); + spin_unlock_irqrestore(&ds1307_rtc_lock,flags); + + return ret; +} + +static void +ds1307_dumpram( void) +{ + unsigned char buf[DS1307_RAM_SIZE]; + int ret; + + ret = ds1307_readram( buf, DS1307_RAM_SIZE); + + if( ret > 0) + { + int i; + for( i=0; iaddr , 0, 1, ad }, + { ds1307_i2c_client->addr , I2C_M_RD, 1, buf } + }; + unsigned char ctrl_info; + int ret; + + if( enable) + ctrl_info = SQW_ENABLE | RATE_32768HZ; + else + ctrl_info = SQW_DISABLE; + ds1307_command(ds1307_i2c_client, DS1307_SETCTRL, &ctrl_info); + + /* read addr 0 (Clock-Halt bit and second counter */ + ret = i2c_transfer(ds1307_i2c_client->adapter, msgs, 2); + + if( enable) + buf[1] = buf[0] & ~CLOCK_HALT; /* clear Clock-Halt bit */ + else + buf[1] = buf[0] | CLOCK_HALT; /* set Clock-Halt bit */ + buf[0] = 0; /* control register address on DS1307 */ + + ret = i2c_master_send(ds1307_i2c_client, (char *)buf, 2); +} + +static int +ds1307_attach(struct i2c_adapter *adap, int addr, unsigned short flags,int kind) +{ + struct i2c_client *c; + unsigned char buf[1], ad[1] = { 7 }; + struct i2c_msg msgs[2] = { + { addr , 0, 1, ad }, + { addr , I2C_M_RD, 1, buf } + }; + int ret; + + c = (struct i2c_client *)kmalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + + strcpy(c->name, "DS1307"); + c->id = ds1307_driver.id; + c->flags = 0; + c->addr = addr; + c->adapter = adap; + c->driver = &ds1307_driver; + c->data = NULL; + + ret = i2c_transfer(c->adapter, msgs, 2); + + if ( ret == 2 ) + { + DAT(c) = buf[0]; + } + else + printk ("ds1307_attach(): i2c_transfer() returned %d.\n",ret); + + ds1307_i2c_client = c; + ds1307_enable_clock( 1); + + return i2c_attach_client(c); +} + +static int +ds1307_probe(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, ds1307_attach); +} + +static int +ds1307_detach(struct i2c_client *client) +{ + i2c_detach_client(client); + ds1307_enable_clock( 0); + + return 0; +} + +static void +ds1307_convert_to_time( struct rtc_time *dt, char *buf) +{ + dt->tm_sec = BCD_TO_BIN(buf[0]); + dt->tm_min = BCD_TO_BIN(buf[1]); + + if ( TWELVE_HOUR_MODE(buf[2]) ) + { + dt->tm_hour = HOURS_12(buf[2]); + if (HOURS_AP(buf[2])) /* PM */ + { + dt->tm_hour += 12; + } + } + else /* 24-hour-mode */ + { + dt->tm_hour = HOURS_24(buf[2]); + } + + dt->tm_mday = BCD_TO_BIN(buf[4]); + /* dt->tm_mon is zero-based */ + dt->tm_mon = BCD_TO_BIN(buf[5]) - 1; + /* year is 1900 + dt->tm_year */ + dt->tm_year = BCD_TO_BIN(buf[6]) + 100; + + if( rtc_debug > 2) + { + printk("ds1307_get_datetime: year = %d\n", dt->tm_year); + printk("ds1307_get_datetime: mon = %d\n", dt->tm_mon); + printk("ds1307_get_datetime: mday = %d\n", dt->tm_mday); + printk("ds1307_get_datetime: hour = %d\n", dt->tm_hour); + printk("ds1307_get_datetime: min = %d\n", dt->tm_min); + printk("ds1307_get_datetime: sec = %d\n", dt->tm_sec); + } +} + +static int +ds1307_get_datetime(struct i2c_client *client, struct rtc_time *dt) +{ + unsigned char buf[7], addr[1] = { 0 }; + struct i2c_msg msgs[2] = { + { client->addr, 0, 1, addr }, + { client->addr, I2C_M_RD, 7, buf } + }; + int ret = -EIO; + + memset(buf, 0, sizeof(buf)); + + ret = i2c_transfer(client->adapter, msgs, 2); + + if (ret == 2) { + ds1307_convert_to_time( dt, buf); + ret = 0; + } + else + printk("ds1307_get_datetime(), i2c_transfer() returned %d\n",ret); + + return ret; +} + +static int +ds1307_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo) +{ + unsigned char buf[8]; + int ret, len = 4; + + if( rtc_debug > 2) + { + printk("ds1307_set_datetime: tm_year = %d\n", dt->tm_year); + printk("ds1307_set_datetime: tm_mon = %d\n", dt->tm_mon); + printk("ds1307_set_datetime: tm_mday = %d\n", dt->tm_mday); + printk("ds1307_set_datetime: tm_hour = %d\n", dt->tm_hour); + printk("ds1307_set_datetime: tm_min = %d\n", dt->tm_min); + printk("ds1307_set_datetime: tm_sec = %d\n", dt->tm_sec); + } + + buf[0] = 0; /* register address on DS1307 */ + buf[1] = (BIN_TO_BCD(dt->tm_sec)); + buf[2] = (BIN_TO_BCD(dt->tm_min)); + buf[3] = (BIN_TO_BCD(dt->tm_hour)); + + if (datetoo) { + len = 8; + /* we skip buf[4] as we don't use day-of-week. */ + buf[5] = (BIN_TO_BCD(dt->tm_mday)); + buf[6] = (BIN_TO_BCD(dt->tm_mon + 1)); + /* The year only ranges from 0-99, we are being passed an offset from 1900, + * and the chip calulates leap years based on 2000, thus we adjust by 100. + */ + buf[7] = (BIN_TO_BCD(dt->tm_year - 100)); + } + ret = i2c_master_send(client, (char *)buf, len); + if (ret == len) + ret = 0; + else + printk("ds1307_set_datetime(), i2c_master_send() returned %d\n",ret); + + + return ret; +} + +static int +ds1307_get_ctrl(struct i2c_client *client, unsigned char *ctrl) +{ + *ctrl = DAT(client); + + return 0; +} + +static int +ds1307_set_ctrl(struct i2c_client *client, unsigned char *cinfo) +{ + unsigned char buf[2]; + int ret; + + + buf[0] = 7; /* control register address on DS1307 */ + buf[1] = *cinfo; + /* save the control reg info in the client data field so that get_ctrl + * function doesn't have to do an I2C transfer to get it. + */ + DAT(client) = buf[1]; + + ret = i2c_master_send(client, (char *)buf, 2); + + return ret; +} + +static int +ds1307_read_mem(struct i2c_client *client, struct rtc_mem *mem) +{ + unsigned char addr[1]; + struct i2c_msg msgs[2] = { + { client->addr, 0, 1, addr }, + { client->addr, I2C_M_RD, mem->nr, mem->data } + }; + + if ( (mem->loc < DS1307_RAM_ADDR_START) || + ((mem->loc + mem->nr -1) > DS1307_RAM_ADDR_END) ) + return -EINVAL; + + addr[0] = mem->loc; + + return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; +} + +static int +ds1307_write_mem(struct i2c_client *client, struct rtc_mem *mem) +{ + unsigned char addr[1]; + struct i2c_msg msgs[2] = { + { client->addr, 0, 1, addr }, + { client->addr, 0, mem->nr, mem->data } + }; + + if ( (mem->loc < DS1307_RAM_ADDR_START) || + ((mem->loc + mem->nr -1) > DS1307_RAM_ADDR_END) ) + return -EINVAL; + + addr[0] = mem->loc; + + return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; +} + +static int +ds1307_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + switch (cmd) { + case DS1307_GETDATETIME: + return ds1307_get_datetime(client, arg); + + case DS1307_SETTIME: + return ds1307_set_datetime(client, arg, 0); + + case DS1307_SETDATETIME: + return ds1307_set_datetime(client, arg, 1); + + case DS1307_GETCTRL: + return ds1307_get_ctrl(client, arg); + + case DS1307_SETCTRL: + return ds1307_set_ctrl(client, arg); + + case DS1307_MEM_READ: + return ds1307_read_mem(client, arg); + + case DS1307_MEM_WRITE: + return ds1307_write_mem(client, arg); + + default: + return -EINVAL; + } +} + +static int +ds1307_rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int +ds1307_rtc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int +ds1307_rtc_ioctl( struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct rtc_time wtime; + int status = 0; + + switch (cmd) { + default: + case RTC_UIE_ON: + case RTC_UIE_OFF: + case RTC_PIE_ON: + case RTC_PIE_OFF: + case RTC_AIE_ON: + case RTC_AIE_OFF: + case RTC_ALM_SET: + case RTC_ALM_READ: + case RTC_IRQP_READ: + case RTC_IRQP_SET: + case RTC_EPOCH_READ: + case RTC_EPOCH_SET: + case RTC_WKALM_SET: + case RTC_WKALM_RD: + status = -EINVAL; + break; + + case RTC_RD_TIME: + spin_lock_irqsave(&ds1307_rtc_lock, flags); + ds1307_command( ds1307_i2c_client, DS1307_GETDATETIME, &wtime); + spin_unlock_irqrestore(&ds1307_rtc_lock,flags); + + if( copy_to_user((void *)arg, &wtime, sizeof (struct rtc_time))) + status = -EFAULT; + break; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + { + status = -EACCES; + break; + } + + if (copy_from_user(&wtime, (struct rtc_time *)arg, sizeof(struct rtc_time)) ) + { + status = -EFAULT; + break; + } + + spin_lock_irqsave(&ds1307_rtc_lock, flags); + ds1307_command( ds1307_i2c_client, DS1307_SETDATETIME, &wtime); + spin_unlock_irqrestore(&ds1307_rtc_lock,flags); + break; + } + + return status; +} + +static char * +ds1307_mon2str( unsigned int mon) +{ + char *mon2str[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + if( mon > 11) return "error"; + else return mon2str[ mon]; +} + +static int ds1307_rtc_proc_output( char *buf) +{ +#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no") + unsigned char ram[DS1307_RAM_SIZE]; + int ret; + + char *p = buf; + + ret = ds1307_readram( ram, DS1307_RAM_SIZE); + if( ret > 0) + { + int i; + struct rtc_time dt; + char text[9]; + + p += sprintf(p, "DS1307 (64x8 Serial Real Time Clock)\n"); + + ds1307_convert_to_time( &dt, ram); + p += sprintf(p, "Date/Time : %02d-%s-%04d %02d:%02d:%02d\n", + dt.tm_mday, ds1307_mon2str(dt.tm_mon), dt.tm_year + 1900, + dt.tm_hour, dt.tm_min, dt.tm_sec); + + p += sprintf(p, "Clock halted : %s\n", CHECK(ram[0],0x80)); + p += sprintf(p, "24h mode : %s\n", CHECK(ram[2],0x40)); + p += sprintf(p, "Square wave enabled : %s\n", CHECK(ram[7],0x10)); + p += sprintf(p, "Freq : "); + + switch( ram[7] & 0x03) + { + case RATE_1HZ: + p += sprintf(p, "1Hz\n"); + break; + case RATE_4096HZ: + p += sprintf(p, "4.096kHz\n"); + break; + case RATE_8192HZ: + p += sprintf(p, "8.192kHz\n"); + break; + case RATE_32768HZ: + default: + p += sprintf(p, "32.768kHz\n"); + break; + + } + + p += sprintf(p, "RAM dump:\n"); + text[8]='\0'; + for( i=0; i126)) ram[i]='.'; + text[i%8] = ram[i]; + if( (i%8) == 7) p += sprintf(p, "%s\n",text); + } + p += sprintf(p, "\n"); + } + else + { + p += sprintf(p, "Failed to read RTC memory!\n"); + } + + return p - buf; +} + +static int ds1307_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = ds1307_rtc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static __init int ds1307_init(void) +{ + int retval=0; + + if( slave_address != 0xffff) + { + normal_addr[0] = slave_address; + } + + if( normal_addr[0] == 0xffff) + { + printk(KERN_ERR"I2C: Invalid slave address for DS1307 RTC (%#x)\n", + normal_addr[0]); + return -EINVAL; + } + + retval = i2c_add_driver(&ds1307_driver); + + if (retval==0) + { + misc_register (&ds1307_rtc_miscdev); + create_proc_read_entry (PROC_DS1307_NAME, 0, 0, ds1307_rtc_read_proc, NULL); + printk("I2C: DS1307 RTC driver successfully loaded\n"); + + if( rtc_debug) ds1307_dumpram(); + } + return retval; +} + +static __exit void ds1307_exit(void) +{ + remove_proc_entry (PROC_DS1307_NAME, NULL); + misc_deregister(&ds1307_rtc_miscdev); + i2c_del_driver(&ds1307_driver); +} + +module_init(ds1307_init); +module_exit(ds1307_exit); + +MODULE_PARM (slave_address, "i"); +MODULE_PARM_DESC (slave_address, "I2C slave address for DS1307 RTC."); + +MODULE_AUTHOR ("Intrinsyc Software Inc."); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/char/ds1307.h @@ -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 --- /dev/null +++ linux-2.4.27/drivers/char/ds1307.h.rej @@ -0,0 +1,17 @@ +*************** +*** 11,17 **** + #ifndef DS1307_H + #define DS1307_H + +- #if defined(CONFIG_PXA_EMERSON_SBC) || defined(CONFIG_PXA_CERF_BOARD) + #define DS1307_I2C_SLAVE_ADDR 0x68 + #else + #define DS1307_I2C_SLAVE_ADDR 0xffff +--- 11,17 ---- + #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 --- /dev/null +++ linux-2.4.27/drivers/char/edb7211_keyb.c @@ -0,0 +1,335 @@ +/* + * drivers/char/edb7211_keyb.c + * + * Copyright (C) 2000 Blue Mug, Inc. All Rights Reserved. + * + * EDB7211 Keyboard driver for ARM Linux. + * + * The EP7211 keyboard hardware only supports generating interrupts for 64 keys. + * The EBD7211's keyboard has 84 keys. Therefore we need to poll for keys, + * instead of waiting for interrupts. + * + * In a real-world hardware situation, this would be a bad thing. It would + * kill power management. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + + +/* + * The number of jiffies between keyboard scans. + */ +#define KEYBOARD_SCAN_INTERVAL 5 + +/* + * Values for the keyboard column scan control register. + */ +#define KBSC_HI 0x0 /* All driven high */ +#define KBSC_LO 0x1 /* All driven low */ +#define KBSC_X 0x2 /* All high impedance */ +#define KBSC_COL0 0x8 /* Column 0 high, others high impedance */ +#define KBSC_COL1 0x9 /* Column 1 high, others high impedance */ +#define KBSC_COL2 0xa /* Column 2 high, others high impedance */ +#define KBSC_COL3 0xb /* Column 3 high, others high impedance */ +#define KBSC_COL4 0xc /* Column 4 high, others high impedance */ +#define KBSC_COL5 0xd /* Column 5 high, others high impedance */ +#define KBSC_COL6 0xe /* Column 6 high, others high impedance */ +#define KBSC_COL7 0xf /* Column 7 high, others high impedance */ + + +/* XXX: Figure out what these values should be... */ +/* Simple translation table for the SysRq keys */ +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char edb7211_kbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +/* + * Row/column to scancode mappings. + * + * This table maps row/column keyboard matrix positions to XT scancodes. + * + * The port A rows come first, followed by the extended rows. + */ +static unsigned char colrow_2_scancode[128] = +{ +/* Column: + Row 0 1 2 3 4 5 6 7 */ +/* A0 */ 0x01, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x40, 0x41, +/* A1 */ 0x02, 0x07, 0x06, 0x05, 0x04, 0x03, 0x08, 0x09, +/* A2 */ 0x0f, 0x14, 0x13, 0x12, 0x11, 0x10, 0x15, 0x16, +/* A3 */ 0x3a, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x23, 0x24, +/* A4 */ 0x29, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x31, 0x32, +/* A5 */ 0x39, 0x35, 0x6F, 0x52, 0x00, 0x6B, 0x34, 0x33, +/* A6 */ 0x6A, 0x27, 0x28, 0x00, 0x1c, 0x6D, 0x26, 0x25, +/* A7 */ 0x67, 0x19, 0x1a, 0x1b, 0x2b, 0x68, 0x18, 0x17, +/* E0 */ 0x6C, 0x0c, 0x0d, 0x0e, 0x00, 0x66, 0x0b, 0x0a, +/* E1 */ 0x69, 0x44, 0x45, 0x37, 0x46, 0x77, 0x43, 0x42, +/* E2 */ 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* E3 */ 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* E4 */ 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* E5 */ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* E6 */ 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* E7 */ 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * A bitfield array which contains the state of the keyboard after the last + * scan. A bit set in this array corresponds to a key down. Only the lower + * 16 bits of each array element are used. + */ +static unsigned long previous_keys[8]; +static unsigned long keys[8]; + + +/* This will be set to a non-zero value if a key was found to be pressed + * in the last scan. */ +static int key_is_pressed; + +static struct tq_struct kbd_process_task; +static struct timer_list edb7211_kbd_timer; + +/* + * External methods. + */ +void edb7211_kbd_init_hw(void); + +/* + * Internal methods. + */ +static int edb7211_kbd_scan_matrix(u_long* keys); +static void edb7211_kbd_timeout(unsigned long data); +static void edb7211_kbd_process(void* data); + +/* + * Translate a raw keycode to an XT keyboard scancode. + */ +static int +edb7211_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + *keycode = colrow_2_scancode[scancode & 0x7f]; + return 1; +} + +/* + * Scan the keyboard matrix; for each key that is pressed, set the + * corresponding bit in the bitfield array. + * + * The parameter is expected to be an array of 8 32-bit values. Only the lower + * 16 bits of each value is used. Each value contains the row bits for the + * corresponding column. + */ +static int +edb7211_kbd_scan_matrix(u_long* keys) +{ + int column, row, key_pressed; + unsigned char port_a_data, ext_port_data; + + key_pressed = 0; + + /* Drive all the columns low. */ + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_LO, + SYSCON1); + + for (column = 0; column < 8; column++) { + + /* Drive the column high. */ + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | + (KBSC_COL0 + column), SYSCON1); + + /* Read port A and the extended port. */ + port_a_data = clps_readb(PADR) & 0xff; + ext_port_data = __raw_readb(EP7211_VIRT_EXTKBD) & 0xff; + + /* Drive all columns tri-state. */ + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, + SYSCON1); + + /* Look at each column in port A. */ + for (row=0; row < 8; row++) { + /* If the row's bit is set, set the bit in the bitfield. + * Otherwise, clear it. + */ + if (port_a_data & (1 << row)) { + keys[column] |= (1 << row); + key_pressed = 1; + } else { + keys[column] &= ~(1 << row); + } + } + + /* Look at each column in the extended port. */ + for (row=0; row < 8; row++) { + /* If the row's bit is set, set the bit in the bitfield. + * Otherwise, clear it. + */ + if (ext_port_data & (1 << row)) { + keys[column] |= (1 << (row + 8)); + key_pressed = 1; + } else { + keys[column] &= ~(1 << (row + 8)); + } + } + + /* + * Short delay: The example code for the EDB7211 runs an empty + * loop 256 times. At this rate, there were some spurious keys + * generated. I doubled the delay to let the column drives + * settle some. + */ + for (row=0; row < 512; row++) { } + } + + /* If we could use interrupts, we would drive all columns high so + * that interrupts will be generated on key presses. But we can't, + * so we leave all columns floating. + */ + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, + SYSCON1); + + return key_pressed; +} + +/* + * XXX: This is really ugly; this needs to be reworked to have less levels of + * indentation. + */ +static void +edb7211_kbd_timeout(unsigned long data) +{ + /* Schedule the next timer event. */ + edb7211_kbd_timer.expires = jiffies + KEYBOARD_SCAN_INTERVAL; + add_timer(&edb7211_kbd_timer); + + if (edb7211_kbd_scan_matrix(keys) || key_is_pressed) { + queue_task(&kbd_process_task, &tq_timer); + } else { + key_is_pressed = 0; + } +} + +/* + * Process the keys that have been pressed. + */ +static void +edb7211_kbd_process(void* data) +{ + int i; + + /* First check if any keys have been released. */ + if (key_is_pressed) { + for (i=0; i < 8; i++) { + if (previous_keys[i]) { + int row; + + for (row=0; row < 16; row++) { + if ((previous_keys[i] & (1 << row)) && + !(keys[i] & (1 << row))) { + /* Generate the up event. */ + handle_scancode( + (row<<3)+i, 0); + } + } + } + } + } + + key_is_pressed = 0; + + /* Now scan the keys and send press events. */ + for (i=0; i < 8; i++) { + if (keys[i]) { + int row; + + for (row=0; row < 16; row++) { + if (keys[i] & (1 << row)) { + if (previous_keys[i] & (1 << row)) { + /* Generate the hold event. */ + handle_scancode((row<<3)+i, 1); + } else { + /* Generate the down event. */ + handle_scancode((row<<3)+i, 1); + } + + key_is_pressed = 1; + } + } + } + } + + /* Update the state variables. */ + memcpy(previous_keys, keys, 8 * sizeof(unsigned long)); +} + +static char edb7211_unexpected_up(unsigned char scancode) +{ + return 0200; +} + +static void edb7211_leds(unsigned char leds) +{ +} + +/* + * Initialize the keyboard hardware. Set the column drives low and + * start the timer. + */ +void __init +edb7211_kbd_init_hw(void) +{ + k_translate = edb7211_translate; + k_unexpected_up = edb7211_unexpected_up; + k_leds = edb7211_leds; + + /* + * If we had the ability to use interrupts, we would want to drive all + * columns high. But we have more keys than can generate interrupts, so + * we leave them floating. + */ + clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, + SYSCON1); + + /* Initialize the matrix processing task. */ + kbd_process_task.routine = edb7211_kbd_process; + kbd_process_task.data = NULL; + + /* Setup the timer to poll the keyboard. */ + init_timer(&edb7211_kbd_timer); + edb7211_kbd_timer.function = edb7211_kbd_timeout; + edb7211_kbd_timer.data = (unsigned long)NULL; + edb7211_kbd_timer.expires = jiffies + KEYBOARD_SCAN_INTERVAL; + add_timer(&edb7211_kbd_timer); +} + + --- /dev/null +++ linux-2.4.27/drivers/char/epxa_wdt.c @@ -0,0 +1,178 @@ +/* + * Watchdog driver for the Altera Excalibur EPXA1DB + * + * (c) Copyright 2003 Krzysztof Marianski + * Based on SA11x0 Watchdog driver by Oleg Drokin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This material is provided "AS-IS" and at no charge + * + * (c) Copyright 2003 Krzysztof Marianski + * + * 1/08/2003 Initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WATCHDOG00_TYPE (volatile unsigned int*) +#include +#include + +#define TIMER_MARGIN 30 /* (secs) Default is 30 seconds */ + +static int margin = TIMER_MARGIN; /* in seconds */ +static int epxa1wdt_users; +static unsigned char last_written_byte; + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout=1; +#else +static int nowayout=0; +#endif + +#ifdef MODULE +MODULE_PARM(margin,"i"); +MODULE_PARM(nowayout, "i"); +#endif + +/* + * Allow only one person to hold it open + */ + +static int epxa1dog_open(struct inode *inode, struct file *file) +{ + if(test_and_set_bit(1,&epxa1wdt_users)) + return -EBUSY; + + /* Reset the Watchdog, just to be sure we don't set + a value close to actual value of WDOG_COUNT register */ + *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1; + *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2; + + /* Activate EPXA1DB Watchdog timer */ + *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))= (EXC_INPUT_CLK_FREQUENCY * margin) & WDOG_CR_TRIGGER_MSK; + + last_written_byte = 'V'; //in case user opens it only to ioctl + return 0; +} + +static int epxa1dog_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer and set lock bit when no special + * character 'V' was last written + */ + + if ((last_written_byte != 'V') && (nowayout)) { + *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE)) |= WDOG_CR_LK_MSK; + printk("No special character 'V' was written to Watchdog just before closing it\n"); + printk("WATCHDOG LOCKED - Reboot expected!!!\n"); + } else + *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))=0; + + epxa1wdt_users = 0; + + return 0; +} + +static ssize_t epxa1dog_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + /* Reset Watchdog timer. */ + if(len) { + *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1; + *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2; + last_written_byte = *data; + return 1; + } + return 0; +} + +static int epxa1dog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + identity: "EPXA Watchdog", + }; + + switch(cmd){ + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)); +// case WDIOC_GETSTATUS: //TODO +// return put_user(0,(int *)arg); +// case WDIOC_GETBOOTSTATUS: //TODO +// return 0; + case WDIOC_KEEPALIVE: + *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1; + *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2; + return 0; + case WDIOC_SETTIMEOUT: + *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))= (EXC_INPUT_CLK_FREQUENCY * margin) & WDOG_CR_TRIGGER_MSK; + return 0; + case WDIOC_GETTIMEOUT: + return put_user( ((*WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE)))/EXC_INPUT_CLK_FREQUENCY), (int*)arg); + } +} + +static struct file_operations epxa1dog_fops = { + .owner = THIS_MODULE, + .write = epxa1dog_write, + .ioctl = epxa1dog_ioctl, + .open = epxa1dog_open, + .release = epxa1dog_release, +}; + +static struct miscdevice epxa1dog_miscdev= +{ + .minor = WATCHDOG_MINOR, + .name = "EPXA watchdog", + .fops = &epxa1dog_fops +}; + +static int __init epxa1dog_init(void) +{ + int ret; + + ret = misc_register(&epxa1dog_miscdev); + + if (ret) + return ret; + + printk("EPXA Watchdog Timer: timer margin %d sec\n", margin); + printk("EPXA Watchdog Timer: no way out is %s\n", nowayout ? "enabled" : "disabled"); + + return 0; +} + +static void __exit epxa1dog_exit(void) +{ + misc_deregister(&epxa1dog_miscdev); +} + +module_init(epxa1dog_init); +module_exit(epxa1dog_exit); + +MODULE_AUTHOR("Krzysztof Marianski "); +MODULE_DESCRIPTION("EPXA Watchdog Timer"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/char/gc_kbmap.h @@ -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}; + + + --- /dev/null +++ linux-2.4.27/drivers/char/gc_keyb.c @@ -0,0 +1,1145 @@ +/* + * linux/arch/arm/drivers/char/gc_keyb.c + * + * Copyright 2000 Applied Data Systems + * + * Keyboard & Smartio driver for GraphicsClient ARM Linux. + * Graphics Client is SA1110 based single board computer by + * Applied Data Systems (http://www.applieddata.net) + * + * Change log: + * 7-10/6/01 Thomas Thaele + * - Added Keyboard Sniffer on /dev/sio12 + * - First implementation of PC- compatible Scancodes (thanks to pc_keyb.c) + * 3/23/01 Woojung Huh + * Power Management added + * 12/01/00 Woojung Huh + * Bug fixed + * 11/16/00 Woojung Huh [whuh@applieddata.net] + * Added smartio device driver on it + */ + +/* + * Introduced setkeycode, ketkeycode for the GC+ by Thomas Thaele + * GC+ now performs like a real PC on the keyboard. + * Warning: this code is still beta! PrntScrn and Pause keys are not + * completely tested and implemented!!! Keyboard driver can be confused + * by hacking like crazy on the keyboard. (hardware problem on serial line?) + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define ADS_AVR_IRQ 63 + +#define SMARTIO_IOCTL_BASES 's' +#define SMARTIO_KPD_TIMEOUT _IOW(SMARTIO_IOCTL_BASES, 0, int) +#define SMARTIO_KPD_SETUP _IOW(SMARTIO_IOCTL_BASES, 1, short) +#define SMARTIO_BL_CONTROL _IOW(SMARTIO_IOCTL_BASES, 2, char) +#define SMARTIO_BL_CONTRAST _IOW(SMARTIO_IOCTL_BASES, 3, char) +#define SMARTIO_PORT_CONFIG _IOW(SMARTIO_IOCTL_BASES, 4, char) +#define SMARTIO_SNIFFER_TIMEOUT _IOW(SMARTIO_IOCTL_BASES, 5, long) + + +/* Simple translation table for the SysRq keys */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char pckbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +int gc_kbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int gc_kbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + +int gc_kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +// this table converts the hardware dependent codes of a MF-2 Keyboard to +// the codes normally comming out of a i8042. This table is 128 Bytes too +// big, but for stability reasons it should be kept like it is! +// There is no range checking in the code! +static int mf_two_kbdmap[256] = { + 00, 67, 65, 63, 61, 59, 60, 88, 00, 68, 66, 64, 62, 15, 41, 00, + 00, 56, 42, 00, 29, 16, 02, 00, 00, 00, 44, 31, 30, 17, 03, 00, + 00, 46, 45, 32, 18, 05, 04, 00, 00, 57, 47, 33, 20, 19, 06, 00, + 00, 49, 48, 35, 34, 21, 7, 00, 00, 00, 50, 36, 22, 8, 9, 00, + 00, 51, 37, 23, 24, 11, 10, 00, 00, 52, 53, 38, 39, 25, 12, 00, + 00, 00, 40, 00, 26, 13, 00, 00, 58, 54, 28, 27, 00, 43, 00, 00, + 00, 86, 00, 00, 00, 00, 14, 00, 00, 79, 00, 75, 71, 00, 00, 00, + 82, 83, 80, 76, 77, 72, 01, 69, 87, 78, 81, 74, 55, 73, 70, 00, + 00, 00, 00, 65, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; + + +// some texts displayed by the proc_file_system +static char *kbd_sniff[2] = { "off", "on" }; +static char *kbd_sniff_mode[2] = { "passive", "active" }; + +#define PASSIVE 0 +#define ACTIVE 1 + +// is the sniffer active (1) or inactive (0) +static int SNIFFER = 0; +// do we get a copy (SNIFFMODE = PASSIVE) or do we get the original data (SNIFFMODE = ACTIVE) +// and have to reinsert the data +static int SNIFFMODE = PASSIVE; + +// we allow only one process to sniff +static int sniffer_in_use = 0; + +// timeout for the keyboard sniffer -1 = blocking, otherwise timeout in msecs +static long sniffer_timeout = -1; + +// the value we sniffed from the keyboard +static int sniffed_value; + +static char *smartio_version = "1.02 MF-II compatibility patch "; +static char *smartio_date = "Aug-27-2001"; + +static int sio_reset_flag; +static int kbd_press_flag; + +static void send_SSP_msg(unchar *pBuf, int num) +{ + ushort tmp; + int i; + + for (i=0;i 7)) + return 0xFFFF; + + CONV_ADC_CMD.Opt[0] = (unchar) channel; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONV_ADC_CMD, 3); + unlock_smartio(&flags); + + interruptible_sleep_on(&smartio_adc_queue); + + return adc_value & 0x3FF; +} + +static ushort read_sio_port(int port) +{ + unsigned long flags; + ushort ret; + + if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) + return 0xFFFF; + + READ_PORT_CMD.Code = (unchar) port; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &READ_PORT_CMD, 2); + ret = read_SSP_response(1); + unlock_smartio(&flags); + + return ret; +} + +static ushort read_sio_kpd(void) +{ + long timeout; + + // kpd_timeout is mSec order + // interrupt_sleep_on_timeout is based on 10msec timer tick + if (kpd_timeout == -1) { + interruptible_sleep_on(&smartio_kpd_queue); + } + else { + timeout = interruptible_sleep_on_timeout(&smartio_kpd_queue, + kpd_timeout/10); + if (timeout == 0) { + // timeout without keypad input + return 0xFFFF; + } + } + return kpd_value; +} + +static ushort read_sio_sniff(void) +{ + long timeout; + + // kpd_timeout is mSec order + // interrupt_sleep_on_timeout is based on 10msec timer tick + if (sniffer_timeout == -1) { + interruptible_sleep_on(&sniffer_queue); + } + else { + timeout = interruptible_sleep_on_timeout(&sniffer_queue, + sniffer_timeout/10); + if (timeout == 0) { + // timeout without keypad input + return -1; + } + } + return (ushort)sniffed_value; +} + +static struct sio_ver { + uint DevVer; + uint DevType; + uint FwLevel; +}; + +static ushort read_sio_version(struct sio_ver *ptr) +{ + unsigned long flags; + ushort ret; + + // Read Device Version + lock_smartio(&flags); + send_SSP_msg((unchar *) &READ_DEVVER_CMD, 2); + ret = read_SSP_response(1); + unlock_smartio(&flags); + ptr->DevVer = (uint)ret; + // Read Device Type + lock_smartio(&flags); + send_SSP_msg((unchar *) &READ_DEVTYPE_CMD, 2); + ret = read_SSP_response(2); + unlock_smartio(&flags); + // swap MSB & LSB + ret = ((ret & 0xFF) << 8) | ((ret & 0xFF00) >> 8); + ptr->DevType = (uint)ret; + // Read Firmware Level + lock_smartio(&flags); + send_SSP_msg((unchar *) &READ_FWLEVEL_CMD, 2); + ret = read_SSP_response(2); + unlock_smartio(&flags); + // swap MSB & LSB + ret = ((ret & 0xFF) << 8) | ((ret & 0xFF00) >> 8); + ptr->FwLevel = (uint)ret; + + return 0; +} + +static ssize_t sio_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct inode *inode = file->f_dentry->d_inode; + unsigned int minor = MINOR(inode->i_rdev); + ushort *ret = (ushort *)buf; + + switch (minor) { + case SMARTIO_ADC: + if ((*ret = read_sio_adc(buf[0])) != 0xFFFF) + return sizeof(ushort); // 2 bytes + case SMARTIO_PORT_B: + case SMARTIO_PORT_C: + case SMARTIO_PORT_D: + if ((*ret = read_sio_port(minor)) != 0xFFFF) + return sizeof(ushort); + case SMARTIO_VERSION: + if ((read_sio_version((struct sio_ver *)buf)) != 0xFFFF) + return sizeof(struct sio_ver); + case SMARTIO_KEYPAD: + if ((*ret = read_sio_kpd()) != 0xFFFF) + return sizeof(ushort); + case SMARTIO_KBD_SNIFFER: + if ((*ret = read_sio_sniff()) != (ushort)-1) + return 1; + default : + return -ENXIO; + } +} + +static SMARTIO_CMD WRITE_PORT_CMD = { 0x81, 0x00, { 0x00, 0x00 } }; +static SMARTIO_CMD SELECT_OPT_CMD = { 0x80, 0x00, { 0x00, 0x00 } }; +static SMARTIO_CMD CONTROL_BL_CMD = { 0x80, 0x00, { 0x00, 0x00 } }; +static SMARTIO_CMD CONTRAST_BL_CMD = { 0x80, 0x21, { 0x00, 0x00 } }; +static SMARTIO_CMD CONTROL_KPD_CMD = { 0x80, 0x27, { 0x00, 0x00 } }; +static SMARTIO_CMD CONTROL_VEE_CMD = { 0x80, 0x22, { 0x00, 0x00 } }; + +static ushort write_sio_port(int port, unchar value) +{ + unsigned long flags; + + if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) + return 0xFFFF; + + WRITE_PORT_CMD.Code = (unchar) port; + WRITE_PORT_CMD.Opt[0] = (unchar) value; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &WRITE_PORT_CMD, 3); + unlock_smartio(&flags); + + return 0; +} + +static ushort write_sio_select(unchar select) +{ + unsigned long flags; + + if ((select < 1) || (select > 2)) + return 0xFFFF; + + SELECT_OPT_CMD.Code = (unchar) (select + 0x28); + + lock_smartio(&flags); + send_SSP_msg((unchar *) &SELECT_OPT_CMD, 2); + unlock_smartio(&flags); + + return 0; +} + +static ushort control_sio_backlite(int cmd, int value) +{ + unsigned long flags; + + if (cmd == SMARTIO_BL_CONTRAST) { + value &= 0xFF; + CONTRAST_BL_CMD.Opt[0] = (unchar) value; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONTRAST_BL_CMD, 3); + unlock_smartio(&flags); + } + else if (cmd == SMARTIO_BL_CONTROL) { + if (value == 0x00) { + // Backlite OFF + CONTROL_BL_CMD.Code = 0x24; + } + else { + // Backlite ON + CONTROL_BL_CMD.Code = 0x23; + } + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONTROL_BL_CMD, 2); + unlock_smartio(&flags); + } + else + return 0xFFFF; + + return 0; +} + +static ushort control_sio_keypad(int x, int y) +{ + unsigned long flags; + + if ( (x<1) || (x>8) || (y<1) || (y>8)) { + return 0xFFFF; + } + + CONTROL_KPD_CMD.Opt[0] = (unchar) x; + CONTROL_KPD_CMD.Opt[1] = (unchar) y; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONTROL_KPD_CMD, 4); + unlock_smartio(&flags); + + return 0; +} + +static ushort control_sio_vee(int value) +{ + unsigned long flags; + + value &= 0xFF; + CONTROL_VEE_CMD.Opt[0] = (unchar) value; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONTROL_VEE_CMD, 3); + unlock_smartio(&flags); + + return 0; +} + +static ssize_t sio_write(struct file *file, const char *buf, size_t cont, loff_t *ppos) +{ + struct inode *inode = file->f_dentry->d_inode; + unsigned int minor = MINOR(inode->i_rdev); + + switch (minor) { + case SMARTIO_PORT_B: + case SMARTIO_PORT_C: + case SMARTIO_PORT_D: + if (write_sio_port(minor, buf[0]) != 0xFFFF) + return 1; + case SMARTIO_SELECT_OPTION: + if (write_sio_select(buf[0]) != 0xFFFF) + return 1; + case SMARTIO_BACKLITE: + if (control_sio_backlite(SMARTIO_BL_CONTROL, buf[0]) != 0xFFFF) + return 1; + case SMARTIO_KEYPAD: + if (control_sio_keypad(buf[0], buf[1]) != 0xFFFF) + return 2; + case SMARTIO_VEE_PWM: + if (control_sio_vee(buf[0]) != 0xFFFF) + return 1; + case SMARTIO_KBD_SNIFFER: + // here are the scancodes injected + handle_scancode((unchar)buf[0], (buf[0] & 0x80) ? 0 : 1); + wake_up_interruptible(&keyboard_done_queue); + // give some time to process! File IO is a bit faster than manual typing ;-) + udelay(10000); + return 1; + default: + return -ENXIO; + } +} + +static unsigned int sio_poll(struct file *file, struct poll_table_struct *wait) +{ + return 0; +} + +static SMARTIO_CMD IOCTL_PORT_CMD = { 0x81, 0x00, { 0x00, 0x00 } }; + +static ushort ioctl_sio_port(int port, unchar value) +{ + unsigned long flags; + + if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) + return 0xFFFF; + + IOCTL_PORT_CMD.Code = (unchar) port + 0x04; // 0x05 ~ 0x08 + if (port == SMARTIO_PORT_B) { + // Port B has 4 bits only + IOCTL_PORT_CMD.Opt[0] = (unchar) value & 0x0F; + } + else + IOCTL_PORT_CMD.Opt[0] = (unchar) value; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &IOCTL_PORT_CMD, 3); + unlock_smartio(&flags); + + return 0; +} + +static int sio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned int minor = MINOR(inode->i_rdev); + unchar *buf = (unchar *)arg; + + switch (minor) { + case SMARTIO_PORT_B: + case SMARTIO_PORT_C: + case SMARTIO_PORT_D: + if (cmd == SMARTIO_PORT_CONFIG) { + if (ioctl_sio_port(minor, buf[0]) != 0xFFFF) + return 0; + } + return -EINVAL; + case SMARTIO_SELECT_OPTION: + if (write_sio_select(buf[0]) != 0xFFFF) return 0; + return -EINVAL; + case SMARTIO_BACKLITE: + if (cmd == SMARTIO_BL_CONTROL) { + if (control_sio_backlite(SMARTIO_BL_CONTROL, buf[0]) != 0xFFFF) return 0; + } + else if (cmd == SMARTIO_BL_CONTRAST) { + if (control_sio_backlite(SMARTIO_BL_CONTRAST, buf[0]) != 0xFFFF) return 0; + } + else return -EINVAL; + case SMARTIO_KEYPAD: + if (cmd == SMARTIO_KPD_TIMEOUT) { + kpd_timeout = *(long*)buf; + return 0; + } + else if (cmd == SMARTIO_KPD_SETUP) { + if (control_sio_keypad(buf[0], buf[1]) != 0xFFFF) return 0; + } + return -EINVAL; + case SMARTIO_VEE_PWM: + if (control_sio_vee(buf[0]) != 0xFFFF) return 0; + return -EINVAL; + case SMARTIO_KBD_SNIFFER: + if (cmd == SMARTIO_SNIFFER_TIMEOUT) { + sniffer_timeout = *(long*)buf; + if (sniffer_timeout < 0) sniffer_timeout = -1; + // the value will be devided by 10 later on + if (!sniffer_timeout) sniffer_timeout = 10; + return 0; + } + return -EINVAL; + default: + return -ENXIO; + } +} + +static int sio_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + // we open all by default. we only have a special handler for the kbd sniffer + switch (minor) { + case SMARTIO_KBD_SNIFFER: + if (sniffer_in_use) return -EBUSY; + sniffer_in_use = 1; + SNIFFER = 1; + // sniff in active or passive mode + if ((file->f_flags & O_RDWR) == O_RDWR) SNIFFMODE = 1; else SNIFFMODE = 0; + // do we have a blocking or non blocking sniffer? + if ((file->f_flags & O_NONBLOCK) == O_NONBLOCK) sniffer_timeout = 100; else sniffer_timeout = -1; + break; + default: + break; + } + return 0; +} + +static int sio_close(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + switch (minor) { + case SMARTIO_KBD_SNIFFER: + SNIFFER = 0; + SNIFFMODE = 0; + sniffer_in_use = 0; + break; + default: + break; + } + return 0; +} + +static struct file_operations sio_fops = { + read: sio_read, + write: sio_write, + poll: sio_poll, + ioctl: sio_ioctl, + open: sio_open, + release: sio_close, +}; + +static struct proc_dir_entry *sio_dir, *parent_dir = NULL; + +#define SMARTIO_MAJOR 58 +#define MAJOR_NR SMARTIO_MAJOR + +#define PROC_NAME "sio" + +static int sio_read_proc(char *buf, char **start, off_t pos, int count, int *eof, void *data) +{ + char *p = buf; + + p += sprintf(p, "ADS SMARTIO Status: \n"); + p += sprintf(p, "\t Keyboard Interrupt : %lu\n", kbd_int); + p += sprintf(p, "\t Keypad Interrupt : %lu\n", kpd_int); + p += sprintf(p, "\t ADC Interrupt : %lu\n", adc_int); + p += sprintf(p, "\t Keyboard Sniffer : %s mode : %s\n", kbd_sniff[ SNIFFER ], kbd_sniff_mode [ SNIFFMODE ]); + + return (p-buf); +} + +#ifdef CONFIG_PM +static int pm_smartio_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_RESUME: + gc_sio_init(); + break; + case PM_SUSPEND: + // 4/5/01 Woojung + // It checks Keybard received pair of press/release code. + // System can sleep before receiving release code + if (kbd_press_flag) { + interruptible_sleep_on(&keyboard_done_queue); + } + break; + } + + return 0; +} +#endif + +void __init sio_init(void) +{ + if (register_chrdev(MAJOR_NR, "sio", &sio_fops)) { + printk("smartio : unable to get major %d\n", MAJOR_NR); + return; + } + else { + printk("smartio driver initialized. version %s, date:%s\n", + smartio_version, smartio_date); + + if (sio_reset_flag != 1) { + gc_sio_init(); + if (request_irq(ADS_AVR_IRQ, gc_sio_interrupt,0,"sio",NULL) != 0){ + printk("smartio : Could not allocate IRQ!\n"); + return; + } + } + + if ((sio_dir = create_proc_entry(PROC_NAME, 0, parent_dir)) == NULL) { + printk("smartio : Unable to create /proc entry\n"); + return; + } + else { + sio_dir->read_proc = sio_read_proc; +#ifdef CONFIG_PM + pm_register(PM_SYS_DEV, PM_SYS_KBC, pm_smartio_callback); +#endif + } + } +} --- /dev/null +++ linux-2.4.27/drivers/char/gckeymap.c @@ -0,0 +1,262 @@ +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include +#include +#include + +u_short plain_map[NR_KEYS] = { + 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, + 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, + 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, + 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, + 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, + 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, + 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, + 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_map[NR_KEYS] = { + 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, + 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, + 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, + 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, + 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, + 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, + 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, + 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, + 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short altgr_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, + 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, + 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, + 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73, + 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76, + 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, + 0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911, + 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b, + 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, + 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, + 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, + 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short alt_map[NR_KEYS] = { + 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, + 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, + 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, + 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, + 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, + 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, + 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, + 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, + 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907, + 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, + 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, + 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_alt_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, + 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813, + 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816, + 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, + 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a, + 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +ushort *key_maps[MAX_NR_KEYMAPS] = { + plain_map, shift_map, altgr_map, 0, + ctrl_map, shift_ctrl_map, 0, 0, + alt_map, 0, 0, 0, + ctrl_alt_map, 0 +}; + +unsigned int keymap_count = 7; + +/* + * Philosophy: most people do not define more strings, but they who do + * often want quite a lot of string space. So, we statically allocate + * the default and allocate dynamically in chunks of 512 bytes. + */ + +char func_buf[] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, + '\033', '[', '1', '~', 0, + '\033', '[', '2', '~', 0, + '\033', '[', '3', '~', 0, + '\033', '[', '4', '~', 0, + '\033', '[', '5', '~', 0, + '\033', '[', '6', '~', 0, + '\033', '[', 'M', 0, + '\033', '[', 'P', 0, +}; + +char *funcbufptr = func_buf; +int funcbufsize = sizeof(func_buf); +int funcbufleft = 0; /* space left */ + +char *func_table[MAX_NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + func_buf + 115, + func_buf + 120, + func_buf + 125, + func_buf + 130, + func_buf + 135, + func_buf + 140, + func_buf + 145, + 0, + 0, + func_buf + 149, + 0, +}; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'`', 'A', '\300'}, {'`', 'a', '\340'}, + {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, + {'^', 'A', '\302'}, {'^', 'a', '\342'}, + {'~', 'A', '\303'}, {'~', 'a', '\343'}, + {'"', 'A', '\304'}, {'"', 'a', '\344'}, + {'O', 'A', '\305'}, {'o', 'a', '\345'}, + {'0', 'A', '\305'}, {'0', 'a', '\345'}, + {'A', 'A', '\305'}, {'a', 'a', '\345'}, + {'A', 'E', '\306'}, {'a', 'e', '\346'}, + {',', 'C', '\307'}, {',', 'c', '\347'}, + {'`', 'E', '\310'}, {'`', 'e', '\350'}, + {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, + {'^', 'E', '\312'}, {'^', 'e', '\352'}, + {'"', 'E', '\313'}, {'"', 'e', '\353'}, + {'`', 'I', '\314'}, {'`', 'i', '\354'}, + {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, + {'^', 'I', '\316'}, {'^', 'i', '\356'}, + {'"', 'I', '\317'}, {'"', 'i', '\357'}, + {'-', 'D', '\320'}, {'-', 'd', '\360'}, + {'~', 'N', '\321'}, {'~', 'n', '\361'}, + {'`', 'O', '\322'}, {'`', 'o', '\362'}, + {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, + {'^', 'O', '\324'}, {'^', 'o', '\364'}, + {'~', 'O', '\325'}, {'~', 'o', '\365'}, + {'"', 'O', '\326'}, {'"', 'o', '\366'}, + {'/', 'O', '\330'}, {'/', 'o', '\370'}, + {'`', 'U', '\331'}, {'`', 'u', '\371'}, + {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, + {'^', 'U', '\333'}, {'^', 'u', '\373'}, + {'"', 'U', '\334'}, {'"', 'u', '\374'}, + {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, + {'T', 'H', '\336'}, {'t', 'h', '\376'}, + {'s', 's', '\337'}, {'"', 'y', '\377'}, + {'s', 'z', '\337'}, {'i', 'j', '\377'}, +}; + +unsigned int accent_table_size = 68; --- /dev/null +++ linux-2.4.27/drivers/char/gckeymap.map @@ -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 'ÿ' --- linux-2.4.27/drivers/char/generic_serial.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/generic_serial.c @@ -883,6 +883,9 @@ if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n"); } + /* + * should be using tty_get_baud_rate() here -- rmk + */ baudrate = tiosp->c_cflag & CBAUD; if (baudrate & CBAUDEX) { baudrate &= ~CBAUDEX; @@ -957,6 +960,11 @@ unsigned long flags; unsigned long page; + /* + * Do we expect to allocate tmp_buf from an interrupt routine? + * If not, then save_flags() cli() and restore_flags() are + * redundant here and should be replaced by a semaphore. -- rmk + */ save_flags (flags); if (!tmp_buf) { page = get_free_page(GFP_KERNEL); @@ -976,6 +984,11 @@ if (port->flags & ASYNC_INITIALIZED) return 0; + /* + * Do we expect to allocate xmit_buf from an interrupt routine? + * If not, then save_flags() cli() and restore_flags() are + * redundant here and should be replaced by a semaphore. -- rmk + */ if (!port->xmit_buf) { /* We may sleep in get_free_page() */ unsigned long tmp; @@ -1016,7 +1029,7 @@ struct serial_struct sio; if (copy_from_user(&sio, sp, sizeof(struct serial_struct))) - return(-EFAULT); + return -EFAULT; if (!capable(CAP_SYS_ADMIN)) { if ((sio.baud_base != port->baud_base) || --- linux-2.4.27/drivers/char/keyboard.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/keyboard.c @@ -14,13 +14,17 @@ * `Sticky' modifier keys, 951006. * * 11-11-96: SAK should now work in the raw mode (Martin Mares) - * + * * Modified to provide 'generic' keyboard support by Hamish Macdonald * Merge with the m68k keyboard driver and split-off of the PC low-level * parts by Geert Uytterhoeven, May 1997 * * 27-05-97: Added support for the Magic SysRq Key (Martin Mares) * 30-07-98: Dead keys redone, aeb@cwi.nl. + * + * 04-04-1998: Added keyboard autorepeat support (some keyboards don't + * autorepeat, and some keyboard changers interfere with keyboard + * autorepeat settings). - Russell King (rmk@arm.linux.org.uk) */ #include @@ -30,6 +34,7 @@ #include #include #include +#include #include #include @@ -61,6 +66,14 @@ #define KBD_DEFLOCK 0 #endif +/* + * Default autorepeat settings. + * DEFAULT_REPEAT_TIMEOUT is the timeout from the keypress to the first repeat + * DEFAULT_REPEAT_INTERVAL is the timeout between successive repeats + */ +#define DEFAULT_REPEAT_TIMEOUT HZ*300/1000 +#define DEFAULT_REPEAT_INTERVAL HZ*30/1000 + void (*kbd_ledfunc)(unsigned int led); EXPORT_SYMBOL(handle_scancode); EXPORT_SYMBOL(kbd_ledfunc); @@ -82,21 +95,25 @@ static unsigned long key_down[256/BITS_PER_LONG]; static int dead_key_next; -/* +/* * In order to retrieve the shift_state (for the mouse server), either - * the variable must be global, or a new procedure must be created to + * the variable must be global, or a new procedure must be created to * return the value. I chose the former way. */ int shift_state; static int npadch = -1; /* -1 or number assembled on pad */ static unsigned char diacr; static char rep; /* flag telling character repeat */ +static int kbd_repeatkeycode= -1; +static int kbd_repeattimeout = DEFAULT_REPEAT_TIMEOUT; +static int kbd_repeatinterval= DEFAULT_REPEAT_INTERVAL; struct kbd_struct kbd_table[MAX_NR_CONSOLES]; static struct tty_struct **ttytab; static struct kbd_struct * kbd = kbd_table; static struct tty_struct * tty; static unsigned char prev_scancode; +static void kbd_processkeycode(unsigned char scancode, char up_flag, int autorepeat); void compute_shiftstate(void); typedef void (*k_hand)(unsigned char value, char up_flag); @@ -163,7 +180,8 @@ * string, and in both cases we might assume that it is * in utf-8 already. */ -void to_utf8(ushort c) { +void to_utf8(ushort c) +{ if (c < 0x80) put_queue(c); /* 0******* */ else if (c < 0x800) { @@ -182,17 +200,23 @@ * Translation of escaped scancodes to keycodes. * This is now user-settable (for machines were it makes sense). */ - int setkeycode(unsigned int scancode, unsigned int keycode) { - return kbd_setkeycode(scancode, keycode); + return kbd_setkeycode(scancode, keycode); } int getkeycode(unsigned int scancode) { - return kbd_getkeycode(scancode); + return kbd_getkeycode(scancode); } +static void key_callback(unsigned long nr); + +static struct timer_list key_autorepeat_timer = +{ + function: key_callback +}; + void handle_scancode(unsigned char scancode, int down) { unsigned char keycode; @@ -201,6 +225,7 @@ char have_keycode; pm_access(pm_kbd); + add_keyboard_randomness(scancode | up_flag); tty = ttytab? ttytab[fg_console]: NULL; @@ -223,15 +248,15 @@ if (raw_mode) { /* * The following is a workaround for hardware - * which sometimes send the key release event twice + * which sometimes send the key release event twice */ unsigned char next_scancode = scancode|up_flag; if (have_keycode && up_flag && next_scancode==prev_scancode) { /* unexpected 2nd release event */ } else { - /* + /* * Only save previous scancode if it was a key-up - * and had a single-byte scancode. + * and had a single-byte scancode. */ if (!have_keycode) prev_scancode = 1; @@ -256,12 +281,35 @@ * return the keycode if in MEDIUMRAW mode. */ + kbd_processkeycode(keycode, up_flag, 0); + +out: + do_poke_blanked_console = 1; + schedule_console_callback(); +} + +static void +kbd_processkeycode(unsigned char keycode, char up_flag, int autorepeat) +{ + char raw_mode = (kbd->kbdmode == VC_RAW); + if (up_flag) { rep = 0; if(!test_and_clear_bit(keycode, key_down)) up_flag = kbd_unexpected_up(keycode); - } else + } else { rep = test_and_set_bit(keycode, key_down); + /* If the keyboard autorepeated for us, ignore it. + * We do our own autorepeat processing. + */ + if (rep && !autorepeat) + return; + } + + if (kbd_repeatkeycode == keycode || !up_flag || raw_mode) { + kbd_repeatkeycode = -1; + del_timer(&key_autorepeat_timer); + } #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == SYSRQ_KEY) { @@ -275,6 +323,23 @@ } #endif + /* + * Calculate the next time when we have to do some autorepeat + * processing. Note that we do not do autorepeat processing + * while in raw mode but we do do autorepeat processing in + * medium raw mode. + */ + if (!up_flag && !raw_mode) { + kbd_repeatkeycode = keycode; + if (vc_kbd_mode(kbd, VC_REPEAT)) { + if (rep) + key_autorepeat_timer.expires = jiffies + kbd_repeatinterval; + else + key_autorepeat_timer.expires = jiffies + kbd_repeattimeout; + add_timer(&key_autorepeat_timer); + } + } + if (kbd->kbdmode == VC_MEDIUMRAW) { /* soon keycodes will require more than one byte */ put_queue(keycode + up_flag); @@ -343,9 +408,24 @@ #endif } } + rep = 0; out: - do_poke_blanked_console = 1; - schedule_console_callback(); +} + +/* + * This clears the key down arrays when the keyboard is reset. On + * keyboard reset, this must be called before any keycodes are + * received. + */ +void kbd_reset_kdown(void) +{ + int i; + + for (i = 0; i < NR_SHIFT; i++) + k_down[i] = 0; + for (i = 0; i < SIZE(key_down); i++) + key_down[i] = 0; + shift_state = 0; } void put_queue(int ch) @@ -453,7 +533,7 @@ static void decr_console(void) { int i; - + for (i = fg_console-1; i != fg_console; i--) { if (i == -1) i = MAX_NR_CONSOLES-1; @@ -531,7 +611,7 @@ { } -static void do_null() +static void do_null(void) { compute_shiftstate(); } @@ -646,8 +726,8 @@ static void do_pad(unsigned char value, char up_flag) { - static const char *pad_chars = "0123456789+-*/\015,.?()"; - static const char *app_map = "pqrstuvwxylSRQMnnmPQ"; + static const char *pad_chars = "0123456789+-*/\015,.?()#"; + static const char *app_map = "pqrstuvwxylSRQMnnmPQS"; if (up_flag) return; /* no action, if this is a key release */ @@ -748,9 +828,10 @@ } } -/* called after returning from RAW mode or when changing consoles - - recompute k_down[] and shift_state from key_down[] */ -/* maybe called when keymap is undefined, so that shiftkey release is seen */ +/* Called after returning from RAW mode or when changing consoles - + * recompute k_down[] and shift_state from key_down[] + * Maybe called when keymap is undefined so that shift key release is seen + */ void compute_shiftstate(void) { int i, j, k, sym, val; @@ -829,19 +910,22 @@ } /* - * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, - * or (ii) whatever pattern of lights people want to show using KDSETLED, - * or (iii) specified bits of specified words in kernel memory. + * The leds display either + * (i) the status of NumLock, CapsLock, ScrollLock, or + * (ii) whatever pattern of lights people want to show using KDSETLED, or + * (iii) specified bits of specified words in kernel memory. */ static unsigned char ledstate = 0xff; /* undefined */ static unsigned char ledioctl; -unsigned char getledstate(void) { +unsigned char getledstate(void) +{ return ledstate; } -void setledstate(struct kbd_struct *kbd, unsigned int led) { +void setledstate(struct kbd_struct *kbd, unsigned int led) +{ if (!(led & ~7)) { ledioctl = led; kbd->ledmode = LED_SHOW_IOCTL; @@ -857,7 +941,8 @@ } ledptrs[3]; void register_leds(int console, unsigned int led, - unsigned int *addr, unsigned int mask) { + unsigned int *addr, unsigned int mask) +{ struct kbd_struct *kbd = kbd_table + console; if (led < 3) { ledptrs[led].addr = addr; @@ -868,7 +953,8 @@ kbd->ledmode = LED_SHOW_FLAGS; } -static inline unsigned char getleds(void){ +static inline unsigned char getleds(void) +{ struct kbd_struct *kbd = kbd_table + fg_console; unsigned char leds; @@ -915,6 +1001,19 @@ { unsigned char leds = getleds(); + if (rep && kbd_repeatkeycode != -1) { + tty = ttytab? ttytab[fg_console]: NULL; + kbd = kbd_table + fg_console; + + /* This prevents the kbd_key routine from being called + * twice, once by this BH, and once by the interrupt + * routine. + */ + kbd_disable_irq(); + kbd_processkeycode(kbd_repeatkeycode, 0, 1); + kbd_enable_irq(); + } + if (leds != ledstate) { ledstate = leds; kbd_leds(leds); @@ -937,6 +1036,12 @@ tasklet_enable(&keyboard_tasklet); } +static void key_callback(unsigned long nr) +{ + rep = 1; + tasklet_schedule(&keyboard_tasklet); +} + typedef void (pm_kbd_func) (void); pm_callback pm_kbd_request_override = NULL; @@ -953,7 +1058,7 @@ kbd0.slockstate = 0; kbd0.modeflags = KBD_DEFMODE; kbd0.kbdmode = VC_XLATE; - + for (i = 0 ; i < MAX_NR_CONSOLES ; i++) kbd_table[i] = kbd0; @@ -963,7 +1068,7 @@ tasklet_enable(&keyboard_tasklet); tasklet_schedule(&keyboard_tasklet); - + pm_kbd = pm_register(PM_SYS_DEV, PM_SYS_KBC, pm_kbd_request_override); return 0; --- linux-2.4.27/drivers/char/mem.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/mem.c @@ -27,9 +27,6 @@ #include #include -#ifdef CONFIG_I2C -extern int i2c_init_all(void); -#endif #ifdef CONFIG_FB extern void fbmem_init(void); #endif @@ -740,9 +737,6 @@ printk("unable to get major %d for memory devs\n", MEM_MAJOR); memory_devfs_register(); rand_initialize(); -#ifdef CONFIG_I2C - i2c_init_all(); -#endif #if defined (CONFIG_FB) fbmem_init(); #endif --- /dev/null +++ linux-2.4.27/drivers/char/omaha-rtc.c @@ -0,0 +1,566 @@ +/* + * (C) ARM Limited 2002. + * + * Real Time Clock interface for Linux on Omaha + * + * Based on sa1100-rtc.c + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * Date/time conversion routines taken from arch/arm/kernel/time.c + * by Linus Torvalds and Russell King + * and the GNU C Library + * ( ... I love the GPL ... just take what you need! ;) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 1.00 2001-06-08 Nicolas Pitre + * - added periodic timer capability using OSMR1 + * - flag compatibility with other RTC chips + * - permission checks for ioctls + * - major cleanup, partial rewrite + * + * 0.03 2001-03-07 CIH + * - Modify the bug setups RTC clock. + * + * 0.02 2001-02-27 Nils Faerber + * - removed mktime(), added alarm irq clear + * + * 0.01 2000-10-01 Nils Faerber + * - initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "1.00" + +#define epoch 1970 + +#define TIMER_FREQ 3686400 + +#define RTC_DEF_DIVIDER 32768 - 1 +#define RTC_DEF_TRIM 0 + +/* Those are the bits from a classic RTC we want to mimic */ +#define RTC_IRQF 0x80 /* any of the following 3 is active */ +#define RTC_PF 0x40 +#define RTC_AF 0x20 +#define RTC_UF 0x10 + +// bitdefs for rtc registers +#define TICNT_ENABLE 0x80 // Enable tick interrupt +#define TICNT_PERIOD 0x7F // Divisor required for 1Hz tick +#define RTC_ENABLE 0x1 // Enable bit for RTC + +static unsigned long rtc_status; +static unsigned long rtc_irq_data; +static unsigned long rtc_freq = 1024; + +static struct fasync_struct *rtc_async_queue; +static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); + +extern spinlock_t rtc_lock; + +static const unsigned char days_in_mo[] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +#define is_leap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + +// all the alarm and rtc registers +static volatile unsigned int almsec = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMSEC); +static volatile unsigned int almmin = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMMIN); +static volatile unsigned int almhour = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMHOUR); +static volatile unsigned int almday = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMDAY); +static volatile unsigned int almmon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMMON); +static volatile unsigned int almyear = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMYEAR); + +static volatile unsigned int bcdsec = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDSEC); +static volatile unsigned int bcdmin = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDMIN); +static volatile unsigned int bcdhour = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDHOUR); +static volatile unsigned int bcdday = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDDAY); +static volatile unsigned int bcddate = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDDATE); +static volatile unsigned int bcdmon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDMON); +static volatile unsigned int bcdyear = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDYEAR); + +/* + * Converts seconds since 1970-01-01 00:00:00 to Gregorian date. + */ + +static void decodetime (unsigned long t, struct rtc_time *tval) +{ + long days, month, year, rem; + + days = t / 86400; + rem = t % 86400; + tval->tm_hour = rem / 3600; + rem %= 3600; + tval->tm_min = rem / 60; + tval->tm_sec = rem % 60; + tval->tm_wday = (4 + days) % 7; + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) + + year = epoch; + while (days >= (365 + is_leap(year))) { + unsigned long yg = year + days / 365; + days -= ((yg - year) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (year - 1)); + year = yg; + } + tval->tm_year = year - 1900; + tval->tm_yday = days + 1; + + month = 0; + if (days >= 31) { + days -= 31; + month++; + if (days >= (28 + is_leap(year))) { + days -= (28 + is_leap(year)); + month++; + while (days >= days_in_mo[month]) { + days -= days_in_mo[month]; + month++; + } + } + } + tval->tm_mon = month; + tval->tm_mday = days + 1; +} + +// Get alarm time in seconds +static unsigned long get_alarm_time(void) +{ + int sec, min,hour,date,mon,year; + + // Read data from h/w + year = __raw_readb(almyear); + mon = __raw_readb(almmon); + date = __raw_readb(almday); + hour = __raw_readb(almhour); + min = __raw_readb(almmin); + sec = __raw_readb(almsec); + + // convert all the data into binary + year = BCD_TO_BIN(year); + mon = BCD_TO_BIN(mon); + date = BCD_TO_BIN(date); + hour = BCD_TO_BIN(hour); + min = BCD_TO_BIN(min); + sec = BCD_TO_BIN(sec); + + // convert year to 19xx or 20xx as appropriate + if (year > 69) + year += 1900; + else + year += 2000; + + // Now calculate number of seconds since time began... + return mktime(year,mon,date,hour,min,sec); +} + +// Get rtc time in seconds +static unsigned long get_rtc_time(void) +{ + int sec,min,hour,day,date,mon,year; + + // Read data from h/w + year = __raw_readb(bcdyear); + mon = __raw_readb(bcdmon); + date = __raw_readb(bcdday); + day = __raw_readb(bcddate); + hour = __raw_readb(bcdhour); + min = __raw_readb(bcdmin); + sec = __raw_readb(bcdsec); + + // convert all the data into binary + year = BCD_TO_BIN(year); + mon = BCD_TO_BIN(mon); + date = BCD_TO_BIN(date); + day = BCD_TO_BIN(day); + hour = BCD_TO_BIN(hour); + min = BCD_TO_BIN(min); + sec = BCD_TO_BIN(sec); + + // convert year to 19xx or 20xx as appropriate + if (year > 69) + year += 1900; + else + year += 2000; + + // Now calculate number of seconds since time began... + return mktime(year,mon,date,hour,min,sec); +} + +/* Sets time of alarm */ +static void set_alarm_time(struct rtc_time *tval) +{ + + int sec,min,hour,day,mon,year; + + // Convert data from binary to 8-bit bcd + sec = BIN_TO_BCD(tval->tm_sec); + min = BIN_TO_BCD(tval->tm_min); + hour = BIN_TO_BCD(tval->tm_hour); + day = BIN_TO_BCD(tval->tm_mday); + mon = BIN_TO_BCD(tval->tm_mon); + + // Year is special + year = tval->tm_year; + if(year > 1999) + year -=2000; + else + year -=1900; + + year = BIN_TO_BCD(year); + + // Write all the registers + __raw_writeb(year,almyear); + __raw_writeb(mon,almmon); + __raw_writeb(day,almday); + __raw_writeb(hour,almhour); + __raw_writeb(min,almmin); + __raw_writeb(sec,almsec); +} + +/* Sets time of alarm */ +static void set_rtc_time(struct rtc_time *tval) +{ + + int sec,min,hour,day,date,mon,year; + + // Convert data from binary to 8-bit bcd + sec = BIN_TO_BCD(tval->tm_sec); + min = BIN_TO_BCD(tval->tm_min); + hour = BIN_TO_BCD(tval->tm_hour); + day = BIN_TO_BCD(tval->tm_mday); + date = BIN_TO_BCD(tval->tm_wday); + mon = BIN_TO_BCD(tval->tm_mon); + + // Year is special + year = tval->tm_year; + if(year > 1999) + year -=2000; + else + year -=1900; + + year = BIN_TO_BCD(year); + + // Write all the registers + __raw_writeb(year,bcdyear); + __raw_writeb(mon,bcdmon); + __raw_writeb(date,bcddate); + __raw_writeb(day,bcdday); + __raw_writeb(hour,bcdhour); + __raw_writeb(min,bcdmin); + __raw_writeb(sec,bcdsec); +} + +static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* update irq data & counter */ + rtc_irq_data += 0x100; + + /* wake up waiting process */ + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit (1, &rtc_status)) + return -EBUSY; + MOD_INC_USE_COUNT; + rtc_irq_data = 0; + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + rtc_status = 0; + MOD_DEC_USE_COUNT; + return 0; +} + +static int rtc_fasync (int fd, struct file *filp, int on) +{ + return fasync_helper (fd, filp, on, &rtc_async_queue); +} + +static unsigned int rtc_poll(struct file *file, poll_table *wait) +{ + poll_wait (file, &rtc_wait, wait); + return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM; +} + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t retval; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + for (;;) { + spin_lock_irq (&rtc_lock); + data = rtc_irq_data; + if (data != 0) { + rtc_irq_data = 0; + break; + } + spin_unlock_irq (&rtc_lock); + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + spin_unlock_irq (&rtc_lock); + + data -= 0x100; /* the first IRQ wasn't actually missed */ + + retval = put_user(data, (unsigned long *)buf); + if (!retval) + retval = sizeof(unsigned long); + +out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc_wait, &wait); + return retval; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + volatile unsigned int rtcalm = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_RTCALM); + volatile unsigned int ticnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_TICINT); + + struct rtc_time tm, tm2; + switch (cmd) { + case RTC_AIE_OFF: + spin_lock_irq(&rtc_lock); + __raw_writel(0,rtcalm); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_AIE_ON: + spin_lock_irq(&rtc_lock); + __raw_writel(0x7F,rtcalm); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_OFF: + spin_lock_irq(&rtc_lock); + __raw_writel(~TICNT_ENABLE,ticnt); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_ON: + spin_lock_irq(&rtc_lock); + __raw_writel(TICNT_ENABLE|TICNT_PERIOD,ticnt); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_OFF: + spin_lock_irq(&rtc_lock); + // Periodic int not available + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_ON: + spin_lock_irq(&rtc_lock); + // Periodic int not available + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_ALM_READ: + decodetime(get_alarm_time(),&tm); + break; + case RTC_ALM_SET: + if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2))) + return -EFAULT; + decodetime(get_rtc_time(),&tm); + if ((unsigned)tm2.tm_hour < 24) + tm.tm_hour = tm2.tm_hour; + if ((unsigned)tm2.tm_min < 60) + tm.tm_min = tm2.tm_min; + if ((unsigned)tm2.tm_sec < 60) + tm.tm_sec = tm2.tm_sec; + + // Munge (as per sa1100) + tm.tm_year+=1900; + tm.tm_mon+=1; + + // Set the alarm + set_alarm_time(&tm); + return 0; + case RTC_RD_TIME: + decodetime (get_rtc_time(), &tm); + break; + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm))) + return -EFAULT; + tm.tm_year += 1900; + if (tm.tm_year < epoch || (unsigned)tm.tm_mon >= 12 || + tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] + + (tm.tm_mon == 1 && is_leap(tm.tm_year))) || + (unsigned)tm.tm_hour >= 24 || + (unsigned)tm.tm_min >= 60 || + (unsigned)tm.tm_sec >= 60) + return -EINVAL; + tm.tm_mon +=1; // wierd: same as sa1100 though (gets month wrong otherwise!) + set_rtc_time(&tm); + return 0; + case RTC_IRQP_READ: + return put_user(rtc_freq, (unsigned long *)arg); + case RTC_IRQP_SET: + if (arg < 1 || arg > TIMER_FREQ) + return -EINVAL; + if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + rtc_freq = arg; + return 0; + case RTC_EPOCH_READ: + return put_user (epoch, (unsigned long *)arg); + default: + return -EINVAL; + } + return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0; +} + +static struct file_operations rtc_fops = { + .owner = THIS_MODULE, + .llseek = rtc_llseek, + .read = rtc_read, + .poll = rtc_poll, + .ioctl = rtc_ioctl, + .open = rtc_open, + .release = rtc_release, + .fasync = rtc_fasync, +}; + +static struct miscdevice omahartc_miscdev = { + .minor = RTC_MINOR, + .name = "rtc", + .fops = &rtc_fops, +}; + +static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *p = page; + int len; + struct rtc_time tm; + + decodetime (get_rtc_time(), &tm); + p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); + decodetime (get_alarm_time(), &tm); + p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" + "alrm_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int __init rtc_init(void) +{ + volatile unsigned int ticnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_TICINT); + volatile unsigned int rtccon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_RTCCON); + int ret; + + misc_register (&omahartc_miscdev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + + // Enable RTC + __raw_writel(RTC_ENABLE,rtccon); + + // Acquire 1Hz timer + ret = request_irq (OMAHA_INT_TICK, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL); + if (ret) { + printk (KERN_ERR "rtc: IRQ %d already in use.\n", OMAHA_INT_TICK); + goto IRQ_TICK_failed; + } + + // Acquire RTC (Alarm interrupt) + ret = request_irq (OMAHA_INT_RTC, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL); + if (ret) { + printk (KERN_ERR "rtc: IRQ %d already in use.\n", OMAHA_INT_RTC); + goto IRQ_RTC_failed; + } + + printk (KERN_INFO "Omaha Real Time Clock driver v" DRIVER_VERSION "\n"); + + // Program tick interrupt divisor to generate real 1Hz clock and enable the interrupt + __raw_writeb(TICNT_ENABLE|TICNT_PERIOD,ticnt); + + return 0; + +IRQ_TICK_failed: + free_irq (OMAHA_INT_TICK, NULL); +IRQ_RTC_failed: + free_irq(OMAHA_INT_RTC, NULL); + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&omahartc_miscdev); + return ret; +} + +static void __exit rtc_exit(void) +{ + free_irq (OMAHA_INT_TICK, NULL); + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&omahartc_miscdev); +} + +module_init(rtc_init); +module_exit(rtc_exit); + +MODULE_AUTHOR("ARM Limited "); +MODULE_DESCRIPTION("Omaha Realtime Clock Driver (RTC)"); +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/char/omaha_wdt.c @@ -0,0 +1,193 @@ +/* + * Watchdog driver for the Omaha + * (C) ARM Limited 2002. + * + * Based on sa1100_wdt.c + * + * (c) Copyright 2000 Oleg Drokin + * Based on SoftDog driver by Alan Cox + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Oleg Drokin nor iXcelerator.com admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 2000 Oleg Drokin + * + * 27/11/2000 Initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_MARGIN 8 /* (secs) Default is 1 minute */ +#define WT_TPS 7812 /* Watchdog ticks per second. */ +#define WT_ENABLE 0x21 // Enable bits for watchdog +#define WT_CLKSEL_128 0x18 // Select 1/128 divider + +static int omaha_margin = TIMER_MARGIN; /* in seconds */ +static int omahawdt_users; +static int pre_margin; +#ifdef MODULE +MODULE_PARM(omaha_margin,"i"); +#endif + +/* + * Allow only one person to hold it open + */ + +static int omahadog_open(struct inode *inode, struct file *file) +{ + volatile unsigned int wtcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCON); + volatile unsigned int wtdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTDAT); + volatile unsigned int wtcnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCNT); + unsigned int tmp; + + if(test_and_set_bit(1,&omahawdt_users)) + return -EBUSY; + MOD_INC_USE_COUNT; + /* Activate omaha Watchdog timer */ + + /* Assume that uhal has set up pre-scaler according + * to the system clock frequency (don't change it!) + * + * Ie. all counting occurs at 1MHz / 128 = 7812.5Hz + * + * Since we have 16-bit counter, maximum period is + * 65536/7812.5 = 8.338608 seconds! + */ + + pre_margin = WT_TPS * omaha_margin; + + // Set count to the maximum + __raw_writel(pre_margin,wtcnt); + + // Set the clock division factor + tmp = __raw_readl(wtcon); + tmp |= WT_CLKSEL_128; + __raw_writel(tmp,wtcon); + + // Program an initial count into WTDAT + __raw_writel(0x8000,wtdat); + + // enable the watchdog + tmp = __raw_readl(wtcon); + tmp |= WT_ENABLE; + + __raw_writel(tmp,wtcon); + + return 0; +} + +static int omahadog_release(struct inode *inode, struct file *file) +{ + volatile unsigned int wtcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCON); + unsigned int tmp; + + /* + * Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT + */ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + tmp = __raw_readl(wtcon); + tmp &= ~WT_ENABLE; + __raw_writel(tmp,wtcon); +#endif + omahawdt_users = 0; + MOD_DEC_USE_COUNT; + return 0; +} + +static ssize_t omahadog_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + volatile unsigned int wtcnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCNT); + + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + /* Refresh timer. */ + if(len) { + __raw_writel(pre_margin,wtcnt); + return 1; + } + return 0; +} + +static int omahadog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + volatile unsigned int wtdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTDAT); + static struct watchdog_info ident = { + identity: "omaha Watchdog", + }; + + switch(cmd){ + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)); + case WDIOC_GETSTATUS: + return put_user(0,(int *)arg); + case WDIOC_GETBOOTSTATUS: + return 0; + case WDIOC_KEEPALIVE: + __raw_writel(pre_margin,wtdat); + return 0; + } +} + +static struct file_operations omahadog_fops= +{ + .owner = THIS_MODULE, + .write = omahadog_write, + .ioctl = omahadog_ioctl, + .open = omahadog_open, + .release = omahadog_release, +}; + +static struct miscdevice omahadog_miscdev= +{ + .minor = WATCHDOG_MINOR, + .name = "omaha watchdog", + .fops = &omahadog_fops +}; + +static int __init omahadog_init(void) +{ + int ret; + + ret = misc_register(&omahadog_miscdev); + + if (ret) + return ret; + + printk("Omaha Watchdog Timer: timer margin %d sec\n", omaha_margin); + + return 0; +} + +static void __exit omahadog_exit(void) +{ + misc_deregister(&omahadog_miscdev); +} + +module_init(omahadog_init); +module_exit(omahadog_exit); --- linux-2.4.27/drivers/char/pcmcia/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/pcmcia/Config.in @@ -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 --- /dev/null +++ linux-2.4.27/drivers/char/pcmcia/memory_cs.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static dev_info_t dev_info = "memory_cs"; +static dev_link_t *dev_list; + +struct memcs_dev { + dev_link_t link; + struct map_info map; +}; + +static __u8 mem_cs_read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + ofs); +} + +static __u16 mem_cs_read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + ofs); +} + +static __u32 mem_cs_read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + ofs); +} + +static void mem_cs_copy_from(struct map_info *map, void *to, unsigned long ofs, ssize_t size) +{ + memcpy_fromio(to, map->map_priv_1 + ofs, size); +} + +static void mem_cs_write8(struct map_info *map, __u8 val, unsigned long ofs) +{ + writeb(val, map->map_priv_1 + ofs); +} + +static void mem_cs_write16(struct map_info *map, __u16 val, unsigned long ofs) +{ + writew(val, map->map_priv_1 + ofs); +} + +static void mem_cs_write32(struct map_info *map, __u32 val, unsigned long ofs) +{ + writel(val, map->map_priv_1 + ofs); +} + +static void mem_cs_copy_to(struct map_info *map, unsigned long ofs, const void *to, ssize_t size) +{ + memcpy_toio(map->map_priv_1 + ofs, from, size); +} + +static void mem_cs_release(u_long arg); + +static void mem_cs_detach(dev_link_t *link) +{ + del_timer(&link->release); + if (link->state & DEV_CONFIG) { + mem_cs_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + kfree(link); +} + +static void mem_cs_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + link->dev = NULL; + if (link->win) { + CardServices(ReleaseWindow, link->win); + } + link->state &= ~DEV_CONFIG; + + if (link->state & DEV_STALE_LINK) + mem_cs_detach(link); +} + +static void mem_cs_config(dev_link_t *link) +{ + struct memcs_dev *dev = link->priv; + cs_status_t status; + win_req_t req; + + link->state |= DEV_CONFIG; + + req.Attributes = word_width ? WIN_DATA_WIDTH_16 : WIN_DATA_WIDTH_8; + req.Base = 0; + req.Size = 0; + req.AccessSpeed = mem_speed; + + link->win = (window_handle_t)link->handle; + + CS_CHECK(RequestWindow, &link->win, &req); + + CS_CHECK(GetStatus, link->handle, &status); + + dev->map.buswidth = word_width ? 2 : 1; + dev->map.size = req.Size; + dev->map.map_priv_1 = ioremap(req.Base, req.Size); +} + +static int +mem_cs_event(event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + mod_timer(&link->release, jiffies + HZ/20); + break; + + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + mem_cs_config(link); + break; + } + return 0; +} + +static dev_link_t *mem_cs_attach(void) +{ + struct memcs_dev *dev; + client_reg_t clnt; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (dev) { + memset(dev, 0, sizeof(*dev)); + + dev->map.read8 = mem_cs_read8; + dev->map.read16 = mem_cs_read16; + dev->map.read32 = mem_cs_read32; + dev->map.copy_from = mem_cs_copy_from; + dev->map.write8 = mem_cs_write8; + dev->map.write16 = mem_cs_write16; + dev->map.write32 = mem_cs_write32; + dev->map.copy_to = mem_cs_copy_to; + + dev->link.release.function = &mem_cs_release; + dev->link.release.data = (u_long)link; + dev->link.priv = dev; + + dev->link.next = dev_list; + dev_list = &dev->link; + + clnt.dev_info = &dev_info; + clnt.Attributes = INOF_IO_CLIENT | INFO_CARD_SHARE; + clnt.EventMask = + CS_EVENT_WRITE_PROTECT | CS_EVENT_CARD_INSERTION | + CS_EVENT_CARD_REMOVAL | CS_EVENT_BATTERY_DEAD | + CS_EVENT_BATTERY_LOW; + + clnt.event_handler = &mem_cs_event; + clnt.Version = 0x0210; + clnt.event_callback_args.client_data = &dev->link; + + ret = CardServices(RegisterClient, &dev->link.handle, &clnt); + if (ret != CS_SUCCESS) { + error_info_t err = { RegisterClient, ret }; + CardServices(ReportError, dev->link.handle, &err); + mem_cs_detach(&dev->link); + dev = NULL; + } + } + + return &dev->link; +} + +static int __init mem_cs_init(void) +{ + servinfo_t serv; + + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "memory_cs: Card services release " + "does not match\n"); + return -ENXIO; + } + register_pccard_driver(&dev_info, mem_cs_attach, mem_cs_detach); + return 0; +} + +static void __exit mem_cs_exit(void) +{ + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + memory_detach(dev_list); +} + +module_init(mem_cs_init); +module_exit(mem_cs_exit); + +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/char/sa1100-rtc.c @@ -0,0 +1,474 @@ +/* + * Real Time Clock interface for Linux on StrongARM SA1100 + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * Date/time conversion routines taken from arch/arm/kernel/time.c + * by Linus Torvalds and Russel King + * and the GNU C Library + * ( ... I love the GPL ... just take what you need! ;) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 1.00 2001-06-08 Nicolas Pitre + * - added periodic timer capability using OSMR1 + * - flag compatibility with other RTC chips + * - permission checks for ioctls + * - major cleanup, partial rewrite + * + * 0.03 2001-03-07 CIH + * - Modify the bug setups RTC clock. + * + * 0.02 2001-02-27 Nils Faerber + * - removed mktime(), added alarm irq clear + * + * 0.01 2000-10-01 Nils Faerber + * - initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "1.00" + +#define TIMER_FREQ 3686400 + +#define RTC_DEF_DIVIDER 32768 - 1 +#define RTC_DEF_TRIM 0 + +/* Those are the bits from a classic RTC we want to mimic */ +#define RTC_IRQF 0x80 /* any of the following 3 is active */ +#define RTC_PF 0x40 +#define RTC_AF 0x20 +#define RTC_UF 0x10 + +static unsigned long rtc_status; +static unsigned long rtc_irq_data; +static unsigned long rtc_freq = 1024; + +static struct fasync_struct *rtc_async_queue; +static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); + +extern spinlock_t rtc_lock; + +static const unsigned char days_in_mo[] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +#define is_leap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + +/* + * Converts seconds since 1970-01-01 00:00:00 to Gregorian date. + */ + +static void decodetime (unsigned long t, struct rtc_time *tval) +{ + long days, month, year, rem; + + days = t / 86400; + rem = t % 86400; + tval->tm_hour = rem / 3600; + rem %= 3600; + tval->tm_min = rem / 60; + tval->tm_sec = rem % 60; + tval->tm_wday = (4 + days) % 7; + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) + + year = 1970 + days / 365; + days -= ((year - 1970) * 365 + + LEAPS_THRU_END_OF (year - 1) + - LEAPS_THRU_END_OF (1970 - 1)); + if (days < 0) { + year -= 1; + days += 365 + is_leap(year); + } + tval->tm_year = year - 1900; + tval->tm_yday = days + 1; + + month = 0; + if (days >= 31) { + days -= 31; + month++; + if (days >= (28 + is_leap(year))) { + days -= (28 + is_leap(year)); + month++; + while (days >= days_in_mo[month]) { + days -= days_in_mo[month]; + month++; + } + } + } + tval->tm_mon = month; + tval->tm_mday = days + 1; +} + +static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int rtsr = RTSR; + + /* clear interrupt sources */ + RTSR = 0; + RTSR = (RTSR_AL|RTSR_HZ); + + /* clear alarm interrupt if it has occurred */ + if (rtsr & RTSR_AL) + rtsr &= ~RTSR_ALE; + RTSR = rtsr & (RTSR_ALE|RTSR_HZE); + + /* update irq data & counter */ + if (rtsr & RTSR_AL) + rtc_irq_data |= (RTC_AF|RTC_IRQF); + if (rtsr & RTSR_HZ) + rtc_irq_data |= (RTC_UF|RTC_IRQF); + rtc_irq_data += 0x100; + + /* wake up waiting process */ + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); +} + +static void timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* + * If we match for the first time, the periodic interrupt flag won't + * be set. If it is, then we did wrap around (very unlikely but + * still possible) and compute the amount of missed periods. + * The match reg is updated only when the data is actually retrieved + * to avoid unnecessary interrupts. + */ + OSSR = OSSR_M1; /* clear match on timer1 */ + if (rtc_irq_data & RTC_PF) { + rtc_irq_data += (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))) << 8; + } else { + rtc_irq_data += (0x100|RTC_PF|RTC_IRQF); + } + + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit (1, &rtc_status)) + return -EBUSY; + rtc_irq_data = 0; + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + spin_lock_irq (&rtc_lock); + RTSR = 0; + RTSR = (RTSR_AL|RTSR_HZ); + OIER &= ~OIER_E1; + OSSR = OSSR_M1; + spin_unlock_irq (&rtc_lock); + rtc_status = 0; + return 0; +} + +static int rtc_fasync (int fd, struct file *filp, int on) +{ + return fasync_helper (fd, filp, on, &rtc_async_queue); +} + +static unsigned int rtc_poll(struct file *file, poll_table *wait) +{ + poll_wait (file, &rtc_wait, wait); + return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM; +} + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t retval; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + for (;;) { + spin_lock_irq (&rtc_lock); + data = rtc_irq_data; + if (data != 0) { + rtc_irq_data = 0; + break; + } + spin_unlock_irq (&rtc_lock); + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + if (data & RTC_PF) { + /* interpolate missed periods and set match for the next one */ + unsigned long period = TIMER_FREQ/rtc_freq; + unsigned long oscr = OSCR; + unsigned long osmr1 = OSMR1; + unsigned long missed = (oscr - osmr1)/period; + data += missed << 8; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + (missed + 1)*period; + /* ensure we didn't miss another match in the mean time */ + while( (signed long)((osmr1 = OSMR1) - OSCR) <= 0 ) { + data += 0x100; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + period; + } + } + spin_unlock_irq (&rtc_lock); + + data -= 0x100; /* the first IRQ wasn't actually missed */ + + retval = put_user(data, (unsigned long *)buf); + if (!retval) + retval = sizeof(unsigned long); + +out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc_wait, &wait); + return retval; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct rtc_time tm, tm2; + + switch (cmd) { + case RTC_AIE_OFF: + spin_lock_irq(&rtc_lock); + RTSR &= ~RTSR_ALE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_AIE_ON: + spin_lock_irq(&rtc_lock); + RTSR |= RTSR_ALE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_OFF: + spin_lock_irq(&rtc_lock); + RTSR &= ~RTSR_HZE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_ON: + spin_lock_irq(&rtc_lock); + RTSR |= RTSR_HZE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_OFF: + spin_lock_irq(&rtc_lock); + OIER &= ~OIER_E1; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_ON: + if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE)) + return -EACCES; + spin_lock_irq(&rtc_lock); + OSMR1 = TIMER_FREQ/rtc_freq + OSCR; + OIER |= OIER_E1; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_ALM_READ: + decodetime (RTAR, &tm); + break; + case RTC_ALM_SET: + if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2))) + return -EFAULT; + decodetime (RCNR, &tm); + if ((unsigned)tm2.tm_hour < 24) + tm.tm_hour = tm2.tm_hour; + if ((unsigned)tm2.tm_min < 60) + tm.tm_min = tm2.tm_min; + if ((unsigned)tm2.tm_sec < 60) + tm.tm_sec = tm2.tm_sec; + RTAR = mktime ( tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return 0; + case RTC_RD_TIME: + decodetime (RCNR, &tm); + break; + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm))) + return -EFAULT; + tm.tm_year += 1900; + if (tm.tm_year < 1970 || (unsigned)tm.tm_mon >= 12 || + tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] + + (tm.tm_mon == 1 && is_leap(tm.tm_year))) || + (unsigned)tm.tm_hour >= 24 || + (unsigned)tm.tm_min >= 60 || + (unsigned)tm.tm_sec >= 60) + return -EINVAL; + RCNR = mktime ( tm.tm_year, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return 0; + case RTC_IRQP_READ: + return put_user(rtc_freq, (unsigned long *)arg); + case RTC_IRQP_SET: + if (arg < 1 || arg > TIMER_FREQ) + return -EINVAL; + if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + rtc_freq = arg; + return 0; + case RTC_EPOCH_READ: + return put_user (1970, (unsigned long *)arg); + default: + return -EINVAL; + } + return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0; +} + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: rtc_llseek, + read: rtc_read, + poll: rtc_poll, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, + fasync: rtc_fasync, +}; + +static struct miscdevice sa1100rtc_miscdev = { + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *p = page; + int len; + struct rtc_time tm; + + decodetime (RCNR, &tm); + p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1970); + decodetime (RTAR, &tm); + p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" + "alrm_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + p += sprintf(p, "trim/divider\t: 0x%08x\n", RTTR); + p += sprintf(p, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" ); + p += sprintf(p, "update_IRQ\t: %s\n", (RTSR & RTSR_HZE) ? "yes" : "no"); + p += sprintf(p, "periodic_IRQ\t: %s\n", (OIER & OIER_E1) ? "yes" : "no"); + p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int __init rtc_init(void) +{ + int ret; + + misc_register (&sa1100rtc_miscdev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + ret = request_irq (IRQ_RTC1Hz, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL); + if (ret) { + printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTC1Hz); + goto IRQ_RTC1Hz_failed; + } + ret = request_irq (IRQ_RTCAlrm, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTCAlrm); + goto IRQ_RTCAlrm_failed; + } + ret = request_irq (IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_OST1); + goto IRQ_OST1_failed; + } + + printk (KERN_INFO "SA1100 Real Time Clock driver v" DRIVER_VERSION "\n"); + + /* + * According to the manual we should be able to let RTTR be zero + * and then a default diviser for a 32.768KHz clock is used. + * Apparently this doesn't work, at least for my SA1110 rev 5. + * If the clock divider is uninitialized then reset it to the + * default value to get the 1Hz clock. + */ + if (RTTR == 0) { + RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + printk (KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n"); + /* The current RTC value probably doesn't make sense either */ + RCNR = 0; + } + + return 0; + +IRQ_OST1_failed: + free_irq (IRQ_RTCAlrm, NULL); +IRQ_RTCAlrm_failed: + free_irq (IRQ_RTC1Hz, NULL); +IRQ_RTC1Hz_failed: + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&sa1100rtc_miscdev); + return ret; +} + +static void __exit rtc_exit(void) +{ + free_irq (IRQ_OST1, NULL); + free_irq (IRQ_RTCAlrm, NULL); + free_irq (IRQ_RTC1Hz, NULL); + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&sa1100rtc_miscdev); +} + +module_init(rtc_init); +module_exit(rtc_exit); + +MODULE_AUTHOR("Nils Faerber "); +MODULE_DESCRIPTION("SA1100 Realtime Clock Driver (RTC)"); +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/char/sa1100_wdt.c @@ -0,0 +1,150 @@ +/* + * Watchdog driver for the SA11x0 + * + * (c) Copyright 2000 Oleg Drokin + * Based on SoftDog driver by Alan Cox + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Oleg Drokin nor iXcelerator.com admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 2000 Oleg Drokin + * + * 27/11/2000 Initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ + +static int sa1100_margin = TIMER_MARGIN; /* in seconds */ +static int sa1100wdt_users; +static int pre_margin; +#ifdef MODULE +MODULE_PARM(sa1100_margin,"i"); +#endif + +/* + * Allow only one person to hold it open + */ + +static int sa1100dog_open(struct inode *inode, struct file *file) +{ + if(test_and_set_bit(1,&sa1100wdt_users)) + return -EBUSY; + MOD_INC_USE_COUNT; + /* Activate SA1100 Watchdog timer */ + pre_margin=3686400 * sa1100_margin; + OSMR3 = OSCR + pre_margin; + OSSR = OSSR_M3; + OWER = OWER_WME; + OIER |= OIER_E3; + return 0; +} + +static int sa1100dog_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT + */ + OSMR3 = OSCR + pre_margin; +#ifndef CONFIG_WATCHDOG_NOWAYOUT + OIER &= ~OIER_E3; +#endif + sa1100wdt_users = 0; + MOD_DEC_USE_COUNT; + return 0; +} + +static ssize_t sa1100dog_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + /* Refresh OSMR3 timer. */ + if(len) { + OSMR3 = OSCR + pre_margin; + return 1; + } + return 0; +} + +static int sa1100dog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + identity: "SA1100 Watchdog", + }; + + switch(cmd){ + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)); + case WDIOC_GETSTATUS: + return put_user(0,(int *)arg); + case WDIOC_GETBOOTSTATUS: + return put_user((RCSR & RCSR_WDR) ? WDIOF_CARDRESET : 0, (int *)arg); + case WDIOC_KEEPALIVE: + OSMR3 = OSCR + pre_margin; + return 0; + } +} + +static struct file_operations sa1100dog_fops= +{ + owner: THIS_MODULE, + write: sa1100dog_write, + ioctl: sa1100dog_ioctl, + open: sa1100dog_open, + release: sa1100dog_release, +}; + +static struct miscdevice sa1100dog_miscdev= +{ + WATCHDOG_MINOR, + "SA1100 watchdog", + &sa1100dog_fops +}; + +static int __init sa1100dog_init(void) +{ + int ret; + + ret = misc_register(&sa1100dog_miscdev); + + if (ret) + return ret; + + printk("SA1100 Watchdog Timer: timer margin %d sec\n", sa1100_margin); + + return 0; +} + +static void __exit sa1100dog_exit(void) +{ + misc_deregister(&sa1100dog_miscdev); +} + +module_init(sa1100dog_init); +module_exit(sa1100dog_exit); --- /dev/null +++ linux-2.4.27/drivers/char/sa1111_keyb.c @@ -0,0 +1,1153 @@ +/* + * SA1111 PS/2 keyboard/mouse driver + * + * 2000 by VASARA RESEARCH INC. + * + * Changelog: + * Jun.xx,2000: Kunihiko IMAI + * Port to 2.4.0test1-ac19-rmk1-np1 + * Apr.17,2000: Takafumi Kawana + * Internal Release for XP860 + * + * + * This driver is based on linux/drivers/char/pc_keyb.c + * Original declaration follows: + + * + * linux/drivers/char/pc_keyb.c + * + * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 + * See keyboard.c for the whole history. + * + * Major cleanup by Martin Mares, May 1997 + * + * Combined the keyboard and PS/2 mouse handling into one file, + * because they share the same hardware. + * Johan Myreen 1998-10-08. + * + * Code fixes to handle mouse ACKs properly. + * C. Scott Ananian 1999-01-29. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* Some configuration switches are present in the include file... */ + +#include +#include +#include + +#define KBD_STAT_RXB (1<<4) +#define KBD_STAT_RXF (1<<5) +#define KBD_STAT_TXB (1<<6) +#define KBD_STAT_TXE (1<<7) +#define KBD_STAT_STP (1<<8) + +#define MSE_STAT_RXB (1<<4) +#define MSE_STAT_RXF (1<<5) +#define MSE_STAT_TXB (1<<6) +#define MSE_STAT_TXE (1<<7) +#define MSE_STAT_STP (1<<8) + +/* Simple translation table for the SysRq keys */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char sa1111_sysrq_xlate[128] = "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +// static void kbd_write_command_w(int data); +static void kbd_write_output_w(int data); + +spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +static void handle_kbd_event(void); + +/* used only by send_data - set by keyboard_interrupt */ +static volatile unsigned char reply_expected = 0; +static volatile unsigned char acknowledge = 0; +static volatile unsigned char resend = 0; + + +#if defined CONFIG_PSMOUSE +/* + * PS/2 Auxiliary Device + */ + +static int __init psaux_init(void); + +static struct aux_queue *queue; /* Mouse data buffer. */ +static int aux_count = 0; +/* used when we send commands to the mouse that expect an ACK. */ +static unsigned char mouse_reply_expected = 0; + +#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT) +#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT) + +#define MAX_RETRIES 60 /* some aux operations take long time */ +#endif /* CONFIG_PSMOUSE */ + +/* + * Wait for keyboard controller input buffer to drain. + * + * Don't use 'jiffies' so that we don't depend on + * interrupts.. + * + * Quote from PS/2 System Reference Manual: + * + * "Address hex 0060 and address hex 0064 should be written only when + * the input-buffer-full bit and output-buffer-full bit in the + * Controller Status register are set 0." + */ + +static void kb_wait(void) +{ + unsigned long timeout = KBC_TIMEOUT; + + do { + /* + * "handle_kbd_event()" will handle any incoming events + * while we wait - keypresses or mouse movement. + */ + handle_kbd_event(); + if (KBDSTAT & KBD_STAT_TXE) + return; + mdelay(1); + timeout--; + } + while (timeout); +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING "Keyboard timed out[1]\n"); +#endif +} + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +/* for USB 106 keyboard */ +#define E0_YEN 124 +#define E0_BACKSLASH 89 + + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END, /* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + //0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, E0_BACKSLASH, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, E0_YEN, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +int sa1111_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int sa1111_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < + 128) ? high_keys[scancode - SC_LIM] : e0_keys[scancode - 128]; +} + +static int do_acknowledge(unsigned char scancode) +{ + if (reply_expected) { + /* Unfortunately, we must recognise these codes only if we know they + * are known to be valid (i.e., after sending a command), because there + * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have + * keys with such codes :( + */ + if (scancode == KBD_REPLY_ACK) { + acknowledge = 1; + reply_expected = 0; + return 0; + } else if (scancode == KBD_REPLY_RESEND) { + resend = 1; + reply_expected = 0; + return 0; + } + /* Should not happen... */ +#if 0 + printk(KERN_DEBUG "keyboard reply expected - got %02x\n", + scancode); +#endif + } + return 1; +} + +int +sa1111_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode = 0; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } + else if (prev_scancode == 0x100 + && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO + "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO + "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO + "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +char sa1111_unexpected_up(unsigned char keycode) +{ + /* unexpected, but this can happen: maybe this was a key release for a + FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ + if (keycode >= SC_LIM || keycode == 85) + return 0; + else + return 0200; +} + +static unsigned char kbd_exists = 1; + +static inline void handle_keyboard_event(unsigned char scancode) +{ +#ifdef CONFIG_VT + kbd_exists = 1; + if (do_acknowledge(scancode)) + handle_scancode(scancode, !(scancode & 0x80)); +#endif + tasklet_schedule(&keyboard_tasklet); +} + +/* + * This reads the keyboard status port, and does the + * appropriate action. + * + * It requires that we hold the keyboard controller + * spinlock. + */ +static void handle_kbd_event(void) +{ + unsigned int status = KBDSTAT; + unsigned int work = 10000; + unsigned char scancode; + + while (status & KBD_STAT_RXF) { + while (status & KBD_STAT_RXF) { + scancode = KBDDATA & 0xff; + if (!(status & KBD_STAT_STP)) + handle_keyboard_event(scancode); + if (!--work) { + printk(KERN_ERR + "pc_keyb: keyboard controller jammed (0x%02X).\n", + status); + return; + } + status = KBDSTAT; + } + work = 10000; + } + + if (status & KBD_STAT_STP) + KBDSTAT = KBD_STAT_STP; +} + +static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + +#ifdef CONFIG_VT + kbd_pt_regs = regs; +#endif + spin_lock_irqsave(&kbd_controller_lock, flags); + handle_kbd_event(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +/* + * send_data sends a character to the keyboard and waits + * for an acknowledge, possibly retrying if asked to. Returns + * the success status. + * + * Don't use 'jiffies', so that we don't depend on interrupts + */ +static int send_data(unsigned char data) +{ + int retries = 3; + + do { + unsigned long timeout = KBD_TIMEOUT; + + acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */ + resend = 0; + reply_expected = 1; + kbd_write_output_w(data); + for (;;) { + if (acknowledge) + return 1; + if (resend) + break; + mdelay(1); + if (!--timeout) { +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING + "keyboard: Timeout - AT keyboard not present?\n"); +#endif + return 0; + } + } + } + while (retries-- > 0); +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING + "keyboard: Too many NACKs -- noisy kbd cable?\n"); +#endif + return 0; +} + +void sa1111_leds(unsigned char leds) +{ + if (kbd_exists + && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) { + send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ + kbd_exists = 0; + } +} + +#define KBD_NO_DATA (-1) /* No data */ +#define KBD_BAD_DATA (-2) /* Parity or other error */ + +static int __init kbd_read_data(void) +{ + int retval = KBD_NO_DATA; + unsigned int status; + + status = KBDSTAT; + if (status & KBD_STAT_RXF) { + unsigned char data = KBDDATA; + + retval = data; + if (status & KBD_STAT_STP) + retval = KBD_BAD_DATA; + } + return retval; +} + +static void __init kbd_clear_input(void) +{ + int maxread = 100; /* Random number */ + + do { + if (kbd_read_data() == KBD_NO_DATA) + break; + } + while (--maxread); +} + +static int __init kbd_wait_for_input(void) +{ + long timeout = KBD_INIT_TIMEOUT; + + do { + int retval = kbd_read_data(); + if (retval >= 0) + return retval; + mdelay(1); + } + while (--timeout); + return -1; +} + +#if 0 +static void kbd_write_command_w(int data) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} +#endif + +static void kbd_write_output_w(int data) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + KBDDATA = data & 0xff; + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +/* + * Test the keyboard interface. We basically check to make sure that + * we can drive each line to the keyboard independently of each other. + */ +static int kbdif_test(void) +{ + int ret = 0; + + KBDCR = KBDCR_ENA | KBDCR_FKC; + udelay(2); + if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != KBDSTAT_KBD) { + printk("Keyboard interface test failed[1]: %02x\n", + KBDSTAT); + ret = -ENODEV; + } + + KBDCR = KBDCR_ENA; + udelay(2); + if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != (KBDSTAT_KBC | KBDSTAT_KBD)) { + printk("Keyboard interface test failed[2]: %02x\n", + KBDSTAT); + ret = -ENODEV; + } + + KBDCR = KBDCR_ENA | KBDCR_FKD; + udelay(2); + if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != KBDSTAT_KBC) { + printk("Keyboard interface test failed[3]: %02x\n", + KBDSTAT); + ret = -ENODEV; + } + + return ret; +} + +static char *__init initialize_kbd(void) +{ + int status; + + /* + * Test the keyboard interface. + */ + kbdif_test(); + + /* + * Ok, drop the force low bits, and wait a while, + * and clear the stop bit error flag. + */ + KBDCR = KBDCR_ENA; + udelay(4); + KBDSTAT = KBD_STAT_STP; + + /* + * Ok, we're now ready to talk to the keyboard. Reset + * it, just to make sure we're starting in a sane state. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + KBDDATA = KBD_CMD_RESET; + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) + return "Keyboard reset failed, no ACK"; + } while (1); + + if (kbd_wait_for_input() != KBD_REPLY_POR) + return "Keyboard reset failed, no POR"; + + /* + * Set keyboard controller mode. During this, the keyboard should be + * in the disabled state. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_DISABLE); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) + return "Disable keyboard: no ACK"; + } while (1); + +#if 0 /*@@@ */ + kbd_write_command_w(KBD_CCMD_WRITE_MODE); + kbd_write_output_w(KBD_MODE_KBD_INT + | KBD_MODE_SYS | KBD_MODE_DISABLE_MOUSE | + KBD_MODE_KCC); + + /* ibm powerpc portables need this to use scan-code set 1 -- Cort */ + kbd_write_command_w(KBD_CCMD_READ_MODE); + if (!(kbd_wait_for_input() & KBD_MODE_KCC)) { + /* + * If the controller does not support conversion, + * Set the keyboard to scan-code set 1. + */ + kbd_write_output_w(0xF0); + kbd_wait_for_input(); + kbd_write_output_w(0x01); + kbd_wait_for_input(); + } +#else + kbd_write_output_w(0xf0); + kbd_wait_for_input(); + kbd_write_output_w(0x01); + kbd_wait_for_input(); +#endif + + + kbd_write_output_w(KBD_CMD_ENABLE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Enable keyboard: no ACK"; + + /* + * Finally, set the typematic rate to maximum. + */ + kbd_write_output_w(KBD_CMD_SET_RATE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Set rate: no ACK"; + kbd_write_output_w(0x00); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Set rate: no ACK"; + + return NULL; +} + +int __init sa1111_kbd_init_hw(void) +{ + char *msg; + int ret; + + if (!request_mem_region(_KBDCR, 512, "keyboard")) + return -EBUSY; + + SKPCR |= SKPCR_PTCLKEN; + KBDCLKDIV = 0; + KBDPRECNT = 127; + + /* Flush any pending input. */ + kbd_clear_input(); + + msg = initialize_kbd(); + if (msg) + printk(KERN_WARNING "initialize_kbd: %s\n", msg); + +#if defined CONFIG_PSMOUSE + psaux_init(); +#endif + + k_setkeycode = sa1111_setkeycode; + k_getkeycode = sa1111_getkeycode; + k_translate = sa1111_translate; + k_unexpected_up = sa1111_unexpected_up; + k_leds = sa1111_leds; +#ifdef CONFIG_MAGIC_SYSRQ + k_sysrq_xlate = sa1111_sysrq_xlate; + k_sysrq_key = 0x54; +#endif + + /* Ok, finally allocate the IRQ, and off we go.. */ + ret = request_irq(IRQ_TPRXINT, keyboard_interrupt, 0, "keyboard", NULL); + if (ret) + release_mem_region(_KBDCR, 512); + + return ret; +} + +#if defined CONFIG_PSMOUSE + +static inline void handle_mouse_event(unsigned char scancode) +{ + if (mouse_reply_expected) { + if (scancode == AUX_ACK) { + mouse_reply_expected--; + return; + } + mouse_reply_expected = 0; + } + + add_mouse_randomness(scancode); + if (aux_count) { + int head = queue->head; + + queue->buf[head] = scancode; + head = (head + 1) & (AUX_BUF_SIZE - 1); + if (head != queue->tail) { + queue->head = head; + if (queue->fasync) + kill_fasync(&queue->fasync, SIGIO, + POLL_IN); + wake_up_interruptible(&queue->proc_list); + } + } +} + +static void handle_mse_event(void) +{ + unsigned int msests = MSESTAT; + unsigned int work = 10000; + unsigned char scancode; + + while (msests & MSE_STAT_RXF) { + while (msests & MSE_STAT_RXF) { + scancode = MSEDATA & 0xff; + if (!(msests & MSE_STAT_STP)) + handle_mouse_event(scancode); + if (!--work) { + printk(KERN_ERR + "pc_keyb: mouse controller jammed (0x%02X).\n", + msests); + return; + /*XXX*/} + msests = MSESTAT; + } + work = 10000; + } +} + +static void ms_wait(void) +{ + unsigned long timeout = KBC_TIMEOUT; + + do { + /* + * "handle_kbd_event()" will handle any incoming events + * while we wait - keypresses or mouse movement. + */ + handle_mse_event(); + if (MSESTAT & MSE_STAT_TXE) + return; + mdelay(1); + timeout--; + } + while (timeout); +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING "Mouse timed out[1]\n"); +#endif +} + +static void mouse_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + handle_mse_event(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +/* + * Check if this is a dual port controller. + */ +static int __init detect_auxiliary_port(void) +{ + unsigned long flags; + int loops = 10; + int retval = 0; + + /* Check if the BIOS detected a device on the auxiliary port. */ + if (aux_device_present == 0xaa) + return 1; + + spin_lock_irqsave(&kbd_controller_lock, flags); + + /* Put the value 0x5A in the output buffer using the "Write + * Auxiliary Device Output Buffer" command (0xD3). Poll the + * Status Register for a while to see if the value really + * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF + * bit is also set to 1 in the Status Register, we assume this + * controller has an Auxiliary Port (a.k.a. Mouse Port). + */ + // kb_wait(); + // kbd_write_command(KBD_CCMD_WRITE_AUX_OBUF); + + SKPCR |= SKPCR_PMCLKEN; + + MSECLKDIV = 0; + MSEPRECNT = 127; + MSECR = MSECR_ENA; + mdelay(50); + MSEDATA = 0xf4; + mdelay(50); + + do { + unsigned int msests = MSESTAT; + + if (msests & MSE_STAT_RXF) { + do { + msests = MSEDATA; /* dummy read */ + mdelay(50); + msests = MSESTAT; + } + while (msests & MSE_STAT_RXF); + printk(KERN_INFO "Detected PS/2 Mouse Port.\n"); + retval = 1; + break; + } + mdelay(1); + } + while (--loops); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + + return retval; +} + +/* + * Send a byte to the mouse. + */ +static void aux_write_dev(int val) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + // kb_wait(); + // kbd_write_command(KBD_CCMD_WRITE_MOUSE); + ms_wait(); + MSEDATA = val; + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +/* + * Send a byte to the mouse & handle returned ack + */ +static void aux_write_ack(int val) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + // kb_wait(); + // kbd_write_command(KBD_CCMD_WRITE_MOUSE); + ms_wait(); + MSEDATA = val; + /* we expect an ACK in response. */ + mouse_reply_expected++; + ms_wait(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +static unsigned char get_from_queue(void) +{ + unsigned char result; + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + result = queue->buf[queue->tail]; + queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE - 1); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + return result; +} + + +static inline int queue_empty(void) +{ + return queue->head == queue->tail; +} + +static int fasync_aux(int fd, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(fd, filp, on, &queue->fasync); + if (retval < 0) + return retval; + return 0; +} + + +/* + * Random magic cookie for the aux device + */ +#define AUX_DEV ((void *)queue) + +static int release_aux(struct inode *inode, struct file *file) +{ + fasync_aux(-1, file, 0); + if (--aux_count) + return 0; + // kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ + // kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE); + aux_write_ack(AUX_DISABLE_DEV); /* Disable aux device */ + MSECR &= ~MSECR_ENA; + free_irq(IRQ_MSRXINT, AUX_DEV); + return 0; +} + +/* + * Install interrupt handler. + * Enable auxiliary device. + */ + +static int open_aux(struct inode *inode, struct file *file) +{ + if (aux_count++) { + return 0; + } + queue->head = queue->tail = 0; /* Flush input queue */ + /* Don't enable the mouse controller until we've registered IRQ handler */ + if (request_irq(IRQ_MSRXINT, mouse_interrupt, SA_SHIRQ, "PS/2 Mouse", AUX_DEV)) { + aux_count--; + return -EBUSY; + } + MSECLKDIV = 0; + MSEPRECNT = 127; + MSECR &= ~MSECR_ENA; + mdelay(50); + MSECR = MSECR_ENA; + mdelay(50); + MSEDATA = 0xf4; + mdelay(50); + if (MSESTAT & 0x0100) { + MSESTAT = 0x0100; /* clear IRQ status */ + } +/* kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); *//* Enable the + auxiliary port on + controller. */ + aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */ + // kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */ + + // send_data(KBD_CMD_ENABLE); /* try to workaround toshiba4030cdt problem */ + + return 0; +} + +/* + * Put bytes from input queue to buffer. + */ + +static ssize_t +read_aux(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + ssize_t i = count; + unsigned char c; + + if (queue_empty()) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + add_wait_queue(&queue->proc_list, &wait); + repeat: + set_current_state(TASK_INTERRUPTIBLE); + if (queue_empty() && !signal_pending(current)) { + schedule(); + goto repeat; + } + current->state = TASK_RUNNING; + remove_wait_queue(&queue->proc_list, &wait); + } + while (i > 0 && !queue_empty()) { + c = get_from_queue(); + put_user(c, buffer++); + i--; + } + if (count - i) { + file->f_dentry->d_inode->i_atime = CURRENT_TIME; + return count - i; + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* + * Write to the aux device. + */ + +static ssize_t +write_aux(struct file *file, const char *buffer, size_t count, + loff_t * ppos) +{ + ssize_t retval = 0; + + if (count) { + ssize_t written = 0; + + if (count > 32) + count = 32; /* Limit to 32 bytes. */ + do { + char c; + get_user(c, buffer++); + aux_write_dev(c); + written++; + } + while (--count); + retval = -EIO; + if (written) { + retval = written; + file->f_dentry->d_inode->i_mtime = CURRENT_TIME; + } + } + + return retval; +} + +static unsigned int aux_poll(struct file *file, poll_table * wait) +{ + poll_wait(file, &queue->proc_list, wait); + if (!queue_empty()) + return POLLIN | POLLRDNORM; + return 0; +} + +struct file_operations psaux_fops = { + read: read_aux, + write: write_aux, + poll: aux_poll, + open: open_aux, + release: release_aux, + fasync: fasync_aux, +}; + +/* + * Initialize driver. + */ +static struct miscdevice psaux_mouse = { + PSMOUSE_MINOR, "psaux", &psaux_fops +}; + + +static int __init psaux_init(void) +{ + int ret; + + if (!request_mem_region(_MSECR, 512, "psaux")) + return -EBUSY; + + if (!detect_auxiliary_port()) { + ret = -EIO; + goto out; + } + + misc_register(&psaux_mouse); + queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); + memset(queue, 0, sizeof(*queue)); + queue->head = queue->tail = 0; + init_waitqueue_head(&queue->proc_list); + +#ifdef CONFIG_PSMOUSE + aux_write_ack(AUX_SET_SAMPLE); + aux_write_ack(100); /* 100 samples/sec */ + aux_write_ack(AUX_SET_RES); + aux_write_ack(3); /* 8 counts per mm */ + aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ +#endif + ret = 0; + + out: + if (ret) + release_mem_region(_MSECR, 512); + return ret; +} + +#endif /* CONFIG_PSMOUSE */ --- linux-2.4.27/drivers/char/serial.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/serial.c @@ -4527,6 +4527,14 @@ } /* + * If there is exactly one port of 8 bytes, use it. + */ + if (num_port == 1 && pci_resource_len(dev, first_port) == 8) { + board->flags = first_port; + return 0; + } + + /* * If there is 1 or 0 iomem regions, and exactly one port, use * it. */ --- linux-2.4.27/drivers/char/serial_21285.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * linux/drivers/char/serial_21285.c - * - * Driver for the serial port on the 21285 StrongArm-110 core logic chip. - * - * Based on drivers/char/serial.c - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define BAUD_BASE (mem_fclk_21285/64) - -#define SERIAL_21285_NAME "ttyFB" -#define SERIAL_21285_MAJOR 204 -#define SERIAL_21285_MINOR 4 - -#define SERIAL_21285_AUXNAME "cuafb" -#define SERIAL_21285_AUXMAJOR 205 -#define SERIAL_21285_AUXMINOR 4 - -static struct tty_driver rs285_driver, callout_driver; -static int rs285_refcount; -static struct tty_struct *rs285_table[1]; - -static struct termios *rs285_termios[1]; -static struct termios *rs285_termios_locked[1]; - -static char wbuf[1000], *putp = wbuf, *getp = wbuf, x_char; -static struct tty_struct *rs285_tty; -static int rs285_use_count; - -static int rs285_write_room(struct tty_struct *tty) -{ - return putp >= getp ? (sizeof(wbuf) - (long) putp + (long) getp) : ((long) getp - (long) putp - 1); -} - -static void rs285_rx_int(int irq, void *dev_id, struct pt_regs *regs) -{ - if (!rs285_tty) { - disable_irq(IRQ_CONRX); - return; - } - while (!(*CSR_UARTFLG & 0x10)) { - int ch, flag; - ch = *CSR_UARTDR; - flag = *CSR_RXSTAT; - if (flag & 4) - tty_insert_flip_char(rs285_tty, 0, TTY_OVERRUN); - if (flag & 2) - flag = TTY_PARITY; - else if (flag & 1) - flag = TTY_FRAME; - tty_insert_flip_char(rs285_tty, ch, flag); - } - tty_flip_buffer_push(rs285_tty); -} - -static void rs285_send_xchar(struct tty_struct *tty, char ch) -{ - x_char = ch; - enable_irq(IRQ_CONTX); -} - -static void rs285_throttle(struct tty_struct *tty) -{ - if (I_IXOFF(tty)) - rs285_send_xchar(tty, STOP_CHAR(tty)); -} - -static void rs285_unthrottle(struct tty_struct *tty) -{ - if (I_IXOFF(tty)) { - if (x_char) - x_char = 0; - else - rs285_send_xchar(tty, START_CHAR(tty)); - } -} - -static void rs285_tx_int(int irq, void *dev_id, struct pt_regs *regs) -{ - while (!(*CSR_UARTFLG & 0x20)) { - if (x_char) { - *CSR_UARTDR = x_char; - x_char = 0; - continue; - } - if (putp == getp) { - disable_irq(IRQ_CONTX); - break; - } - *CSR_UARTDR = *getp; - if (++getp >= wbuf + sizeof(wbuf)) - getp = wbuf; - } - if (rs285_tty) - wake_up_interruptible(&rs285_tty->write_wait); -} - -static inline int rs285_xmit(int ch) -{ - if (putp + 1 == getp || (putp + 1 == wbuf + sizeof(wbuf) && getp == wbuf)) - return 0; - *putp = ch; - if (++putp >= wbuf + sizeof(wbuf)) - putp = wbuf; - enable_irq(IRQ_CONTX); - return 1; -} - -static int rs285_write(struct tty_struct *tty, int from_user, - const u_char * buf, int count) -{ - int i; - - if (from_user && verify_area(VERIFY_READ, buf, count)) - return -EINVAL; - - for (i = 0; i < count; i++) { - char ch; - if (from_user) - __get_user(ch, buf + i); - else - ch = buf[i]; - if (!rs285_xmit(ch)) - break; - } - return i; -} - -static void rs285_put_char(struct tty_struct *tty, u_char ch) -{ - rs285_xmit(ch); -} - -static int rs285_chars_in_buffer(struct tty_struct *tty) -{ - return sizeof(wbuf) - rs285_write_room(tty); -} - -static void rs285_flush_buffer(struct tty_struct *tty) -{ - disable_irq(IRQ_CONTX); - putp = getp = wbuf; - if (x_char) - enable_irq(IRQ_CONTX); -} - -static inline void rs285_set_cflag(int cflag) -{ - int h_lcr, baud, quot; - - switch (cflag & CSIZE) { - case CS5: - h_lcr = 0x10; - break; - case CS6: - h_lcr = 0x30; - break; - case CS7: - h_lcr = 0x50; - break; - default: /* CS8 */ - h_lcr = 0x70; - break; - - } - if (cflag & CSTOPB) - h_lcr |= 0x08; - if (cflag & PARENB) - h_lcr |= 0x02; - if (!(cflag & PARODD)) - h_lcr |= 0x04; - - switch (cflag & CBAUD) { - case B200: baud = 200; break; - case B300: baud = 300; break; - case B1200: baud = 1200; break; - case B1800: baud = 1800; break; - case B2400: baud = 2400; break; - case B4800: baud = 4800; break; - default: - case B9600: baud = 9600; break; - case B19200: baud = 19200; break; - case B38400: baud = 38400; break; - case B57600: baud = 57600; break; - case B115200: baud = 115200; break; - } - - /* - * The documented expression for selecting the divisor is: - * BAUD_BASE / baud - 1 - * However, typically BAUD_BASE is not divisible by baud, so - * we want to select the divisor that gives us the minimum - * error. Therefore, we want: - * int(BAUD_BASE / baud - 0.5) -> - * int(BAUD_BASE / baud - (baud >> 1) / baud) -> - * int((BAUD_BASE - (baud >> 1)) / baud) - */ - quot = (BAUD_BASE - (baud >> 1)) / baud; - - *CSR_UARTCON = 0; - *CSR_L_UBRLCR = quot & 0xff; - *CSR_M_UBRLCR = (quot >> 8) & 0x0f; - *CSR_H_UBRLCR = h_lcr; - *CSR_UARTCON = 1; -} - -static void rs285_set_termios(struct tty_struct *tty, struct termios *old) -{ - if (old && tty->termios->c_cflag == old->c_cflag) - return; - rs285_set_cflag(tty->termios->c_cflag); -} - - -static void rs285_stop(struct tty_struct *tty) -{ - disable_irq(IRQ_CONTX); -} - -static void rs285_start(struct tty_struct *tty) -{ - enable_irq(IRQ_CONTX); -} - -static void rs285_wait_until_sent(struct tty_struct *tty, int timeout) -{ - int orig_jiffies = jiffies; - while (*CSR_UARTFLG & 8) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - current->state = TASK_RUNNING; -} - -static int rs285_open(struct tty_struct *tty, struct file *filp) -{ - int line; - - MOD_INC_USE_COUNT; - line = MINOR(tty->device) - tty->driver.minor_start; - if (line) { - MOD_DEC_USE_COUNT; - return -ENODEV; - } - - tty->driver_data = NULL; - if (!rs285_tty) - rs285_tty = tty; - - enable_irq(IRQ_CONRX); - rs285_use_count++; - return 0; -} - -static void rs285_close(struct tty_struct *tty, struct file *filp) -{ - if (!--rs285_use_count) { - rs285_wait_until_sent(tty, 0); - disable_irq(IRQ_CONRX); - disable_irq(IRQ_CONTX); - rs285_tty = NULL; - } - MOD_DEC_USE_COUNT; -} - -static int __init rs285_init(void) -{ - int baud = B9600; - - if (machine_is_personal_server()) - baud = B57600; - - rs285_driver.magic = TTY_DRIVER_MAGIC; - rs285_driver.driver_name = "serial_21285"; - rs285_driver.name = SERIAL_21285_NAME; - rs285_driver.major = SERIAL_21285_MAJOR; - rs285_driver.minor_start = SERIAL_21285_MINOR; - rs285_driver.num = 1; - rs285_driver.type = TTY_DRIVER_TYPE_SERIAL; - rs285_driver.subtype = SERIAL_TYPE_NORMAL; - rs285_driver.init_termios = tty_std_termios; - rs285_driver.init_termios.c_cflag = baud | CS8 | CREAD | HUPCL | CLOCAL; - rs285_driver.flags = TTY_DRIVER_REAL_RAW; - rs285_driver.refcount = &rs285_refcount; - rs285_driver.table = rs285_table; - rs285_driver.termios = rs285_termios; - rs285_driver.termios_locked = rs285_termios_locked; - - rs285_driver.open = rs285_open; - rs285_driver.close = rs285_close; - rs285_driver.write = rs285_write; - rs285_driver.put_char = rs285_put_char; - rs285_driver.write_room = rs285_write_room; - rs285_driver.chars_in_buffer = rs285_chars_in_buffer; - rs285_driver.flush_buffer = rs285_flush_buffer; - rs285_driver.throttle = rs285_throttle; - rs285_driver.unthrottle = rs285_unthrottle; - rs285_driver.send_xchar = rs285_send_xchar; - rs285_driver.set_termios = rs285_set_termios; - rs285_driver.stop = rs285_stop; - rs285_driver.start = rs285_start; - rs285_driver.wait_until_sent = rs285_wait_until_sent; - - callout_driver = rs285_driver; - callout_driver.name = SERIAL_21285_AUXNAME; - callout_driver.major = SERIAL_21285_AUXMAJOR; - callout_driver.subtype = SERIAL_TYPE_CALLOUT; - - if (request_irq(IRQ_CONRX, rs285_rx_int, 0, "rs285", NULL)) - panic("Couldn't get rx irq for rs285"); - - if (request_irq(IRQ_CONTX, rs285_tx_int, 0, "rs285", NULL)) - panic("Couldn't get tx irq for rs285"); - - if (tty_register_driver(&rs285_driver)) - printk(KERN_ERR "Couldn't register 21285 serial driver\n"); - if (tty_register_driver(&callout_driver)) - printk(KERN_ERR "Couldn't register 21285 callout driver\n"); - - return 0; -} - -static void __exit rs285_fini(void) -{ - unsigned long flags; - int ret; - - save_flags(flags); - cli(); - ret = tty_unregister_driver(&callout_driver); - if (ret) - printk(KERN_ERR "Unable to unregister 21285 callout driver " - "(%d)\n", ret); - ret = tty_unregister_driver(&rs285_driver); - if (ret) - printk(KERN_ERR "Unable to unregister 21285 driver (%d)\n", - ret); - free_irq(IRQ_CONTX, NULL); - free_irq(IRQ_CONRX, NULL); - restore_flags(flags); -} - -module_init(rs285_init); -module_exit(rs285_fini); - -#ifdef CONFIG_SERIAL_21285_CONSOLE -/************** console driver *****************/ - -static void rs285_console_write(struct console *co, const char *s, u_int count) -{ - int i; - - disable_irq(IRQ_CONTX); - for (i = 0; i < count; i++) { - while (*CSR_UARTFLG & 0x20); - *CSR_UARTDR = s[i]; - if (s[i] == '\n') { - while (*CSR_UARTFLG & 0x20); - *CSR_UARTDR = '\r'; - } - } - enable_irq(IRQ_CONTX); -} - -static kdev_t rs285_console_device(struct console *c) -{ - return MKDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR); -} - -static int __init rs285_console_setup(struct console *co, char *options) -{ - int baud = 9600; - int bits = 8; - int parity = 'n'; - int cflag = CREAD | HUPCL | CLOCAL; - - if (machine_is_personal_server()) - baud = 57600; - - if (options) { - char *s = options; - baud = simple_strtoul(options, NULL, 10); - while (*s >= '0' && *s <= '9') - s++; - if (*s) - parity = *s++; - if (*s) - bits = *s - '0'; - } - - /* - * Now construct a cflag setting. - */ - switch (baud) { - case 1200: - cflag |= B1200; - break; - case 2400: - cflag |= B2400; - break; - case 4800: - cflag |= B4800; - break; - case 9600: - cflag |= B9600; - break; - case 19200: - cflag |= B19200; - break; - case 38400: - cflag |= B38400; - break; - case 57600: - cflag |= B57600; - break; - case 115200: - cflag |= B115200; - break; - default: - cflag |= B9600; - break; - } - switch (bits) { - case 7: - cflag |= CS7; - break; - default: - cflag |= CS8; - break; - } - switch (parity) { - case 'o': - case 'O': - cflag |= PARODD; - break; - case 'e': - case 'E': - cflag |= PARENB; - break; - } - co->cflag = cflag; - rs285_set_cflag(cflag); - rs285_console_write(NULL, "\e[2J\e[Hboot ", 12); - if (options) - rs285_console_write(NULL, options, strlen(options)); - else - rs285_console_write(NULL, "no options", 10); - rs285_console_write(NULL, "\n", 1); - - return 0; -} - -static struct console rs285_cons = -{ - name: SERIAL_21285_NAME, - write: rs285_console_write, - device: rs285_console_device, - setup: rs285_console_setup, - flags: CON_PRINTBUFFER, - index: -1, -}; - -void __init rs285_console_init(void) -{ - register_console(&rs285_cons); -} - -#endif /* CONFIG_SERIAL_21285_CONSOLE */ - -MODULE_LICENSE("GPL"); -EXPORT_NO_SYMBOLS; --- linux-2.4.27/drivers/char/serial_amba.c +++ /dev/null @@ -1,2015 +0,0 @@ -/* - * linux/drivers/char/serial_amba.c - * - * Driver for AMBA serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright 1999 ARM Limited - * Copyright (C) 2000 Deep Blue Solutions Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * - * This is a generic driver for ARM AMBA-type serial ports. They - * have a lot of 16550-like features, but are not register compatable. - * Note that although they do have CTS, DCD and DSR inputs, they do - * not have an RI input, nor do they have DTR or RTS outputs. If - * required, these have to be supplied via some other means (eg, GPIO) - * and hooked into this driver. - * - * This could very easily become a generic serial driver for dumb UARTs - * (eg, {82,16x}50, 21285, SA1100). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#define SERIAL_AMBA_NAME "ttyAM" -#define SERIAL_AMBA_MAJOR 204 -#define SERIAL_AMBA_MINOR 16 -#define SERIAL_AMBA_NR 2 - -#define CALLOUT_AMBA_NAME "cuaam" -#define CALLOUT_AMBA_MAJOR 205 -#define CALLOUT_AMBA_MINOR 16 -#define CALLOUT_AMBA_NR SERIAL_AMBA_NR - -#ifndef TRUE -#define TRUE 1 -#endif -#ifndef FALSE -#define FALSE 0 -#endif - -#define DEBUG 0 -#define DEBUG_LEDS 0 - -#if DEBUG_LEDS -extern int get_leds(void); -extern int set_leds(int); -#endif - -/* - * Access routines for the AMBA UARTs - */ -#define UART_GET_INT_STATUS(p) IO_READ((p)->uart_base + AMBA_UARTIIR) -#define UART_GET_FR(p) IO_READ((p)->uart_base + AMBA_UARTFR) -#define UART_GET_CHAR(p) IO_READ((p)->uart_base + AMBA_UARTDR) -#define UART_PUT_CHAR(p, c) IO_WRITE((p)->uart_base + AMBA_UARTDR, (c)) -#define UART_GET_RSR(p) IO_READ((p)->uart_base + AMBA_UARTRSR) -#define UART_GET_CR(p) IO_READ((p)->uart_base + AMBA_UARTCR) -#define UART_PUT_CR(p,c) IO_WRITE((p)->uart_base + AMBA_UARTCR, (c)) -#define UART_GET_LCRL(p) IO_READ((p)->uart_base + AMBA_UARTLCR_L) -#define UART_PUT_LCRL(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_L, (c)) -#define UART_GET_LCRM(p) IO_READ((p)->uart_base + AMBA_UARTLCR_M) -#define UART_PUT_LCRM(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_M, (c)) -#define UART_GET_LCRH(p) IO_READ((p)->uart_base + AMBA_UARTLCR_H) -#define UART_PUT_LCRH(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_H, (c)) -#define UART_RX_DATA(s) (((s) & AMBA_UARTFR_RXFE) == 0) -#define UART_TX_READY(s) (((s) & AMBA_UARTFR_TXFF) == 0) -#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0) - -#define AMBA_UARTRSR_ANY (AMBA_UARTRSR_OE|AMBA_UARTRSR_BE|AMBA_UARTRSR_PE|AMBA_UARTRSR_FE) -#define AMBA_UARTFR_MODEM_ANY (AMBA_UARTFR_DCD|AMBA_UARTFR_DSR|AMBA_UARTFR_CTS) - -/* - * Things needed by tty driver - */ -static struct tty_driver ambanormal_driver, ambacallout_driver; -static int ambauart_refcount; -static struct tty_struct *ambauart_table[SERIAL_AMBA_NR]; -static struct termios *ambauart_termios[SERIAL_AMBA_NR]; -static struct termios *ambauart_termios_locked[SERIAL_AMBA_NR]; - -#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -/* - * Things needed internally to this driver - */ - -/* - * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a serial write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. - */ -static u_char *tmp_buf; -static DECLARE_MUTEX(tmp_buf_sem); - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 -#define AMBA_ISR_PASS_LIMIT 256 - -#define EVT_WRITE_WAKEUP 0 - -struct amba_icount { - __u32 cts; - __u32 dsr; - __u32 rng; - __u32 dcd; - __u32 rx; - __u32 tx; - __u32 frame; - __u32 overrun; - __u32 parity; - __u32 brk; - __u32 buf_overrun; -}; - -/* - * Static information about the port - */ -struct amba_port { - unsigned int uart_base; - unsigned int irq; - unsigned int uartclk; - unsigned int fifosize; - unsigned int tiocm_support; - void (*set_mctrl)(struct amba_port *, u_int mctrl); -}; - -/* - * This is the state information which is persistent across opens - */ -struct amba_state { - struct amba_icount icount; - unsigned int line; - unsigned int close_delay; - unsigned int closing_wait; - unsigned int custom_divisor; - unsigned int flags; - struct termios normal_termios; - struct termios callout_termios; - - int count; - struct amba_info *info; -}; - -#define AMBA_XMIT_SIZE 1024 -/* - * This is the state information which is only valid when the port is open. - */ -struct amba_info { - struct amba_port *port; - struct amba_state *state; - struct tty_struct *tty; - unsigned char x_char; - unsigned char old_status; - unsigned char read_status_mask; - unsigned char ignore_status_mask; - struct circ_buf xmit; - unsigned int flags; -#ifdef SUPPORT_SYSRQ - unsigned long sysrq; -#endif - - unsigned int event; - unsigned int timeout; - unsigned int lcr_h; - unsigned int mctrl; - int blocked_open; - pid_t session; - pid_t pgrp; - - struct tasklet_struct tlet; - - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - wait_queue_head_t delta_msr_wait; -}; - -#ifdef CONFIG_SERIAL_AMBA_CONSOLE -static struct console ambauart_cons; -#endif -static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios); -static void ambauart_wait_until_sent(struct tty_struct *tty, int timeout); - -#if 1 //def CONFIG_SERIAL_INTEGRATOR -static void amba_set_mctrl_null(struct amba_port *port, u_int mctrl) -{ -} - -static struct amba_port amba_ports[SERIAL_AMBA_NR] = { - { - uart_base: IO_ADDRESS(INTEGRATOR_UART0_BASE), - irq: IRQ_UARTINT0, - uartclk: 14745600, - fifosize: 8, - set_mctrl: amba_set_mctrl_null, - }, - { - uart_base: IO_ADDRESS(INTEGRATOR_UART1_BASE), - irq: IRQ_UARTINT1, - uartclk: 14745600, - fifosize: 8, - set_mctrl: amba_set_mctrl_null, - } -}; -#endif - -static struct amba_state amba_state[SERIAL_AMBA_NR]; - -static void ambauart_enable_rx_interrupt(struct amba_info *info) -{ - unsigned int cr; - - cr = UART_GET_CR(info->port); - cr |= AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE; - UART_PUT_CR(info->port, cr); -} - -static void ambauart_disable_rx_interrupt(struct amba_info *info) -{ - unsigned int cr; - - cr = UART_GET_CR(info->port); - cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE); - UART_PUT_CR(info->port, cr); -} - -static void ambauart_enable_tx_interrupt(struct amba_info *info) -{ - unsigned int cr; - - cr = UART_GET_CR(info->port); - cr |= AMBA_UARTCR_TIE; - UART_PUT_CR(info->port, cr); -} - -static void ambauart_disable_tx_interrupt(struct amba_info *info) -{ - unsigned int cr; - - cr = UART_GET_CR(info->port); - cr &= ~AMBA_UARTCR_TIE; - UART_PUT_CR(info->port, cr); -} - -static void ambauart_stop(struct tty_struct *tty) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - - save_flags(flags); cli(); - ambauart_disable_tx_interrupt(info); - restore_flags(flags); -} - -static void ambauart_start(struct tty_struct *tty) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - - save_flags(flags); cli(); - if (info->xmit.head != info->xmit.tail - && info->xmit.buf) - ambauart_enable_tx_interrupt(info); - restore_flags(flags); -} - - -/* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - */ -static void ambauart_event(struct amba_info *info, int event) -{ - info->event |= 1 << event; - tasklet_schedule(&info->tlet); -} - -static void -#ifdef SUPPORT_SYSRQ -ambauart_rx_chars(struct amba_info *info, struct pt_regs *regs) -#else -ambauart_rx_chars(struct amba_info *info) -#endif -{ - struct tty_struct *tty = info->tty; - unsigned int status, ch, rsr, flg, ignored = 0; - struct amba_icount *icount = &info->state->icount; - struct amba_port *port = info->port; - - status = UART_GET_FR(port); - while (UART_RX_DATA(status)) { - ch = UART_GET_CHAR(port); - - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - goto ignore_char; - icount->rx++; - - flg = TTY_NORMAL; - - /* - * Note that the error handling code is - * out of the main execution path - */ - rsr = UART_GET_RSR(port); - if (rsr & AMBA_UARTRSR_ANY) - goto handle_error; -#ifdef SUPPORT_SYSRQ - if (info->sysrq) { - if (ch && time_before(jiffies, info->sysrq)) { - handle_sysrq(ch, regs, NULL, NULL); - info->sysrq = 0; - goto ignore_char; - } - info->sysrq = 0; - } -#endif - error_return: - *tty->flip.flag_buf_ptr++ = flg; - *tty->flip.char_buf_ptr++ = ch; - tty->flip.count++; - ignore_char: - status = UART_GET_FR(port); - } -out: - tty_flip_buffer_push(tty); - return; - -handle_error: - if (rsr & AMBA_UARTRSR_BE) { - rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE); - icount->brk++; - -#ifdef SUPPORT_SYSRQ - if (info->state->line == ambauart_cons.index) { - if (!info->sysrq) { - info->sysrq = jiffies + HZ*5; - goto ignore_char; - } - } -#endif - } else if (rsr & AMBA_UARTRSR_PE) - icount->parity++; - else if (rsr & AMBA_UARTRSR_FE) - icount->frame++; - if (rsr & AMBA_UARTRSR_OE) - icount->overrun++; - - if (rsr & info->ignore_status_mask) { - if (++ignored > 100) - goto out; - goto ignore_char; - } - rsr &= info->read_status_mask; - - if (rsr & AMBA_UARTRSR_BE) - flg = TTY_BREAK; - else if (rsr & AMBA_UARTRSR_PE) - flg = TTY_PARITY; - else if (rsr & AMBA_UARTRSR_FE) - flg = TTY_FRAME; - - if (rsr & AMBA_UARTRSR_OE) { - /* - * CHECK: does overrun affect the current character? - * ASSUMPTION: it does not. - */ - *tty->flip.flag_buf_ptr++ = flg; - *tty->flip.char_buf_ptr++ = ch; - tty->flip.count++; - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - goto ignore_char; - ch = 0; - flg = TTY_OVERRUN; - } -#ifdef SUPPORT_SYSRQ - info->sysrq = 0; -#endif - goto error_return; -} - -static void ambauart_tx_chars(struct amba_info *info) -{ - struct amba_port *port = info->port; - int count; - - if (info->x_char) { - UART_PUT_CHAR(port, info->x_char); - info->state->icount.tx++; - info->x_char = 0; - return; - } - if (info->xmit.head == info->xmit.tail - || info->tty->stopped - || info->tty->hw_stopped) { - ambauart_disable_tx_interrupt(info); - return; - } - - count = port->fifosize; - do { - UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]); - info->xmit.tail = (info->xmit.tail + 1) & (AMBA_XMIT_SIZE - 1); - info->state->icount.tx++; - if (info->xmit.head == info->xmit.tail) - break; - } while (--count > 0); - - if (CIRC_CNT(info->xmit.head, - info->xmit.tail, - AMBA_XMIT_SIZE) < WAKEUP_CHARS) - ambauart_event(info, EVT_WRITE_WAKEUP); - - if (info->xmit.head == info->xmit.tail) { - ambauart_disable_tx_interrupt(info); - } -} - -static void ambauart_modem_status(struct amba_info *info) -{ - unsigned int status, delta; - struct amba_icount *icount = &info->state->icount; - - status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; - - delta = status ^ info->old_status; - info->old_status = status; - - if (!delta) - return; - - if (delta & AMBA_UARTFR_DCD) { - icount->dcd++; -#ifdef CONFIG_HARD_PPS - if ((info->flags & ASYNC_HARDPPS_CD) && - (status & AMBA_UARTFR_DCD) - hardpps(); -#endif - if (info->flags & ASYNC_CHECK_CD) { - if (status & AMBA_UARTFR_DCD) - wake_up_interruptible(&info->open_wait); - else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_CALLOUT_NOHUP))) { - if (info->tty) - tty_hangup(info->tty); - } - } - } - - if (delta & AMBA_UARTFR_DSR) - icount->dsr++; - - if (delta & AMBA_UARTFR_CTS) { - icount->cts++; - - if (info->flags & ASYNC_CTS_FLOW) { - status &= AMBA_UARTFR_CTS; - - if (info->tty->hw_stopped) { - if (status) { - info->tty->hw_stopped = 0; - ambauart_enable_tx_interrupt(info); - ambauart_event(info, EVT_WRITE_WAKEUP); - } - } else { - if (!status) { - info->tty->hw_stopped = 1; - ambauart_disable_tx_interrupt(info); - } - } - } - } - wake_up_interruptible(&info->delta_msr_wait); - -} - -static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs) -{ - struct amba_info *info = dev_id; - unsigned int status, pass_counter = 0; - -#if DEBUG_LEDS - // tell the world - set_leds(get_leds() | RED_LED); -#endif - - status = UART_GET_INT_STATUS(info->port); - do { - /* - * FIXME: what about clearing the interrupts? - */ - - if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS)) -#ifdef SUPPORT_SYSRQ - ambauart_rx_chars(info, regs); -#else - ambauart_rx_chars(info); -#endif - if (status & AMBA_UARTIIR_TIS) - ambauart_tx_chars(info); - if (status & AMBA_UARTIIR_MIS) - ambauart_modem_status(info); - if (pass_counter++ > AMBA_ISR_PASS_LIMIT) - break; - - status = UART_GET_INT_STATUS(info->port); - } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | AMBA_UARTIIR_TIS)); - -#if DEBUG_LEDS - // tell the world - set_leds(get_leds() & ~RED_LED); -#endif -} - -static void ambauart_tasklet_action(unsigned long data) -{ - struct amba_info *info = (struct amba_info *)data; - struct tty_struct *tty; - - tty = info->tty; - if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event)) - return; - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); -} - -static int ambauart_startup(struct amba_info *info) -{ - unsigned long flags; - unsigned long page; - int retval = 0; - - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - save_flags(flags); cli(); - - if (info->flags & ASYNC_INITIALIZED) { - free_page(page); - goto errout; - } - - if (info->xmit.buf) - free_page(page); - else - info->xmit.buf = (unsigned char *) page; - - /* - * Allocate the IRQ - */ - retval = request_irq(info->port->irq, ambauart_int, 0, "amba", info); - if (retval) { - if (capable(CAP_SYS_ADMIN)) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - retval = 0; - } - goto errout; - } - - info->mctrl = 0; - if (info->tty->termios->c_cflag & CBAUD) - info->mctrl = TIOCM_RTS | TIOCM_DTR; - info->port->set_mctrl(info->port, info->mctrl); - - /* - * initialise the old status of the modem signals - */ - info->old_status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; - - /* - * Finally, enable interrupts - */ - ambauart_enable_rx_interrupt(info); - - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); - info->xmit.head = info->xmit.tail = 0; - - /* - * Set up the tty->alt_speed kludge - */ - if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; - } - - /* - * and set the speed of the serial port - */ - ambauart_change_speed(info, 0); - - info->flags |= ASYNC_INITIALIZED; - restore_flags(flags); - return 0; - -errout: - restore_flags(flags); - return retval; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void ambauart_shutdown(struct amba_info *info) -{ - unsigned long flags; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - - save_flags(flags); cli(); /* Disable interrupts */ - - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free the irq - * here so the queue might never be woken up - */ - wake_up_interruptible(&info->delta_msr_wait); - - /* - * Free the IRQ - */ - free_irq(info->port->irq, info); - - if (info->xmit.buf) { - unsigned long pg = (unsigned long) info->xmit.buf; - info->xmit.buf = NULL; - free_page(pg); - } - - /* - * disable all interrupts, disable the port - */ - UART_PUT_CR(info->port, 0); - - /* disable break condition and fifos */ - UART_PUT_LCRH(info->port, UART_GET_LCRH(info->port) & - ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN)); - - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS); - info->port->set_mctrl(info->port, info->mctrl); - - /* kill off our tasklet */ - tasklet_kill(&info->tlet); - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - - info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); -} - -static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios) -{ - unsigned int lcr_h, baud, quot, cflag, old_cr, bits; - unsigned long flags; - - if (!info->tty || !info->tty->termios) - return; - - cflag = info->tty->termios->c_cflag; - -#if DEBUG - printk("ambauart_set_cflag(0x%x) called\n", cflag); -#endif - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: lcr_h = AMBA_UARTLCR_H_WLEN_5; bits = 7; break; - case CS6: lcr_h = AMBA_UARTLCR_H_WLEN_6; bits = 8; break; - case CS7: lcr_h = AMBA_UARTLCR_H_WLEN_7; bits = 9; break; - default: lcr_h = AMBA_UARTLCR_H_WLEN_8; bits = 10; break; // CS8 - } - if (cflag & CSTOPB) { - lcr_h |= AMBA_UARTLCR_H_STP2; - bits ++; - } - if (cflag & PARENB) { - lcr_h |= AMBA_UARTLCR_H_PEN; - bits++; - if (!(cflag & PARODD)) - lcr_h |= AMBA_UARTLCR_H_EPS; - } - if (info->port->fifosize > 1) - lcr_h |= AMBA_UARTLCR_H_FEN; - - do { - /* Determine divisor based on baud rate */ - baud = tty_get_baud_rate(info->tty); - if (!baud) - baud = 9600; - - if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) - quot = info->state->custom_divisor; - else - quot = (info->port->uartclk / (16 * baud)) - 1; - - if (!quot && old_termios) { - info->tty->termios->c_cflag &= ~CBAUD; - info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); - old_termios = NULL; - } - } while (quot == 0 && old_termios); - - /* As a last resort, if the quotient is zero, default to 9600 bps */ - if (!quot) - quot = (info->port->uartclk / (16 * 9600)) - 1; - - info->timeout = (info->port->fifosize * HZ * bits * quot) / - (info->port->uartclk / 16); - info->timeout += HZ/50; /* Add .02 seconds of slop */ - - if (cflag & CRTSCTS) - info->flags |= ASYNC_CTS_FLOW; - else - info->flags &= ~ASYNC_CTS_FLOW; - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else - info->flags |= ASYNC_CHECK_CD; - - /* - * Set up parity check flag - */ -#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - - info->read_status_mask = AMBA_UARTRSR_OE; - if (I_INPCK(info->tty)) - info->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; - if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) - info->read_status_mask |= AMBA_UARTRSR_BE; - - /* - * Characters to ignore - */ - info->ignore_status_mask = 0; - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; - if (I_IGNBRK(info->tty)) { - info->ignore_status_mask |= AMBA_UARTRSR_BE; - /* - * If we're ignoring parity and break indicators, - * ignore overruns to (for real raw support). - */ - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= AMBA_UARTRSR_OE; - } - - /* first, disable everything */ - save_flags(flags); cli(); - old_cr = UART_GET_CR(info->port) &= ~AMBA_UARTCR_MSIE; - - if ((info->flags & ASYNC_HARDPPS_CD) || - (cflag & CRTSCTS) || - !(cflag & CLOCAL)) - old_cr |= AMBA_UARTCR_MSIE; - - UART_PUT_CR(info->port, 0); - restore_flags(flags); - - /* Set baud rate */ - UART_PUT_LCRM(info->port, ((quot & 0xf00) >> 8)); - UART_PUT_LCRL(info->port, (quot & 0xff)); - - /* - * ----------v----------v----------v----------v----- - * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L - * ----------^----------^----------^----------^----- - */ - UART_PUT_LCRH(info->port, lcr_h); - UART_PUT_CR(info->port, old_cr); -} - -static void ambauart_put_char(struct tty_struct *tty, u_char ch) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - - if (!tty || !info->xmit.buf) - return; - - save_flags(flags); cli(); - if (CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE) != 0) { - info->xmit.buf[info->xmit.head] = ch; - info->xmit.head = (info->xmit.head + 1) & (AMBA_XMIT_SIZE - 1); - } - restore_flags(flags); -} - -static void ambauart_flush_chars(struct tty_struct *tty) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - - if (info->xmit.head == info->xmit.tail - || tty->stopped - || tty->hw_stopped - || !info->xmit.buf) - return; - - save_flags(flags); cli(); - ambauart_enable_tx_interrupt(info); - restore_flags(flags); -} - -static int ambauart_write(struct tty_struct *tty, int from_user, - const u_char * buf, int count) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - int c, ret = 0; - - if (!tty || !info->xmit.buf || !tmp_buf) - return 0; - - save_flags(flags); - if (from_user) { - down(&tmp_buf_sem); - while (1) { - int c1; - c = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - AMBA_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - - c -= copy_from_user(tmp_buf, buf, c); - if (!c) { - if (!ret) - ret = -EFAULT; - break; - } - cli(); - c1 = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - AMBA_XMIT_SIZE); - if (c1 < c) - c = c1; - memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); - info->xmit.head = (info->xmit.head + c) & - (AMBA_XMIT_SIZE - 1); - restore_flags(flags); - buf += c; - count -= c; - ret += c; - } - up(&tmp_buf_sem); - } else { - cli(); - while (1) { - c = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - AMBA_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(info->xmit.buf + info->xmit.head, buf, c); - info->xmit.head = (info->xmit.head + c) & - (AMBA_XMIT_SIZE - 1); - buf += c; - count -= c; - ret += c; - } - restore_flags(flags); - } - if (info->xmit.head != info->xmit.tail - && !tty->stopped - && !tty->hw_stopped) - ambauart_enable_tx_interrupt(info); - return ret; -} - -static int ambauart_write_room(struct tty_struct *tty) -{ - struct amba_info *info = tty->driver_data; - - return CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE); -} - -static int ambauart_chars_in_buffer(struct tty_struct *tty) -{ - struct amba_info *info = tty->driver_data; - - return CIRC_CNT(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE); -} - -static void ambauart_flush_buffer(struct tty_struct *tty) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - -#if DEBUG - printk("ambauart_flush_buffer(%d) called\n", - MINOR(tty->device) - tty->driver.minor_start); -#endif - save_flags(flags); cli(); - info->xmit.head = info->xmit.tail = 0; - restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} - -/* - * This function is used to send a high-priority XON/XOFF character to - * the device - */ -static void ambauart_send_xchar(struct tty_struct *tty, char ch) -{ - struct amba_info *info = tty->driver_data; - - info->x_char = ch; - if (ch) - ambauart_enable_tx_interrupt(info); -} - -static void ambauart_throttle(struct tty_struct *tty) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - - if (I_IXOFF(tty)) - ambauart_send_xchar(tty, STOP_CHAR(tty)); - - if (tty->termios->c_cflag & CRTSCTS) { - save_flags(flags); cli(); - info->mctrl &= ~TIOCM_RTS; - info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); - } -} - -static void ambauart_unthrottle(struct tty_struct *tty) -{ - struct amba_info *info = (struct amba_info *) tty->driver_data; - unsigned long flags; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - ambauart_send_xchar(tty, START_CHAR(tty)); - } - - if (tty->termios->c_cflag & CRTSCTS) { - save_flags(flags); cli(); - info->mctrl |= TIOCM_RTS; - info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); - } -} - -static int get_serial_info(struct amba_info *info, struct serial_struct *retinfo) -{ - struct amba_state *state = info->state; - struct amba_port *port = info->port; - struct serial_struct tmp; - - memset(&tmp, 0, sizeof(tmp)); - tmp.type = 0; - tmp.line = state->line; - tmp.port = port->uart_base; - if (HIGH_BITS_OFFSET) - tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET; - tmp.irq = port->irq; - tmp.flags = 0; - tmp.xmit_fifo_size = port->fifosize; - tmp.baud_base = port->uartclk / 16; - tmp.close_delay = state->close_delay; - tmp.closing_wait = state->closing_wait; - tmp.custom_divisor = state->custom_divisor; - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int set_serial_info(struct amba_info *info, - struct serial_struct *newinfo) -{ - struct serial_struct new_serial; - struct amba_state *state, old_state; - struct amba_port *port; - unsigned long new_port; - unsigned int i, change_irq, change_port; - int retval = 0; - - if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) - return -EFAULT; - - state = info->state; - old_state = *state; - port = info->port; - - new_port = new_serial.port; - if (HIGH_BITS_OFFSET) - new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; - - change_irq = new_serial.irq != port->irq; - change_port = new_port != port->uart_base; - - if (!capable(CAP_SYS_ADMIN)) { - if (change_irq || change_port || - (new_serial.baud_base != port->uartclk / 16) || - (new_serial.close_delay != state->close_delay) || - (new_serial.xmit_fifo_size != port->fifosize) || - ((new_serial.flags & ~ASYNC_USR_MASK) != - (state->flags & ~ASYNC_USR_MASK))) - return -EPERM; - state->flags = ((state->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - state->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || - (new_serial.baud_base < 9600)) - return -EINVAL; - - if (new_serial.type && change_port) { - for (i = 0; i < SERIAL_AMBA_NR; i++) - if ((port != amba_ports + i) && - amba_ports[i].uart_base != new_port) - return -EADDRINUSE; - } - - if ((change_port || change_irq) && (state->count > 1)) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - port->uartclk = new_serial.baud_base * 16; - state->flags = ((state->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | - (info->flags & ASYNC_INTERNAL_FLAGS)); - state->custom_divisor = new_serial.custom_divisor; - state->close_delay = new_serial.close_delay * HZ / 100; - state->closing_wait = new_serial.closing_wait * HZ / 100; - info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - port->fifosize = new_serial.xmit_fifo_size; - - if (change_port || change_irq) { - /* - * We need to shutdown the serial port at the old - * port/irq combination. - */ - ambauart_shutdown(info); - port->irq = new_serial.irq; - port->uart_base = new_port; - } - -check_and_exit: - if (!port->uart_base) - return 0; - if (info->flags & ASYNC_INITIALIZED) { - if ((old_state.flags & ASYNC_SPD_MASK) != - (state->flags & ASYNC_SPD_MASK) || - (old_state.custom_divisor != state->custom_divisor)) { - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; - ambauart_change_speed(info, NULL); - } - } else - retval = ambauart_startup(info); - return retval; -} - - -/* - * get_lsr_info - get line status register info - */ -static int get_lsr_info(struct amba_info *info, unsigned int *value) -{ - unsigned int result, status; - unsigned long flags; - - save_flags(flags); cli(); - status = UART_GET_FR(info->port); - restore_flags(flags); - result = status & AMBA_UARTFR_BUSY ? TIOCSER_TEMT : 0; - - /* - * If we're about to load something into the transmit - * register, we'll pretend the transmitter isn't empty to - * avoid a race condition (depending on when the transmit - * interrupt happens). - */ - if (info->x_char || - ((CIRC_CNT(info->xmit.head, info->xmit.tail, - AMBA_XMIT_SIZE) > 0) && - !info->tty->stopped && !info->tty->hw_stopped)) - result &= TIOCSER_TEMT; - - return put_user(result, value); -} - -static int get_modem_info(struct amba_info *info, unsigned int *value) -{ - unsigned int result = info->mctrl; - unsigned int status; - - status = UART_GET_FR(info->port); - if (status & AMBA_UARTFR_DCD) - result |= TIOCM_CAR; - if (status & AMBA_UARTFR_DSR) - result |= TIOCM_DSR; - if (status & AMBA_UARTFR_CTS) - result |= TIOCM_CTS; - - return put_user(result, value); -} - -static int set_modem_info(struct amba_info *info, unsigned int cmd, - unsigned int *value) -{ - unsigned int arg, old; - unsigned long flags; - - if (get_user(arg, value)) - return -EFAULT; - - old = info->mctrl; - switch (cmd) { - case TIOCMBIS: - info->mctrl |= arg; - break; - - case TIOCMBIC: - info->mctrl &= ~arg; - break; - - case TIOCMSET: - info->mctrl = arg; - break; - - default: - return -EINVAL; - } - save_flags(flags); cli(); - if (old != info->mctrl) - info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); - return 0; -} - -static void ambauart_break_ctl(struct tty_struct *tty, int break_state) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - unsigned int lcr_h; - - save_flags(flags); cli(); - lcr_h = UART_GET_LCRH(info->port); - if (break_state == -1) - lcr_h |= AMBA_UARTLCR_H_BRK; - else - lcr_h &= ~AMBA_UARTLCR_H_BRK; - UART_PUT_LCRH(info->port, lcr_h); - restore_flags(flags); -} - -static int ambauart_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct amba_info *info = tty->driver_data; - struct amba_icount cprev, cnow; - struct serial_icounter_struct icount; - unsigned long flags; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TIOCMGET: - return get_modem_info(info, (unsigned int *)arg); - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - return set_modem_info(info, cmd, (unsigned int *)arg); - case TIOCGSERIAL: - return get_serial_info(info, - (struct serial_struct *)arg); - case TIOCSSERIAL: - return set_serial_info(info, - (struct serial_struct *)arg); - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, (unsigned int *)arg); - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - save_flags(flags); cli(); - /* note the counters on entry */ - cprev = info->state->icount; - /* Force modem status interrupts on */ - UART_PUT_CR(info->port, UART_GET_CR(info->port) | AMBA_UARTCR_MSIE); - restore_flags(flags); - while (1) { - interruptible_sleep_on(&info->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - save_flags(flags); cli(); - cnow = info->state->icount; /* atomic copy */ - restore_flags(flags); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { - return 0; - } - cprev = cnow; - } - /* NOTREACHED */ - - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - case TIOCGICOUNT: - save_flags(flags); cli(); - cnow = info->state->icount; - restore_flags(flags); - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - return copy_to_user((void *)arg, &icount, sizeof(icount)) - ? -EFAULT : 0; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void ambauart_set_termios(struct tty_struct *tty, struct termios *old_termios) -{ - struct amba_info *info = tty->driver_data; - unsigned long flags; - unsigned int cflag = tty->termios->c_cflag; - - if ((cflag ^ old_termios->c_cflag) == 0 && - RELEVENT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) - return; - - ambauart_change_speed(info, old_termios); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && - !(cflag & CBAUD)) { - save_flags(flags); cli(); - info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR); - info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - (cflag & CBAUD)) { - save_flags(flags); cli(); - info->mctrl |= TIOCM_DTR; - if (!(cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) - info->mctrl |= TIOCM_RTS; - info->port->set_mctrl(info->port, info->mctrl); - restore_flags(flags); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(cflag & CRTSCTS)) { - tty->hw_stopped = 0; - ambauart_start(tty); - } - -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); -#endif -} - -static void ambauart_close(struct tty_struct *tty, struct file *filp) -{ - struct amba_info *info = tty->driver_data; - struct amba_state *state; - unsigned long flags; - - if (!info) - return; - - state = info->state; - -#if DEBUG - printk("ambauart_close() called\n"); -#endif - - save_flags(flags); cli(); - - if (tty_hung_up_p(filp)) { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; - } - - if ((tty->count == 1) && (state->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("ambauart_close: bad serial port count; tty->count is 1, " - "state->count is %d\n", state->count); - state->count = 1; - } - if (--state->count < 0) { - printk("rs_close: bad serial port count for %s%d: %d\n", - tty->driver.name, info->state->line, state->count); - state->count = 0; - } - if (state->count) { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; - } - info->flags |= ASYNC_CLOSING; - restore_flags(flags); - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ASYNC_NORMAL_ACTIVE) - info->state->normal_termios = *tty->termios; - if (info->flags & ASYNC_CALLOUT_ACTIVE) - info->state->callout_termios = *tty->termios; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->state->closing_wait); - /* - * At this point, we stop accepting input. To do this, we - * disable the receive line status interrupts. - */ - if (info->flags & ASYNC_INITIALIZED) { - ambauart_disable_rx_interrupt(info); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - ambauart_wait_until_sent(tty, info->timeout); - } - ambauart_shutdown(info); - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - tty->closing = 0; - info->event = 0; - info->tty = NULL; - if (info->blocked_open) { - if (info->state->close_delay) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->state->close_delay); - } - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| - ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - MOD_DEC_USE_COUNT; -} - -static void ambauart_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct amba_info *info = (struct amba_info *) tty->driver_data; - unsigned long char_time, expire; - unsigned int status; - - if (info->port->fifosize == 0) - return; - - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time = (info->timeout - HZ/50) / info->port->fifosize; - char_time = char_time / 5; - if (char_time == 0) - char_time = 1; - if (timeout && timeout < char_time) - char_time = timeout; - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than info->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*info->timeout. - */ - if (!timeout || timeout > 2 * info->timeout) - timeout = 2 * info->timeout; - - expire = jiffies + timeout; -#if DEBUG - printk("ambauart_wait_until_sent(%d), jiff=%lu, expire=%lu...\n", - MINOR(tty->device) - tty->driver.minor_start, jiffies, - expire); -#endif - while (UART_GET_FR(info->port) & AMBA_UARTFR_BUSY) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(char_time); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, expire)) - break; - status = UART_GET_FR(info->port); - } - set_current_state(TASK_RUNNING); -} - -static void ambauart_hangup(struct tty_struct *tty) -{ - struct amba_info *info = tty->driver_data; - struct amba_state *state = info->state; - - ambauart_flush_buffer(tty); - if (info->flags & ASYNC_CLOSING) - return; - ambauart_shutdown(info); - info->event = 0; - state->count = 0; - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); - info->tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct amba_info *info) -{ - DECLARE_WAITQUEUE(wait, current); - struct amba_state *state = info->state; - unsigned long flags; - int do_clocal = 0, extra_count = 0, retval; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); - return (info->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS; - } - - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { - if (info->flags & ASYNC_NORMAL_ACTIVE) - return -EBUSY; - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_SESSION_LOCKOUT) && - (info->session != current->session)) - return -EBUSY; - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_PGRP_LOCKOUT) && - (info->pgrp != current->pgrp)) - return -EBUSY; - info->flags |= ASYNC_CALLOUT_ACTIVE; - return 0; - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - if (info->flags & ASYNC_CALLOUT_ACTIVE) - return -EBUSY; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (info->flags & ASYNC_CALLOUT_ACTIVE) { - if (state->normal_termios.c_cflag & CLOCAL) - do_clocal = 1; - } else { - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - } - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, state->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); - save_flags(flags); cli(); - if (!tty_hung_up_p(filp)) { - extra_count = 1; - state->count--; - } - restore_flags(flags); - info->blocked_open++; - while (1) { - save_flags(flags); cli(); - if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && - (tty->termios->c_cflag & CBAUD)) { - info->mctrl = TIOCM_DTR | TIOCM_RTS; - info->port->set_mctrl(info->port, info->mctrl); - } - restore_flags(flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && - !(info->flags & ASYNC_CLOSING) && - (do_clocal || (UART_GET_FR(info->port) & AMBA_UARTFR_DCD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); - if (extra_count) - state->count++; - info->blocked_open--; - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -static struct amba_info *ambauart_get(int line) -{ - struct amba_info *info; - struct amba_state *state = amba_state + line; - - state->count++; - if (state->info) - return state->info; - info = kmalloc(sizeof(struct amba_info), GFP_KERNEL); - if (info) { - memset(info, 0, sizeof(struct amba_info)); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->delta_msr_wait); - info->flags = state->flags; - info->state = state; - info->port = amba_ports + line; - tasklet_init(&info->tlet, ambauart_tasklet_action, - (unsigned long)info); - } - if (state->info) { - kfree(info); - return state->info; - } - state->info = info; - return info; -} - -static int ambauart_open(struct tty_struct *tty, struct file *filp) -{ - struct amba_info *info; - int retval, line = MINOR(tty->device) - tty->driver.minor_start; - -#if DEBUG - printk("ambauart_open(%d) called\n", line); -#endif - - // is this a line that we've got? - MOD_INC_USE_COUNT; - if (line >= SERIAL_AMBA_NR) { - MOD_DEC_USE_COUNT; - return -ENODEV; - } - - info = ambauart_get(line); - if (!info) - return -ENOMEM; - - tty->driver_data = info; - info->tty = tty; - info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - /* - * Make sure we have the temporary buffer allocated - */ - if (!tmp_buf) { - unsigned long page = get_zeroed_page(GFP_KERNEL); - if (tmp_buf) - free_page(page); - else if (!page) { - MOD_DEC_USE_COUNT; - return -ENOMEM; - } - tmp_buf = (u_char *)page; - } - - /* - * If the port is in the middle of closing, bail out now. - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); - MOD_DEC_USE_COUNT; - return -EAGAIN; - } - - /* - * Start up the serial port - */ - retval = ambauart_startup(info); - if (retval) { - MOD_DEC_USE_COUNT; - return retval; - } - - retval = block_til_ready(tty, filp, info); - if (retval) { - MOD_DEC_USE_COUNT; - return retval; - } - - if ((info->state->count == 1) && - (info->flags & ASYNC_SPLIT_TERMIOS)) { - if (tty->driver.subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->state->normal_termios; - else - *tty->termios = info->state->callout_termios; - } -#ifdef CONFIG_SERIAL_AMBA_CONSOLE - if (ambauart_cons.cflag && ambauart_cons.index == line) { - tty->termios->c_cflag = ambauart_cons.cflag; - ambauart_cons.cflag = 0; - } -#endif - ambauart_change_speed(info, NULL); - info->session = current->session; - info->pgrp = current->pgrp; - return 0; -} - -int __init ambauart_init(void) -{ - int i; - - ambanormal_driver.magic = TTY_DRIVER_MAGIC; - ambanormal_driver.driver_name = "serial_amba"; - ambanormal_driver.name = SERIAL_AMBA_NAME; - ambanormal_driver.major = SERIAL_AMBA_MAJOR; - ambanormal_driver.minor_start = SERIAL_AMBA_MINOR; - ambanormal_driver.num = SERIAL_AMBA_NR; - ambanormal_driver.type = TTY_DRIVER_TYPE_SERIAL; - ambanormal_driver.subtype = SERIAL_TYPE_NORMAL; - ambanormal_driver.init_termios = tty_std_termios; - ambanormal_driver.init_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; - ambanormal_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; - ambanormal_driver.refcount = &ambauart_refcount; - ambanormal_driver.table = ambauart_table; - ambanormal_driver.termios = ambauart_termios; - ambanormal_driver.termios_locked = ambauart_termios_locked; - - ambanormal_driver.open = ambauart_open; - ambanormal_driver.close = ambauart_close; - ambanormal_driver.write = ambauart_write; - ambanormal_driver.put_char = ambauart_put_char; - ambanormal_driver.flush_chars = ambauart_flush_chars; - ambanormal_driver.write_room = ambauart_write_room; - ambanormal_driver.chars_in_buffer = ambauart_chars_in_buffer; - ambanormal_driver.flush_buffer = ambauart_flush_buffer; - ambanormal_driver.ioctl = ambauart_ioctl; - ambanormal_driver.throttle = ambauart_throttle; - ambanormal_driver.unthrottle = ambauart_unthrottle; - ambanormal_driver.send_xchar = ambauart_send_xchar; - ambanormal_driver.set_termios = ambauart_set_termios; - ambanormal_driver.stop = ambauart_stop; - ambanormal_driver.start = ambauart_start; - ambanormal_driver.hangup = ambauart_hangup; - ambanormal_driver.break_ctl = ambauart_break_ctl; - ambanormal_driver.wait_until_sent = ambauart_wait_until_sent; - ambanormal_driver.read_proc = NULL; - - /* - * The callout device is just like the normal device except for - * the major number and the subtype code. - */ - ambacallout_driver = ambanormal_driver; - ambacallout_driver.name = CALLOUT_AMBA_NAME; - ambacallout_driver.major = CALLOUT_AMBA_MAJOR; - ambacallout_driver.subtype = SERIAL_TYPE_CALLOUT; - ambacallout_driver.read_proc = NULL; - ambacallout_driver.proc_entry = NULL; - - if (tty_register_driver(&ambanormal_driver)) - panic("Couldn't register AMBA serial driver\n"); - if (tty_register_driver(&ambacallout_driver)) - panic("Couldn't register AMBA callout driver\n"); - - for (i = 0; i < SERIAL_AMBA_NR; i++) { - struct amba_state *state = amba_state + i; - state->line = i; - state->close_delay = 5 * HZ / 10; - state->closing_wait = 30 * HZ; - state->callout_termios = ambacallout_driver.init_termios; - state->normal_termios = ambanormal_driver.init_termios; - } - - return 0; -} - -__initcall(ambauart_init); - -#ifdef CONFIG_SERIAL_AMBA_CONSOLE -/************** console driver *****************/ - -/* - * This code is currently never used; console->read is never called. - * Therefore, although we have an implementation, we don't use it. - * FIXME: the "const char *s" should be fixed to "char *s" some day. - * (when the definition in include/linux/console.h is also fixed) - */ -#ifdef used_and_not_const_char_pointer -static int ambauart_console_read(struct console *co, const char *s, u_int count) -{ - struct amba_port *port = &amba_ports[co->index]; - unsigned int status; - char *w; - int c; -#if DEBUG - printk("ambauart_console_read() called\n"); -#endif - - c = 0; - w = s; - while (c < count) { - status = UART_GET_FR(port); - if (UART_RX_DATA(status)) { - *w++ = UART_GET_CHAR(port); - c++; - } else { - // nothing more to get, return - return c; - } - } - // return the count - return c; -} -#endif - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * The console must be locked when we get here. - */ -static void ambauart_console_write(struct console *co, const char *s, u_int count) -{ - struct amba_port *port = &amba_ports[co->index]; - unsigned int status, old_cr; - int i; - - /* - * First save the CR then disable the interrupts - */ - old_cr = UART_GET_CR(port); - UART_PUT_CR(port, AMBA_UARTCR_UARTEN); - - /* - * Now, do each character - */ - for (i = 0; i < count; i++) { - do { - status = UART_GET_FR(port); - } while (!UART_TX_READY(status)); - UART_PUT_CHAR(port, s[i]); - if (s[i] == '\n') { - do { - status = UART_GET_FR(port); - } while (!UART_TX_READY(status)); - UART_PUT_CHAR(port, '\r'); - } - } - - /* - * Finally, wait for transmitter to become empty - * and restore the TCR - */ - do { - status = UART_GET_FR(port); - } while (status & AMBA_UARTFR_BUSY); - UART_PUT_CR(port, old_cr); -} - -static kdev_t ambauart_console_device(struct console *c) -{ - return MKDEV(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + c->index); -} - -static int __init ambauart_console_setup(struct console *co, char *options) -{ - struct amba_port *port; - int baud = 38400; - int bits = 8; - int parity = 'n'; - u_int cflag = CREAD | HUPCL | CLOCAL; - u_int lcr_h, quot; - - if (co->index >= SERIAL_AMBA_NR) - co->index = 0; - - port = &amba_ports[co->index]; - - if (options) { - char *s = options; - baud = simple_strtoul(s, NULL, 10); - while (*s >= '0' && *s <= '9') - s++; - if (*s) parity = *s++; - if (*s) bits = *s - '0'; - } - - /* - * Now construct a cflag setting. - */ - switch (baud) { - case 1200: cflag |= B1200; break; - case 2400: cflag |= B2400; break; - case 4800: cflag |= B4800; break; - default: cflag |= B9600; baud = 9600; break; - case 19200: cflag |= B19200; break; - case 38400: cflag |= B38400; break; - case 57600: cflag |= B57600; break; - case 115200: cflag |= B115200; break; - } - switch (bits) { - case 7: cflag |= CS7; lcr_h = AMBA_UARTLCR_H_WLEN_7; break; - default: cflag |= CS8; lcr_h = AMBA_UARTLCR_H_WLEN_8; break; - } - switch (parity) { - case 'o': - case 'O': cflag |= PARODD; lcr_h |= AMBA_UARTLCR_H_PEN; break; - case 'e': - case 'E': cflag |= PARENB; lcr_h |= AMBA_UARTLCR_H_PEN | - AMBA_UARTLCR_H_EPS; break; - } - - co->cflag = cflag; - - if (port->fifosize > 1) - lcr_h |= AMBA_UARTLCR_H_FEN; - - quot = (port->uartclk / (16 * baud)) - 1; - - UART_PUT_LCRL(port, (quot & 0xff)); - UART_PUT_LCRM(port, (quot >> 8)); - UART_PUT_LCRH(port, lcr_h); - - /* we will enable the port as we need it */ - UART_PUT_CR(port, 0); - - return 0; -} - -static struct console ambauart_cons = -{ - name: SERIAL_AMBA_NAME, - write: ambauart_console_write, -#ifdef used_and_not_const_char_pointer - read: ambauart_console_read, -#endif - device: ambauart_console_device, - setup: ambauart_console_setup, - flags: CON_PRINTBUFFER, - index: -1, -}; - -void __init ambauart_console_init(void) -{ - register_console(&ambauart_cons); -} - -#endif /* CONFIG_SERIAL_AMBA_CONSOLE */ - -MODULE_LICENSE("GPL"); -EXPORT_NO_SYMBOLS; --- linux-2.4.27/drivers/char/tty_io.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/tty_io.c @@ -19,7 +19,7 @@ * Also restructured routines so that there is more of a separation * between the high-level tty routines (tty_io.c and tty_ioctl.c) and * the low-level tty routines (serial.c, pty.c, console.c). This - * makes for cleaner and more compact code. -TYT, 9/17/92 + * makes for cleaner and more compact code. -TYT, 9/17/92 * * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines * which can be dynamically activated and de-activated by the line @@ -41,7 +41,7 @@ * * New TIOCLINUX variants added. * -- mj@k332.feld.cvut.cz, 19-Nov-95 - * + * * Restrict vt switching via ioctl() * -- grif@cs.ucr.edu, 5-Dec-95 * @@ -151,8 +151,7 @@ extern void tty3215_init(void); extern void tub3270_con_init(void); extern void tub3270_init(void); -extern void rs285_console_init(void); -extern void sa1100_rs_console_init(void); +extern void uart_console_init(void); extern void sgi_serial_console_init(void); extern void sn_sal_serial_console_init(void); extern void sci_console_init(void); @@ -164,6 +163,7 @@ extern void txx9_serial_console_init(void); extern void sb1250_serial_console_init(void); extern void arc_console_init(void); +extern void rs285_console_init(void); extern int hvc_console_init(void); #ifndef MIN @@ -201,7 +201,7 @@ else sprintf(buf, name, idx + tty->driver.name_base); - + return buf; } @@ -239,7 +239,7 @@ #ifdef CHECK_TTY_COUNT struct list_head *p; int count = 0; - + file_list_lock(); for(p = tty->tty_files.next; p != &tty->tty_files; p = p->next) { if(list_entry(p, struct file, f_list)->private_data == tty) @@ -255,7 +255,7 @@ "!= #fd's(%d) in %s\n", kdevname(tty->device), tty->count, count, routine); return count; - } + } #endif return 0; } @@ -264,14 +264,14 @@ { if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - + if (new_ldisc) { ldiscs[disc] = *new_ldisc; ldiscs[disc].flags |= LDISC_FLAG_DEFINED; ldiscs[disc].num = disc; } else memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc)); - + return 0; } @@ -301,7 +301,7 @@ o_ldisc = tty->ldisc; tty_wait_until_sent(tty, 0); - + /* Shutdown the current discipline. */ if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -339,7 +339,7 @@ { int major, minor; struct tty_driver *p; - + minor = MINOR(device); major = MAJOR(device); @@ -456,7 +456,7 @@ redirect = NULL; } spin_unlock(&redirect_lock); - + check_tty_count(tty, "do_tty_hangup"); file_list_lock(); for (l = tty->tty_files.next; l != &tty->tty_files; l = l->next) { @@ -473,7 +473,7 @@ filp->f_op = &hung_up_tty_fops; } file_list_unlock(); - + /* FIXME! What are the locking issues here? This may me overdoing things.. */ { unsigned long flags; @@ -510,7 +510,7 @@ "error %d\n", -i); } } - + read_lock(&tasklist_lock); for_each_task(p) { if ((tty->session > 0) && (p->session == tty->session) && @@ -550,7 +550,7 @@ { #ifdef TTY_DEBUG_HANGUP char buf[64]; - + printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf)); #endif schedule_task(&tty->tq_hangup); @@ -650,7 +650,7 @@ wake_up_interruptible(&tty->write_wait); } -static ssize_t tty_read(struct file * file, char * buf, size_t count, +static ssize_t tty_read(struct file * file, char * buf, size_t count, loff_t *ppos) { int i; @@ -707,7 +707,7 @@ size_t count) { ssize_t ret = 0, written = 0; - + if (file->f_flags & O_NONBLOCK) { if (down_trylock(&tty->atomic_write)) return -EAGAIN; @@ -835,7 +835,7 @@ struct tty_struct *tty, *o_tty; struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; - struct tty_driver *driver; + struct tty_driver *driver; int retval=0; int idx; @@ -845,7 +845,7 @@ idx = MINOR(device) - driver->minor_start; - /* + /* * Check whether we need to acquire the tty semaphore to avoid * race conditions. For now, play it safe. */ @@ -859,7 +859,7 @@ * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated - * and the allocated memory released. (Except that the termios + * and the allocated memory released. (Except that the termios * and locked termios may be retained.) */ @@ -938,13 +938,13 @@ o_tty->link = tty; } - /* + /* * All structures have been allocated, so now we install them. - * Failures after this point use release_mem to clean up, so + * Failures after this point use release_mem to clean up, so * there's no need to null out the local pointers. */ driver->table[idx] = tty; - + if (!*tp_loc) *tp_loc = tp; if (!*ltp_loc) @@ -954,7 +954,7 @@ (*driver->refcount)++; tty->count++; - /* + /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_mem to clean up. No need * to decrement the use counts, as release_mem doesn't care. @@ -988,7 +988,7 @@ if (driver->type == TTY_DRIVER_TYPE_PTY && driver->subtype == PTY_TYPE_MASTER) { /* - * special case for PTY masters: only one open permitted, + * special case for PTY masters: only one open permitted, * and the slave side open count is incremented as well. */ if (tty->count) { @@ -1002,7 +1002,7 @@ success: *ret_tty = tty; - + /* All paths come through here to release the semaphore */ end_init: up_tty_sem(idx); @@ -1080,7 +1080,7 @@ int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; char buf[64]; - + tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev")) return; @@ -1138,7 +1138,7 @@ idx, kdevname(tty->device)); return; } - if (o_tty->termios_locked != + if (o_tty->termios_locked != tty->driver.other->termios_locked[idx]) { printk(KERN_DEBUG "release_dev: other->termios_locked[" "%d] not o_termios_locked for (%s)\n", @@ -1204,11 +1204,11 @@ printk(KERN_WARNING "release_dev: %s: read/write wait queue " "active!\n", tty_name(tty, buf)); schedule(); - } + } /* - * The closing flags are now consistent with the open counts on - * both sides, and we've completed the last operation that could + * The closing flags are now consistent with the open counts on + * both sides, and we've completed the last operation that could * block, so it's safe to proceed with closing. */ if (pty_master) { @@ -1266,7 +1266,7 @@ /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) return; - + #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "freeing tty structure..."); #endif @@ -1284,14 +1284,14 @@ (o_tty->ldisc.close)(o_tty); o_tty->ldisc = ldiscs[N_TTY]; } - + /* - * Make sure that the tty's task queue isn't activated. + * Make sure that the tty's task queue isn't activated. */ run_task_queue(&tq_timer); flush_scheduled_tasks(); - /* + /* * The release_mem function takes care of the details of clearing * the slots and preserving the termios structure. */ @@ -1482,7 +1482,7 @@ tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_fasync")) return 0; - + retval = fasync_helper(fd, filp, on, &tty->fasync); if (retval <= 0) return retval; @@ -1697,7 +1697,7 @@ static int tty_generic_brk(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - if (cmd == TCSBRK && arg) + if (cmd == TCSBRK && arg) { /* tcdrain case */ int retval = tty_check_change(tty); @@ -1718,7 +1718,7 @@ { struct tty_struct *tty, *real_tty; int retval; - + tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) return -EINVAL; @@ -1738,7 +1738,7 @@ if (tty->driver.ioctl) return tty->driver.ioctl(tty, file, cmd, arg); return -EINVAL; - + /* These two ioctl's always return success; even if */ /* the driver doesn't support them. */ case TCSBRK: @@ -1761,7 +1761,7 @@ case TIOCSBRK: case TIOCCBRK: case TCSBRK: - case TCSBRKP: + case TCSBRKP: retval = tty_check_change(tty); if (retval) return retval; @@ -1824,7 +1824,7 @@ case TIOCSBRK: /* Turn break on, unconditionally */ tty->driver.break_ctl(tty, -1); return 0; - + case TIOCCBRK: /* Turn break off, unconditionally */ tty->driver.break_ctl(tty, 0); return 0; @@ -1837,7 +1837,7 @@ if (!arg) return send_break(tty, HZ/4); return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ + case TCSBRKP: /* support for POSIX tcsendbreak() */ return send_break(tty, arg ? arg*(HZ/10) : HZ/4); } if (tty->driver.ioctl) { @@ -1859,7 +1859,7 @@ * prevent trojan horses by killing all processes associated with this * tty when the user hits the "Secure Attention Key". Required for * super-paranoid applications --- see the Orange Book for more details. - * + * * This code could be nicer; ideally it should send a HUP, wait a few * seconds, then send a INT, and then a KILL signal. But you then * have to coordinate with the init process, since all processes associated @@ -1883,7 +1883,7 @@ int session; int i; struct file *filp; - + if (!tty) return; session = tty->session; @@ -1968,7 +1968,7 @@ count = tty->flip.count; tty->flip.count = 0; restore_flags(flags); - + tty->ldisc.receive_buf(tty, cp, fp, count); } @@ -2000,7 +2000,7 @@ i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; - if (i < 1 || i+15 >= n_baud_table) + if (i < 1 || i+15 >= n_baud_table) tty->termios->c_cflag &= ~CBAUDEX; else i += 15; @@ -2013,7 +2013,7 @@ } return(tty->alt_speed); } - + return baud_table[i]; } @@ -2079,7 +2079,7 @@ mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; break; } - if ( (minor < driver->minor_start) || + if ( (minor < driver->minor_start) || (minor >= driver->minor_start + driver->num) ) { printk(KERN_ERR "Attempt to register invalid minor number " "with devfs (%d:%d).\n", (int)driver->major,(int)minor); @@ -2132,12 +2132,12 @@ if (!driver->put_char) driver->put_char = tty_default_put_char; - + driver->prev = 0; driver->next = tty_drivers; if (tty_drivers) tty_drivers->prev = driver; tty_drivers = driver; - + if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) { for(i = 0; i < driver->num; i++) tty_register_devfs(driver, 0, driver->minor_start + i); @@ -2156,7 +2156,7 @@ int i, found = 0; struct termios *tp; const char *othername = NULL; - + if (*driver->refcount) return -EBUSY; @@ -2166,7 +2166,7 @@ else if (p->major == driver->major) othername = p->name; } - + if (!found) return -ENOENT; @@ -2181,7 +2181,7 @@ driver->prev->next = driver->next; else tty_drivers = driver->next; - + if (driver->next) driver->next->prev = driver->prev; @@ -2221,7 +2221,7 @@ (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); /* - * Set up the standard termios. Individual tty drivers may + * Set up the standard termios. Individual tty drivers may * deviate from this; this is used as a template. */ memset(&tty_std_termios, 0, sizeof(struct termios)); @@ -2233,11 +2233,11 @@ ECHOCTL | ECHOKE | IEXTEN; /* - * set up the console device so that later boot sequences can + * set up the console device so that later boot sequences can * inform about problems etc.. */ #ifdef CONFIG_EARLY_PRINTK - disable_early_printk(); + disable_early_printk(); #endif #ifdef CONFIG_HVC_CONSOLE hvc_console_init(); @@ -2288,18 +2288,12 @@ #ifdef CONFIG_STDIO_CONSOLE stdio_console_init(); #endif -#ifdef CONFIG_SERIAL_21285_CONSOLE - rs285_console_init(); -#endif -#ifdef CONFIG_SERIAL_SA1100_CONSOLE - sa1100_rs_console_init(); +#ifdef CONFIG_SERIAL_CORE_CONSOLE + uart_console_init(); #endif #ifdef CONFIG_ARC_CONSOLE arc_console_init(); #endif -#ifdef CONFIG_SERIAL_AMBA_CONSOLE - ambauart_console_init(); -#endif #ifdef CONFIG_SERIAL_TX3912_CONSOLE tx3912_console_init(); #endif @@ -2315,6 +2309,9 @@ #ifdef CONFIG_IP22_SERIAL sgi_serial_console_init(); #endif +#ifdef CONFIG_SERIAL_21285_CONSOLE + rs285_console_init(); +#endif } static struct tty_driver dev_tty_driver, dev_syscons_driver; @@ -2347,7 +2344,7 @@ dev_tty_driver.num = 1; dev_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM; dev_tty_driver.subtype = SYSTEM_TYPE_TTY; - + if (tty_register_driver(&dev_tty_driver)) panic("Couldn't register /dev/tty driver\n"); @@ -2363,7 +2360,7 @@ panic("Couldn't register /dev/console driver\n"); /* console calls tty_register_driver() before kmalloc() works. - * Thus, we can't devfs_register() then. Do so now, instead. + * Thus, we can't devfs_register() then. Do so now, instead. */ #ifdef CONFIG_VT con_init_devfs(); @@ -2381,7 +2378,7 @@ if (tty_register_driver(&dev_ptmx_driver)) panic("Couldn't register /dev/ptmx driver\n"); #endif - + #ifdef CONFIG_VT dev_console_driver = dev_tty_driver; dev_console_driver.driver_name = "/dev/vc/0"; @@ -2441,10 +2438,10 @@ pty_init(); #ifdef CONFIG_MOXA_SMARTIO mxser_init(); -#endif +#endif #ifdef CONFIG_MOXA_INTELLIO moxa_init(); -#endif +#endif #ifdef CONFIG_VT vcs_init(); #endif --- linux-2.4.27/drivers/char/wdt285.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/wdt285.c @@ -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(); --- linux-2.4.27/drivers/char/wdt977.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/char/wdt977.c @@ -27,6 +27,7 @@ #include #include #include +#include #define WATCHDOG_MINOR 130 --- /dev/null +++ linux-2.4.27/drivers/cpufreq/Kconfig @@ -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. --- /dev/null +++ linux-2.4.27/drivers/cpufreq/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/cpufreq/cpufreq.c @@ -0,0 +1,720 @@ +/* + * linux/kernel/cpufreq.c + * + * Copyright (C) 2001 Russell King + * (C) 2002 - 2003 Dominik Brodowski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +/** + * The "cpufreq driver" - the arch- or hardware-dependend low + * level driver of CPUFreq support, and its spinlock. This lock + * also protects the cpufreq_cpu_data array. + */ +static struct cpufreq_driver *cpufreq_driver; +static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS]; +static spinlock_t cpufreq_driver_lock = SPIN_LOCK_UNLOCKED; + +/* internal prototype */ +static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); + + +/** + * Two notifier lists: the "policy" list is involved in the + * validation process for a new CPU frequency policy; the + * "transition" list for kernel code that needs to handle + * changes to devices when the CPU clock speed changes. + * The mutex locks both lists. + */ +static struct notifier_block *cpufreq_policy_notifier_list; +static struct notifier_block *cpufreq_transition_notifier_list; +static DECLARE_RWSEM (cpufreq_notifier_rwsem); + + +static LIST_HEAD(cpufreq_governor_list); +static DECLARE_MUTEX (cpufreq_governor_sem); + +/* + * backport info: + * we don't have a kobj we can use for ref-counting, so use a + * "unsigned int policy->use_count" and an "unload_sem" [idea from + * Pat Mochel's struct driver unload_sem] for proper reference counting. + */ + +static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu) +{ + struct cpufreq_policy *data; + unsigned long flags; + + if (cpu >= NR_CPUS) + goto err_out; + + /* get the cpufreq driver */ + spin_lock_irqsave(&cpufreq_driver_lock, flags); + + if (!cpufreq_driver) + goto err_out_unlock; + + /* get the CPU */ + data = cpufreq_cpu_data[cpu]; + + if (!data) + goto err_out_unlock; + + if (!data->use_count) + goto err_out_unlock; + + data->use_count += 1; + + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + + return data; + + err_out_unlock: + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + err_out: + return NULL; +} + +static void cpufreq_cpu_put(struct cpufreq_policy *data) +{ + unsigned long flags; + + spin_lock_irqsave(&cpufreq_driver_lock, flags); + data->use_count -= 1; + if (!data->use_count) { + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + up(&data->unload_sem); + return; + } + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +} + +/********************************************************************* + * SYSFS INTERFACE * + *********************************************************************/ + +/** + * cpufreq_parse_governor - parse a governor string + */ +int cpufreq_parse_governor (char *str_governor, unsigned int *policy, + struct cpufreq_governor **governor) +{ + if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { + *policy = CPUFREQ_POLICY_PERFORMANCE; + return 0; + } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) { + *policy = CPUFREQ_POLICY_POWERSAVE; + return 0; + } else { + struct cpufreq_governor *t; + down(&cpufreq_governor_sem); + if (!cpufreq_driver || !cpufreq_driver->target) + goto out; + list_for_each_entry(t, &cpufreq_governor_list, governor_list) { + if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) { + *governor = t; + *policy = CPUFREQ_POLICY_GOVERNOR; + up(&cpufreq_governor_sem); + return 0; + } + } + out: + up(&cpufreq_governor_sem); + } + return -EINVAL; +} +EXPORT_SYMBOL_GPL(cpufreq_parse_governor); + + +/* backport info: + * all the sysfs stuff is missing -- of course + */ + +/** + * cpufreq_add_dev - add a CPU device + * + * Adds the cpufreq interface for a CPU device. + */ +static int cpufreq_add_dev (unsigned int cpu) +{ + int ret = 0; + struct cpufreq_policy new_policy; + struct cpufreq_policy *policy; + unsigned long flags; + + policy = kmalloc(sizeof(struct cpufreq_policy), GFP_KERNEL); + if (!policy) + return -ENOMEM; + memset(policy, 0, sizeof(struct cpufreq_policy)); + + policy->cpu = cpu; + policy->use_count = 1; + init_MUTEX_LOCKED(&policy->lock); + init_MUTEX_LOCKED(&policy->unload_sem); + + /* call driver. From then on the cpufreq must be able + * to accept all calls to ->verify and ->setpolicy for this CPU + */ + ret = cpufreq_driver->init(policy); + if (ret) + goto err_out; + + memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); + + spin_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_cpu_data[cpu] = policy; + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + + up(&policy->lock); + + /* set default policy */ + ret = cpufreq_set_policy(&new_policy); + if (ret) + goto err_out_unregister; + + return 0; + + + err_out_unregister: + spin_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_cpu_data[cpu] = NULL; + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + + err_out: + kfree(policy); + return ret; +} + + +/** + * cpufreq_remove_dev - remove a CPU device + * + * Removes the cpufreq interface for a CPU device. + */ +static int cpufreq_remove_dev (unsigned int cpu) +{ + unsigned long flags; + struct cpufreq_policy *data; + + spin_lock_irqsave(&cpufreq_driver_lock, flags); + data = cpufreq_cpu_data[cpu]; + if (!data) { + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + return -EINVAL; + } + cpufreq_cpu_data[cpu] = NULL; + + data->use_count -= 1; + if (!data->use_count) { + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + up(&data->unload_sem); + } else { + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + /* this will sleep until data->use_count gets to zero */ + down(&data->unload_sem); + up(&data->unload_sem); + } + + if (cpufreq_driver->target) + __cpufreq_governor(data, CPUFREQ_GOV_STOP); + + if (cpufreq_driver->exit) + cpufreq_driver->exit(data); + + kfree(data); + + return 0; +} + + +/********************************************************************* + * NOTIFIER LISTS INTERFACE * + *********************************************************************/ + +/** + * cpufreq_register_notifier - register a driver with cpufreq + * @nb: notifier function to register + * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER + * + * Add a driver to one of two lists: either a list of drivers that + * are notified about clock rate changes (once before and once after + * the transition), or a list of drivers that are notified about + * changes in cpufreq policy. + * + * This function may sleep, and has the same return conditions as + * notifier_chain_register. + */ +int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) +{ + int ret; + + down_write(&cpufreq_notifier_rwsem); + switch (list) { + case CPUFREQ_TRANSITION_NOTIFIER: + ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb); + break; + case CPUFREQ_POLICY_NOTIFIER: + ret = notifier_chain_register(&cpufreq_policy_notifier_list, nb); + break; + default: + ret = -EINVAL; + } + up_write(&cpufreq_notifier_rwsem); + + return ret; +} +EXPORT_SYMBOL(cpufreq_register_notifier); + + +/** + * cpufreq_unregister_notifier - unregister a driver with cpufreq + * @nb: notifier block to be unregistered + * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER + * + * Remove a driver from the CPU frequency notifier list. + * + * This function may sleep, and has the same return conditions as + * notifier_chain_unregister. + */ +int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) +{ + int ret; + + down_write(&cpufreq_notifier_rwsem); + switch (list) { + case CPUFREQ_TRANSITION_NOTIFIER: + ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb); + break; + case CPUFREQ_POLICY_NOTIFIER: + ret = notifier_chain_unregister(&cpufreq_policy_notifier_list, nb); + break; + default: + ret = -EINVAL; + } + up_write(&cpufreq_notifier_rwsem); + + return ret; +} +EXPORT_SYMBOL(cpufreq_unregister_notifier); + + +/********************************************************************* + * GOVERNORS * + *********************************************************************/ + + +int __cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + return cpufreq_driver->target(policy, target_freq, relation); +} +EXPORT_SYMBOL_GPL(__cpufreq_driver_target); + + +int cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int ret; + + policy = cpufreq_cpu_get(policy->cpu); + if (!policy) + return -EINVAL; + + down(&policy->lock); + + ret = __cpufreq_driver_target(policy, target_freq, relation); + + up(&policy->lock); + + cpufreq_cpu_put(policy); + + return ret; +} +EXPORT_SYMBOL_GPL(cpufreq_driver_target); + + +static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event) +{ + int ret = 0; + + switch (policy->policy) { + case CPUFREQ_POLICY_POWERSAVE: + if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) { + ret = __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); + } + break; + case CPUFREQ_POLICY_PERFORMANCE: + if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) { + ret = __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); + } + break; + case CPUFREQ_POLICY_GOVERNOR: + ret = policy->governor->governor(policy, event); + break; + default: + ret = -EINVAL; + } + + return ret; +} + + +int cpufreq_governor(unsigned int cpu, unsigned int event) +{ + int ret = 0; + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + + if (!policy) + return -EINVAL; + + down(&policy->lock); + ret = __cpufreq_governor(policy, event); + up(&policy->lock); + + cpufreq_cpu_put(policy); + + return ret; +} +EXPORT_SYMBOL_GPL(cpufreq_governor); + + +int cpufreq_register_governor(struct cpufreq_governor *governor) +{ + struct cpufreq_governor *t; + + if (!governor) + return -EINVAL; + + if (!strnicmp(governor->name,"powersave",CPUFREQ_NAME_LEN)) + return -EBUSY; + if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN)) + return -EBUSY; + + down(&cpufreq_governor_sem); + + list_for_each_entry(t, &cpufreq_governor_list, governor_list) { + if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) { + up(&cpufreq_governor_sem); + return -EBUSY; + } + } + list_add(&governor->governor_list, &cpufreq_governor_list); + + up(&cpufreq_governor_sem); + + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_register_governor); + + +void cpufreq_unregister_governor(struct cpufreq_governor *governor) +{ + /* backport info: + * As the module usage count isn't assured in 2.4., check for removal + * of running cpufreq governor + */ + unsigned int i; + + if (!governor) + return; + + down(&cpufreq_governor_sem); + + for (i=0; ilock); + + if (policy->policy != CPUFREQ_POLICY_GOVERNOR) + goto unlock_done; + if (policy->governor != governor) + goto unlock_done; + + /* stop old one, start performance [always present] */ + __cpufreq_governor(policy, CPUFREQ_GOV_STOP); + policy->policy = CPUFREQ_POLICY_PERFORMANCE; + __cpufreq_governor(policy, CPUFREQ_GOV_START); + + unlock_done: + up(&policy->lock); + done: + cpufreq_cpu_put(policy); + } + list_del(&governor->governor_list); + up(&cpufreq_governor_sem); + return; +} +EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); + + + +/********************************************************************* + * POLICY INTERFACE * + *********************************************************************/ + +/** + * cpufreq_get_policy - get the current cpufreq_policy + * @policy: struct cpufreq_policy into which the current cpufreq_policy is written + * + * Reads the current cpufreq policy. + */ +int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) +{ + struct cpufreq_policy *cpu_policy; + if (!policy) + return -EINVAL; + + cpu_policy = cpufreq_cpu_get(cpu); + if (!cpu_policy) + return -EINVAL; + + down(&cpu_policy->lock); + memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy)); + up(&cpu_policy->lock); + + cpufreq_cpu_put(cpu_policy); + + return 0; +} +EXPORT_SYMBOL(cpufreq_get_policy); + + +/** + * cpufreq_set_policy - set a new CPUFreq policy + * @policy: policy to be set. + * + * Sets a new CPU frequency and voltage scaling policy. + */ +int cpufreq_set_policy(struct cpufreq_policy *policy) +{ + int ret = 0; + struct cpufreq_policy *data; + + if (!policy) + return -EINVAL; + + data = cpufreq_cpu_get(policy->cpu); + if (!data) + return -EINVAL; + + /* lock this CPU */ + down(&data->lock); + + memcpy(&policy->cpuinfo, + &data->cpuinfo, + sizeof(struct cpufreq_cpuinfo)); + + /* verify the cpu speed can be set within this limit */ + ret = cpufreq_driver->verify(policy); + if (ret) + goto error_out; + + down_read(&cpufreq_notifier_rwsem); + + /* adjust if necessary - all reasons */ + notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST, + policy); + + /* adjust if necessary - hardware incompatibility*/ + notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_INCOMPATIBLE, + policy); + + /* verify the cpu speed can be set within this limit, + which might be different to the first one */ + ret = cpufreq_driver->verify(policy); + if (ret) { + up_read(&cpufreq_notifier_rwsem); + goto error_out; + } + + /* notification of the new policy */ + notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY, + policy); + + up_read(&cpufreq_notifier_rwsem); + + data->min = policy->min; + data->max = policy->max; + + if (cpufreq_driver->setpolicy) { + data->policy = policy->policy; + ret = cpufreq_driver->setpolicy(policy); + } else { + if ((policy->policy != data->policy) || + ((policy->policy == CPUFREQ_POLICY_GOVERNOR) && (policy->governor != data->governor))) { + /* save old, working values */ + unsigned int old_pol = data->policy; + struct cpufreq_governor *old_gov = data->governor; + + /* end old governor */ + __cpufreq_governor(data, CPUFREQ_GOV_STOP); + + /* start new governor */ + data->policy = policy->policy; + data->governor = policy->governor; + if (__cpufreq_governor(data, CPUFREQ_GOV_START)) { + /* new governor failed, so re-start old one */ + data->policy = old_pol; + data->governor = old_gov; + __cpufreq_governor(data, CPUFREQ_GOV_START); + } + /* might be a policy change, too, so fall through */ + } + __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); + } + + error_out: + up(&data->lock); + cpufreq_cpu_put(data); + + return ret; +} +EXPORT_SYMBOL(cpufreq_set_policy); + + + +/********************************************************************* + * EXTERNALLY AFFECTING FREQUENCY CHANGES * + *********************************************************************/ + +/** + * adjust_jiffies - adjust the system "loops_per_jiffy" + * + * This function alters the system "loops_per_jiffy" for the clock + * speed change. Note that loops_per_jiffy cannot be updated on SMP + * systems as each CPU might be scaled differently. So, use the arch + * per-CPU loops_per_jiffy value wherever possible. + */ +#ifndef CONFIG_SMP +static unsigned long l_p_j_ref = 0; +static unsigned int l_p_j_ref_freq = 0; + +static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) +{ + if (!l_p_j_ref_freq) { + l_p_j_ref = loops_per_jiffy; + l_p_j_ref_freq = ci->old; + } + if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) || + (val == CPUFREQ_POSTCHANGE && ci->old > ci->new)) + loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); +} +#else +#define adjust_jiffies(x...) do {} while (0) +#endif + + +/** + * cpufreq_notify_transition - call notifier chain and adjust_jiffies on frequency transition + * + * This function calls the transition notifiers and the "adjust_jiffies" function. It is called + * twice on all CPU frequency changes that have external effects. + */ +void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) +{ + down_read(&cpufreq_notifier_rwsem); + switch (state) { + case CPUFREQ_PRECHANGE: + notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs); + adjust_jiffies(CPUFREQ_PRECHANGE, freqs); + break; + case CPUFREQ_POSTCHANGE: + adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); + notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); + cpufreq_cpu_data[freqs->cpu]->cur = freqs->new; + break; + } + up_read(&cpufreq_notifier_rwsem); +} +EXPORT_SYMBOL_GPL(cpufreq_notify_transition); + + + +/********************************************************************* + * REGISTER / UNREGISTER CPUFREQ DRIVER * + *********************************************************************/ + +/** + * cpufreq_register_driver - register a CPU Frequency driver + * @driver_data: A struct cpufreq_driver containing the values# + * submitted by the CPU Frequency driver. + * + * Registers a CPU Frequency driver to this core code. This code + * returns zero on success, -EBUSY when another driver got here first + * (and isn't unregistered in the meantime). + * + */ +int cpufreq_register_driver(struct cpufreq_driver *driver_data) +{ + unsigned long flags; + unsigned int i; + + if (!driver_data || !driver_data->verify || !driver_data->init || + ((!driver_data->setpolicy) && (!driver_data->target))) + return -EINVAL; + + spin_lock_irqsave(&cpufreq_driver_lock, flags); + if (cpufreq_driver) { + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + return -EBUSY; + } + cpufreq_driver = driver_data; + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + + for (i=0; i +#include +#include +#include + +/********************************************************************* + * FREQUENCY TABLE HELPERS * + *********************************************************************/ + +int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table) +{ + unsigned int min_freq = ~0; + unsigned int max_freq = 0; + unsigned int i = 0; + + for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { + unsigned int freq = table[i].frequency; + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + if (freq < min_freq) + min_freq = freq; + if (freq > max_freq) + max_freq = freq; + } + + policy->min = policy->cpuinfo.min_freq = min_freq; + policy->max = policy->cpuinfo.max_freq = max_freq; + + if (policy->min == ~0) + return -EINVAL; + else + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo); + + +int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table) +{ + unsigned int next_larger = ~0; + unsigned int i = 0; + unsigned int count = 0; + + if (!cpu_online(policy->cpu)) + return -EINVAL; + + cpufreq_verify_within_limits(policy, + policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + + for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { + unsigned int freq = table[i].frequency; + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + if ((freq >= policy->min) && (freq <= policy->max)) + count++; + else if ((next_larger > freq) && (freq > policy->max)) + next_larger = freq; + } + + if (!count) + policy->max = next_larger; + + cpufreq_verify_within_limits(policy, + policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify); + + +int cpufreq_frequency_table_target(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq, + unsigned int relation, + unsigned int *index) +{ + struct cpufreq_frequency_table optimal = { .index = ~0, }; + struct cpufreq_frequency_table suboptimal = { .index = ~0, }; + unsigned int i; + + switch (relation) { + case CPUFREQ_RELATION_H: + optimal.frequency = 0; + suboptimal.frequency = ~0; + break; + case CPUFREQ_RELATION_L: + optimal.frequency = ~0; + suboptimal.frequency = 0; + break; + } + + if (!cpu_online(policy->cpu)) + return -EINVAL; + + for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { + unsigned int freq = table[i].frequency; + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + if ((freq < policy->min) || (freq > policy->max)) + continue; + switch(relation) { + case CPUFREQ_RELATION_H: + if (freq <= target_freq) { + if (freq >= optimal.frequency) { + optimal.frequency = freq; + optimal.index = i; + } + } else { + if (freq <= suboptimal.frequency) { + suboptimal.frequency = freq; + suboptimal.index = i; + } + } + break; + case CPUFREQ_RELATION_L: + if (freq >= target_freq) { + if (freq <= optimal.frequency) { + optimal.frequency = freq; + optimal.index = i; + } + } else { + if (freq >= suboptimal.frequency) { + suboptimal.frequency = freq; + suboptimal.index = i; + } + } + break; + } + } + if (optimal.index > i) { + if (suboptimal.index > i) + return -EINVAL; + *index = suboptimal.index; + } else + *index = optimal.index; + + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); + +static struct cpufreq_frequency_table *show_table[NR_CPUS]; +/** + * show_scaling_governor - show the current policy for the specified CPU + */ +static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf) +{ + unsigned int i = 0; + unsigned int cpu = policy->cpu; + ssize_t count = 0; + struct cpufreq_frequency_table *table; + + if (!show_table[cpu]) + return -ENODEV; + + table = show_table[cpu]; + + for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { + if (table[i].frequency == CPUFREQ_ENTRY_INVALID) + continue; + count += sprintf(&buf[count], "%d ", table[i].frequency); + } + count += sprintf(&buf[count], "\n"); + + return count; + +} + +struct freq_attr cpufreq_freq_attr_scaling_available_freqs = { + .attr = { .name = "scaling_available_frequencies", .mode = 0444 }, + .show = show_available_freqs, +}; +EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs); + +/* + * if you use these, you must assure that the frequency table is valid + * all the time between get_attr and put_attr! + */ +void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, + unsigned int cpu) +{ + show_table[cpu] = table; +} +EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr); + +void cpufreq_frequency_table_put_attr(unsigned int cpu) +{ + show_table[cpu] = NULL; +} +EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr); + + +MODULE_AUTHOR ("Dominik Brodowski "); +MODULE_DESCRIPTION ("CPUfreq frequency table helpers"); +MODULE_LICENSE ("GPL"); --- /dev/null +++ linux-2.4.27/drivers/cpufreq/proc_intf.c @@ -0,0 +1,244 @@ +/* + * linux/drivers/cpufreq/proc_intf.c + * + * Copyright (C) 2002 - 2003 Dominik Brodowski + */ + +#include +#include +#include +#include +#include +#include +#include + + +/** + * cpufreq_parse_policy - parse a policy string + * @input_string: the string to parse. + * @policy: the policy written inside input_string + * + * This function parses a "policy string" - something the user echo'es into + * /proc/cpufreq or gives as boot parameter - into a struct cpufreq_policy. + * If there are invalid/missing entries, they are replaced with current + * cpufreq policy. + */ +static int cpufreq_parse_policy(char input_string[42], struct cpufreq_policy *policy) +{ + unsigned int min = 0; + unsigned int max = 0; + unsigned int cpu = 0; + char str_governor[16]; + struct cpufreq_policy current_policy; + unsigned int result = -EFAULT; + + if (!policy) + return -EINVAL; + + policy->min = 0; + policy->max = 0; + policy->policy = 0; + policy->cpu = CPUFREQ_ALL_CPUS; + + if (sscanf(input_string, "%d:%d:%d:%15s", &cpu, &min, &max, str_governor) == 4) + { + policy->min = min; + policy->max = max; + policy->cpu = cpu; + result = 0; + goto scan_policy; + } + if (sscanf(input_string, "%d%%%d%%%d%%%15s", &cpu, &min, &max, str_governor) == 4) + { + if (!cpufreq_get_policy(¤t_policy, cpu)) { + policy->min = (min * current_policy.cpuinfo.max_freq) / 100; + policy->max = (max * current_policy.cpuinfo.max_freq) / 100; + policy->cpu = cpu; + result = 0; + goto scan_policy; + } + } + + if (sscanf(input_string, "%d:%d:%15s", &min, &max, str_governor) == 3) + { + policy->min = min; + policy->max = max; + result = 0; + goto scan_policy; + } + + if (sscanf(input_string, "%d%%%d%%%15s", &min, &max, str_governor) == 3) + { + if (!cpufreq_get_policy(¤t_policy, cpu)) { + policy->min = (min * current_policy.cpuinfo.max_freq) / 100; + policy->max = (max * current_policy.cpuinfo.max_freq) / 100; + result = 0; + goto scan_policy; + } + } + + return -EINVAL; + +scan_policy: + result = cpufreq_parse_governor(str_governor, &policy->policy, &policy->governor); + + return result; +} + +/** + * cpufreq_proc_read - read /proc/cpufreq + * + * This function prints out the current cpufreq policy. + */ +static int cpufreq_proc_read ( + char *page, + char **start, + off_t off, + int count, + int *eof, + void *data) +{ + char *p = page; + int len = 0; + struct cpufreq_policy policy; + unsigned int min_pctg = 0; + unsigned int max_pctg = 0; + unsigned int i = 0; + + if (off != 0) + goto end; + + p += sprintf(p, " minimum CPU frequency - maximum CPU frequency - policy\n"); + for (i=0;iname); + break; + default: + p += sprintf(p, "INVALID\n"); + break; + } + } +end: + len = (p - page); + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len>count) + len = count; + if (len<0) + len = 0; + + return len; +} + + +/** + * cpufreq_proc_write - handles writing into /proc/cpufreq + * + * This function calls the parsing script and then sets the policy + * accordingly. + */ +static int cpufreq_proc_write ( + struct file *file, + const char *buffer, + unsigned long count, + void *data) +{ + int result = 0; + char proc_string[42] = {'\0'}; + struct cpufreq_policy policy; + unsigned int i = 0; + + + if ((count > sizeof(proc_string) - 1)) + return -EINVAL; + + if (copy_from_user(proc_string, buffer, count)) + return -EFAULT; + + proc_string[count] = '\0'; + + result = cpufreq_parse_policy(proc_string, &policy); + if (result) + return -EFAULT; + + if (policy.cpu == CPUFREQ_ALL_CPUS) + { + for (i=0; iread_proc = cpufreq_proc_read; + entry->write_proc = cpufreq_proc_write; + } + + return 0; +} + + +/** + * cpufreq_proc_exit - removes "cpufreq" from the /proc root directory. + * + * This function removes "cpufreq" from the /proc root directory. + */ +static void __exit cpufreq_proc_exit (void) +{ + remove_proc_entry("cpufreq", &proc_root); + return; +} + +MODULE_AUTHOR ("Dominik Brodowski "); +MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface"); +MODULE_LICENSE ("GPL"); + +module_init(cpufreq_proc_init); +module_exit(cpufreq_proc_exit); --- /dev/null +++ linux-2.4.27/drivers/cpufreq/userspace.c @@ -0,0 +1,591 @@ +/* + * drivers/cpufreq/userspace.c + * + * Copyright (C) 2001 Russell King + * (C) 2002 - 2003 Dominik Brodowski + * + * $Id: userspace.c,v 1.4 2003/03/07 10:30:20 db Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define CTL_CPU_VARS_SPEED_MAX(cpunr) { \ + .ctl_name = CPU_NR_FREQ_MAX, \ + .data = &cpu_max_freq[cpunr], \ + .procname = "speed-max", \ + .maxlen = sizeof(cpu_max_freq[cpunr]),\ + .mode = 0444, \ + .proc_handler = proc_dointvec, } + +#define CTL_CPU_VARS_SPEED_MIN(cpunr) { \ + .ctl_name = CPU_NR_FREQ_MIN, \ + .data = &cpu_min_freq[cpunr], \ + .procname = "speed-min", \ + .maxlen = sizeof(cpu_min_freq[cpunr]),\ + .mode = 0444, \ + .proc_handler = proc_dointvec, } + +#define CTL_CPU_VARS_SPEED(cpunr) { \ + .ctl_name = CPU_NR_FREQ, \ + .procname = "speed", \ + .mode = 0644, \ + .proc_handler = cpufreq_procctl, \ + .strategy = cpufreq_sysctl, \ + .extra1 = (void*) (cpunr), } + +#define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] = {\ + CTL_CPU_VARS_SPEED_MAX(cpunr), \ + CTL_CPU_VARS_SPEED_MIN(cpunr), \ + CTL_CPU_VARS_SPEED(cpunr), \ + { .ctl_name = 0, }, } + +/* the ctl_table entry for each CPU */ +#define CPU_ENUM(s) { \ + .ctl_name = (CPU_NR + s), \ + .procname = #s, \ + .mode = 0555, \ + .child = ctl_cpu_vars_##s } + +/** + * A few values needed by the userspace governor + */ +static unsigned int cpu_max_freq[NR_CPUS]; +static unsigned int cpu_min_freq[NR_CPUS]; +static unsigned int cpu_cur_freq[NR_CPUS]; +static unsigned int cpu_is_managed[NR_CPUS]; +static struct cpufreq_policy current_policy[NR_CPUS]; + +static DECLARE_MUTEX (userspace_sem); + + +/* keep track of frequency transitions */ +static int +userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct cpufreq_freqs *freq = data; + + cpu_cur_freq[freq->cpu] = freq->new; + + return 0; +} + +static struct notifier_block userspace_cpufreq_notifier_block = { + .notifier_call = userspace_cpufreq_notifier +}; + + +/** + * cpufreq_set - set the CPU frequency + * @freq: target frequency in kHz + * @cpu: CPU for which the frequency is to be set + * + * Sets the CPU frequency to freq. + */ +int cpufreq_set(unsigned int freq, unsigned int cpu) +{ + int ret = -EINVAL; + + down(&userspace_sem); + if (!cpu_is_managed[cpu]) + goto err; + + if (freq < cpu_min_freq[cpu]) + freq = cpu_min_freq[cpu]; + if (freq > cpu_max_freq[cpu]) + freq = cpu_max_freq[cpu]; + + ret = cpufreq_driver_target(¤t_policy[cpu], freq, + CPUFREQ_RELATION_L); + + err: + up(&userspace_sem); + return ret; +} +EXPORT_SYMBOL_GPL(cpufreq_set); + + +/** + * cpufreq_setmax - set the CPU to the maximum frequency + * @cpu - affected cpu; + * + * Sets the CPU frequency to the maximum frequency supported by + * this CPU. + */ +int cpufreq_setmax(unsigned int cpu) +{ + if (!cpu_is_managed[cpu] || !cpu_online(cpu)) + return -EINVAL; + return cpufreq_set(cpu_max_freq[cpu], cpu); +} +EXPORT_SYMBOL_GPL(cpufreq_setmax); + + +/** + * cpufreq_get - get the current CPU frequency (in kHz) + * @cpu: CPU number + * + * Get the CPU current (static) CPU frequency + */ +unsigned int cpufreq_get(unsigned int cpu) +{ + return cpu_cur_freq[cpu]; +} +EXPORT_SYMBOL(cpufreq_get); + + +#ifdef CONFIG_CPU_FREQ_24_API + + +/*********************** cpufreq_sysctl interface ********************/ +static int +cpufreq_procctl(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + char buf[16], *p; + int cpu = (int) ctl->extra1; + int len, left = *lenp; + + if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) { + *lenp = 0; + return 0; + } + + if (write) { + unsigned int freq; + + len = left; + if (left > sizeof(buf)) + left = sizeof(buf); + if (copy_from_user(buf, buffer, left)) + return -EFAULT; + buf[sizeof(buf) - 1] = '\0'; + + freq = simple_strtoul(buf, &p, 0); + cpufreq_set(freq, cpu); + } else { + len = sprintf(buf, "%d\n", cpufreq_get(cpu)); + if (len > left) + len = left; + if (copy_to_user(buffer, buf, len)) + return -EFAULT; + } + + *lenp = len; + filp->f_pos += len; + return 0; +} + +static int +cpufreq_sysctl(ctl_table *table, int *name, int nlen, + void *oldval, size_t *oldlenp, + void *newval, size_t newlen, void **context) +{ + int cpu = (int) table->extra1; + + if (!cpu_online(cpu)) + return -EINVAL; + + if (oldval && oldlenp) { + size_t oldlen; + + if (get_user(oldlen, oldlenp)) + return -EFAULT; + + if (oldlen != sizeof(unsigned int)) + return -EINVAL; + + if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) || + put_user(sizeof(unsigned int), oldlenp)) + return -EFAULT; + } + if (newval && newlen) { + unsigned int freq; + + if (newlen != sizeof(unsigned int)) + return -EINVAL; + + if (get_user(freq, (unsigned int *)newval)) + return -EFAULT; + + cpufreq_set(freq, cpu); + } + return 1; +} + +/* ctl_table ctl_cpu_vars_{0,1,...,(NR_CPUS-1)} */ +/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */ + CTL_TABLE_CPU_VARS(0); +#if NR_CPUS > 1 + CTL_TABLE_CPU_VARS(1); +#endif +#if NR_CPUS > 2 + CTL_TABLE_CPU_VARS(2); +#endif +#if NR_CPUS > 3 + CTL_TABLE_CPU_VARS(3); +#endif +#if NR_CPUS > 4 + CTL_TABLE_CPU_VARS(4); +#endif +#if NR_CPUS > 5 + CTL_TABLE_CPU_VARS(5); +#endif +#if NR_CPUS > 6 + CTL_TABLE_CPU_VARS(6); +#endif +#if NR_CPUS > 7 + CTL_TABLE_CPU_VARS(7); +#endif +#if NR_CPUS > 8 + CTL_TABLE_CPU_VARS(8); +#endif +#if NR_CPUS > 9 + CTL_TABLE_CPU_VARS(9); +#endif +#if NR_CPUS > 10 + CTL_TABLE_CPU_VARS(10); +#endif +#if NR_CPUS > 11 + CTL_TABLE_CPU_VARS(11); +#endif +#if NR_CPUS > 12 + CTL_TABLE_CPU_VARS(12); +#endif +#if NR_CPUS > 13 + CTL_TABLE_CPU_VARS(13); +#endif +#if NR_CPUS > 14 + CTL_TABLE_CPU_VARS(14); +#endif +#if NR_CPUS > 15 + CTL_TABLE_CPU_VARS(15); +#endif +#if NR_CPUS > 16 + CTL_TABLE_CPU_VARS(16); +#endif +#if NR_CPUS > 17 + CTL_TABLE_CPU_VARS(17); +#endif +#if NR_CPUS > 18 + CTL_TABLE_CPU_VARS(18); +#endif +#if NR_CPUS > 19 + CTL_TABLE_CPU_VARS(19); +#endif +#if NR_CPUS > 20 + CTL_TABLE_CPU_VARS(20); +#endif +#if NR_CPUS > 21 + CTL_TABLE_CPU_VARS(21); +#endif +#if NR_CPUS > 22 + CTL_TABLE_CPU_VARS(22); +#endif +#if NR_CPUS > 23 + CTL_TABLE_CPU_VARS(23); +#endif +#if NR_CPUS > 24 + CTL_TABLE_CPU_VARS(24); +#endif +#if NR_CPUS > 25 + CTL_TABLE_CPU_VARS(25); +#endif +#if NR_CPUS > 26 + CTL_TABLE_CPU_VARS(26); +#endif +#if NR_CPUS > 27 + CTL_TABLE_CPU_VARS(27); +#endif +#if NR_CPUS > 28 + CTL_TABLE_CPU_VARS(28); +#endif +#if NR_CPUS > 29 + CTL_TABLE_CPU_VARS(29); +#endif +#if NR_CPUS > 30 + CTL_TABLE_CPU_VARS(30); +#endif +#if NR_CPUS > 31 + CTL_TABLE_CPU_VARS(31); +#endif +#if NR_CPUS > 32 +#error please extend CPU enumeration +#endif + +/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */ +static ctl_table ctl_cpu_table[NR_CPUS + 1] = { + CPU_ENUM(0), +#if NR_CPUS > 1 + CPU_ENUM(1), +#endif +#if NR_CPUS > 2 + CPU_ENUM(2), +#endif +#if NR_CPUS > 3 + CPU_ENUM(3), +#endif +#if NR_CPUS > 4 + CPU_ENUM(4), +#endif +#if NR_CPUS > 5 + CPU_ENUM(5), +#endif +#if NR_CPUS > 6 + CPU_ENUM(6), +#endif +#if NR_CPUS > 7 + CPU_ENUM(7), +#endif +#if NR_CPUS > 8 + CPU_ENUM(8), +#endif +#if NR_CPUS > 9 + CPU_ENUM(9), +#endif +#if NR_CPUS > 10 + CPU_ENUM(10), +#endif +#if NR_CPUS > 11 + CPU_ENUM(11), +#endif +#if NR_CPUS > 12 + CPU_ENUM(12), +#endif +#if NR_CPUS > 13 + CPU_ENUM(13), +#endif +#if NR_CPUS > 14 + CPU_ENUM(14), +#endif +#if NR_CPUS > 15 + CPU_ENUM(15), +#endif +#if NR_CPUS > 16 + CPU_ENUM(16), +#endif +#if NR_CPUS > 17 + CPU_ENUM(17), +#endif +#if NR_CPUS > 18 + CPU_ENUM(18), +#endif +#if NR_CPUS > 19 + CPU_ENUM(19), +#endif +#if NR_CPUS > 20 + CPU_ENUM(20), +#endif +#if NR_CPUS > 21 + CPU_ENUM(21), +#endif +#if NR_CPUS > 22 + CPU_ENUM(22), +#endif +#if NR_CPUS > 23 + CPU_ENUM(23), +#endif +#if NR_CPUS > 24 + CPU_ENUM(24), +#endif +#if NR_CPUS > 25 + CPU_ENUM(25), +#endif +#if NR_CPUS > 26 + CPU_ENUM(26), +#endif +#if NR_CPUS > 27 + CPU_ENUM(27), +#endif +#if NR_CPUS > 28 + CPU_ENUM(28), +#endif +#if NR_CPUS > 29 + CPU_ENUM(29), +#endif +#if NR_CPUS > 30 + CPU_ENUM(30), +#endif +#if NR_CPUS > 31 + CPU_ENUM(31), +#endif +#if NR_CPUS > 32 +#error please extend CPU enumeration +#endif + { + .ctl_name = 0, + } +}; + +static ctl_table ctl_cpu[2] = { + { + .ctl_name = CTL_CPU, + .procname = "cpu", + .mode = 0555, + .child = ctl_cpu_table, + }, + { + .ctl_name = 0, + } +}; + +struct ctl_table_header *cpufreq_sysctl_table; + +static inline void cpufreq_sysctl_init(void) +{ + cpufreq_sysctl_table = register_sysctl_table(ctl_cpu, 0); +} + +static inline void cpufreq_sysctl_exit(void) +{ + unregister_sysctl_table(cpufreq_sysctl_table); +} + +#else +#define cpufreq_sysctl_init() do {} while(0) +#define cpufreq_sysctl_exit() do {} while(0) +#endif /* CONFIG_CPU_FREQ_24API */ + + +/************************** sysfs interface ************************/ +static ssize_t show_speed (struct cpufreq_policy *policy, char *buf) +{ + return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]); +} + +static ssize_t +store_speed (struct cpufreq_policy *policy, const char *buf, size_t count) +{ + unsigned int freq = 0; + unsigned int ret; + + ret = sscanf (buf, "%u", &freq); + if (ret != 1) + return -EINVAL; + + cpufreq_set(freq, policy->cpu); + + return count; +} + +static struct freq_attr freq_attr_scaling_setspeed = { + .attr = { .name = "scaling_setspeed", .mode = 0644 }, + .show = show_speed, + .store = store_speed, +}; + +static int cpufreq_governor_userspace(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu = policy->cpu; + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!try_module_get(THIS_MODULE)) || + !policy->cur) + return -EINVAL; + down(&userspace_sem); + cpu_is_managed[cpu] = 1; + cpu_min_freq[cpu] = policy->min; + cpu_max_freq[cpu] = policy->max; + cpu_cur_freq[cpu] = policy->cur; + sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr); + memcpy (¤t_policy[cpu], policy, sizeof(struct cpufreq_policy)); + up(&userspace_sem); + break; + case CPUFREQ_GOV_STOP: + down(&userspace_sem); + cpu_is_managed[cpu] = 0; + cpu_min_freq[cpu] = 0; + cpu_max_freq[cpu] = 0; + sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr); + up(&userspace_sem); + module_put(THIS_MODULE); + break; + case CPUFREQ_GOV_LIMITS: + down(&userspace_sem); + cpu_min_freq[cpu] = policy->min; + cpu_max_freq[cpu] = policy->max; + if (policy->max < cpu_cur_freq[cpu]) + cpufreq_driver_target(¤t_policy[cpu], policy->max, + CPUFREQ_RELATION_H); + else if (policy->min > cpu_cur_freq[cpu]) + cpufreq_driver_target(¤t_policy[cpu], policy->min, + CPUFREQ_RELATION_L); + memcpy (¤t_policy[cpu], policy, sizeof(struct cpufreq_policy)); + up(&userspace_sem); + break; + } + return 0; +} + +/* on ARM SA1100 we need to rely on the values of cpufreq_get() - because + * of this, cpu_cur_freq[] needs to be set early. + */ +#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_SA1100) +extern unsigned int sa11x0_getspeed(void); + +static void cpufreq_sa11x0_compat(void) +{ + cpu_cur_freq[0] = sa11x0_getspeed(); +} +#else +#define cpufreq_sa11x0_compat() do {} while(0) +#endif + + +static struct cpufreq_governor cpufreq_gov_userspace = { + .name = "userspace", + .governor = cpufreq_governor_userspace, + .owner = THIS_MODULE, +}; +EXPORT_SYMBOL(cpufreq_gov_userspace); + +static int already_init = 0; + +int cpufreq_gov_userspace_init(void) +{ + if (!already_init) { + down(&userspace_sem); + cpufreq_sa11x0_compat(); + cpufreq_sysctl_init(); + cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + already_init = 1; + up(&userspace_sem); + } + return cpufreq_register_governor(&cpufreq_gov_userspace); +} +EXPORT_SYMBOL(cpufreq_gov_userspace_init); + + +static void __exit cpufreq_gov_userspace_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_userspace); + cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + cpufreq_sysctl_exit(); +} + + +MODULE_AUTHOR ("Dominik Brodowski , Russell King "); +MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'"); +MODULE_LICENSE ("GPL"); + +module_init(cpufreq_gov_userspace_init); +module_exit(cpufreq_gov_userspace_exit); --- linux-2.4.27/drivers/i2c/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/Config.in @@ -17,6 +17,15 @@ int ' GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12 int ' GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA 13 fi + + dep_tristate ' Guide GPIO adapter' CONFIG_I2C_GUIDE $CONFIG_I2C_ALGOBIT + if [ "$CONFIG_ARCH_OMAHA" = "y" ]; then + dep_tristate ' Omaha I2C interface' CONFIG_I2C_OMAHA $CONFIG_I2C + fi + if [ "$CONFIG_ARCH_SA1100" = "y" ]; then + dep_tristate ' Frodo I2C adapter' CONFIG_I2C_FRODO $CONFIG_I2C_ALGOBIT + dep_tristate ' SA1100 I2C GPIO adapter' CONFIG_I2C_BIT_SA1100_GPIO $CONFIG_I2C_ALGOBIT + fi fi dep_tristate 'NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_ACB $CONFIG_I2C @@ -49,6 +58,10 @@ dep_tristate 'Keywest I2C interface in Apple Core99 machines' CONFIG_I2C_KEYWEST $CONFIG_I2C fi + if [ "$CONFIG_ARCH_AT91RM9200" = "y" ] ; then + dep_tristate 'Atmel AT91RM9200 I2C Two-Wire interface (TWI)' CONFIG_I2C_AT91 $CONFIG_I2C + fi + if [ "$CONFIG_SIBYTE_SB1xxx_SOC" = "y" ]; then dep_tristate 'SiByte SMBus interface' CONFIG_I2C_ALGO_SIBYTE $CONFIG_I2C dep_tristate ' MAX1617 Temperature Sensor' CONFIG_I2C_MAX1617 $CONFIG_I2C_ALGO_SIBYTE --- linux-2.4.27/drivers/i2c/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/Makefile @@ -8,12 +8,21 @@ i2c-algo-ite.o i2c-algo-sibyte.o i2c-algo-sgi.o \ i2c-proc.o +# Init order: core, chardev, bit adapters, pcf adapters + obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o + +# Bit adapters obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o obj-$(CONFIG_I2C_PHILIPSPAR) += i2c-philips-par.o obj-$(CONFIG_I2C_ELV) += i2c-elv.o obj-$(CONFIG_I2C_VELLEMAN) += i2c-velleman.o +obj-$(CONFIG_I2C_GUIDE) += i2c-guide.o +obj-$(CONFIG_I2C_FRODO) += i2c-frodo.o +obj-$(CONFIG_I2C_OMAHA) += i2c-omaha.o + +# PCF adapters obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o @@ -30,4 +39,3 @@ # This is needed for automatic patch generation: sensors code ends here include $(TOPDIR)/Rules.make - --- linux-2.4.27/drivers/i2c/i2c-algo-bit.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/i2c-algo-bit.c @@ -169,7 +169,14 @@ * 1 if the device acknowledged * 0 if the device did not ack * -ETIMEDOUT if an error occurred (while raising the scl line) - */ + + * tsong@iders.ca: an instruction to disable any timeconsuming interrupt + here except the heart beat of timer2 should be added before setsda(adap, sb). + because when interrupt occurs during + scl is set high, the interrupt service routine is served and may take long, + after the interrupt returns, sda could be sampled by the device(slave) + more than once, and this cause error problem. +*/ static int i2c_outb(struct i2c_adapter *i2c_adap, char c) { int i; @@ -582,9 +589,7 @@ printk("\n"); } -#ifdef MODULE MOD_INC_USE_COUNT; -#endif i2c_add_adapter(adap); return 0; @@ -600,15 +605,13 @@ DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name)); -#ifdef MODULE MOD_DEC_USE_COUNT; -#endif return 0; } -int __init i2c_algo_bit_init (void) +static int __init i2c_algo_bit_init (void) { - printk(KERN_INFO "i2c-algo-bit.o: i2c bit algorithm module\n"); + printk(KERN_DEBUG "i2c-algo-bit.o: i2c bit algorithm module\n"); return 0; } @@ -617,7 +620,6 @@ EXPORT_SYMBOL(i2c_bit_add_bus); EXPORT_SYMBOL(i2c_bit_del_bus); -#ifdef MODULE MODULE_AUTHOR("Simon G. Vogl "); MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); MODULE_LICENSE("GPL"); @@ -631,12 +633,4 @@ MODULE_PARM_DESC(i2c_debug, "debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); -int init_module(void) -{ - return i2c_algo_bit_init(); -} - -void cleanup_module(void) -{ -} -#endif +module_init(i2c_algo_bit_init); --- linux-2.4.27/drivers/i2c/i2c-algo-pcf.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/i2c-algo-pcf.c @@ -475,9 +475,7 @@ return i; } -#ifdef MODULE MOD_INC_USE_COUNT; -#endif i2c_add_adapter(adap); @@ -515,13 +513,11 @@ return res; DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name)); -#ifdef MODULE MOD_DEC_USE_COUNT; -#endif return 0; } -int __init i2c_algo_pcf_init (void) +static int __init i2c_algo_pcf_init (void) { printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n"); return 0; @@ -531,7 +527,6 @@ EXPORT_SYMBOL(i2c_pcf_add_bus); EXPORT_SYMBOL(i2c_pcf_del_bus); -#ifdef MODULE MODULE_AUTHOR("Hans Berglund "); MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); MODULE_LICENSE("GPL"); @@ -543,13 +538,4 @@ MODULE_PARM_DESC(i2c_debug, "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); - -int init_module(void) -{ - return i2c_algo_pcf_init(); -} - -void cleanup_module(void) -{ -} -#endif +module_init(i2c_algo_pcf_init); --- linux-2.4.27/drivers/i2c/i2c-core.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/i2c-core.c @@ -41,7 +41,7 @@ /* exclusive access to the bus */ #define I2C_LOCK(adap) down(&adap->lock) -#define I2C_UNLOCK(adap) up(&adap->lock) +#define I2C_UNLOCK(adap) up(&adap->lock) #define ADAP_LOCK() down(&adap_lock) #define ADAP_UNLOCK() up(&adap_lock) @@ -67,7 +67,7 @@ static int driver_count; /**** debug level */ -static int i2c_debug=1; +static int i2c_debug = 0; /* --------------------------------------------------- * /proc entry declarations @@ -77,14 +77,14 @@ #ifdef CONFIG_PROC_FS static int i2cproc_init(void); -static int i2cproc_cleanup(void); +static void i2cproc_cleanup(void); -static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, +static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, loff_t *ppos); static int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof , void *private); -/* To implement the dynamic /proc/bus/i2c-? files, we need our own +/* To implement the dynamic /proc/bus/i2c-? files, we need our own implementation of the read hook */ static struct file_operations i2cproc_operations = { read: i2cproc_bus_read, @@ -101,8 +101,8 @@ /* --------------------------------------------------- - * registering functions - * --------------------------------------------------- + * registering functions + * --------------------------------------------------- */ /* ----- @@ -119,7 +119,7 @@ if (NULL == adapters[i]) break; if (I2C_ADAP_MAX == i) { - printk(KERN_WARNING + printk(KERN_WARNING " i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", adap->name); res = -ENOMEM; @@ -129,7 +129,7 @@ adapters[i] = adap; adap_count++; ADAP_UNLOCK(); - + /* init data types */ init_MUTEX(&adap->lock); @@ -157,18 +157,18 @@ #endif /* def CONFIG_PROC_FS */ /* inform drivers of new adapters */ - DRV_LOCK(); + DRV_LOCK(); for (j=0;jflags&(I2C_DF_NOTIFY|I2C_DF_DUMMY))) /* We ignore the return code; if it fails, too bad */ drivers[j]->attach_adapter(adap); DRV_UNLOCK(); - + DEB(printk(KERN_DEBUG "i2c-core.o: adapter %s registered as adapter %d.\n", adap->name,i)); - return 0; + return 0; ERROR1: @@ -203,13 +203,13 @@ * this or hell will break loose... */ DRV_LOCK(); - for (j = 0; j < I2C_DRIVER_MAX; j++) + for (j = 0; j < I2C_DRIVER_MAX; j++) if (drivers[j] && (drivers[j]->flags & I2C_DF_DUMMY)) if ((res = drivers[j]->attach_adapter(adap))) { printk(KERN_WARNING "i2c-core.o: can't detach adapter %s " "while detaching driver %s: driver not " "detached!",adap->name,drivers[j]->name); - goto ERROR1; + goto ERROR1; } DRV_UNLOCK(); @@ -241,8 +241,8 @@ adapters[i] = NULL; adap_count--; - - ADAP_UNLOCK(); + + ADAP_UNLOCK(); DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name)); return 0; @@ -269,7 +269,7 @@ if (NULL == drivers[i]) break; if (I2C_DRIVER_MAX == i) { - printk(KERN_WARNING + printk(KERN_WARNING " i2c-core.o: register_driver(%s) " "- enlarge I2C_DRIVER_MAX.\n", driver->name); @@ -279,11 +279,11 @@ drivers[i] = driver; driver_count++; - + DRV_UNLOCK(); /* driver was successfully added */ - + DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name)); - + ADAP_LOCK(); /* now look for instances of driver on our adapters @@ -314,11 +314,11 @@ return -ENODEV; } /* Have a look at each adapter, if clients of this driver are still - * attached. If so, detach them to be able to kill the driver + * attached. If so, detach them to be able to kill the driver * afterwards. */ DEB2(printk(KERN_DEBUG "i2c-core.o: unregister_driver - looking for clients.\n")); - /* removing clients does not depend on the notify flag, else + /* removing clients does not depend on the notify flag, else * invalid operation might (will!) result, when using stale client * pointers. */ @@ -333,7 +333,7 @@ /* DUMMY drivers do not register their clients, so we have to * use a trick here: we call driver->attach_adapter to * *detach* it! Of course, each dummy driver should know about - * this or hell will break loose... + * this or hell will break loose... */ if ((res = driver->attach_adapter(adap))) { printk(KERN_WARNING "i2c-core.o: while unregistering " @@ -345,9 +345,9 @@ return res; } } else { - for (j=0;jclients[j]; - if (client != NULL && + if (client != NULL && client->driver == driver) { DEB2(printk(KERN_DEBUG "i2c-core.o: " "detaching client %s:\n", @@ -376,7 +376,7 @@ drivers[i] = NULL; driver_count--; DRV_UNLOCK(); - + DEB(printk(KERN_DEBUG "i2c-core.o: driver unregistered: %s\n",driver->name)); return 0; } @@ -384,7 +384,7 @@ int i2c_check_addr (struct i2c_adapter *adapter, int addr) { int i; - for (i = 0; i < I2C_CLIENT_MAX ; i++) + for (i = 0; i < I2C_CLIENT_MAX ; i++) if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) return -EBUSY; return 0; @@ -402,7 +402,7 @@ if (NULL == adapter->clients[i]) break; if (I2C_CLIENT_MAX == i) { - printk(KERN_WARNING + printk(KERN_WARNING " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", client->name); return -ENOMEM; @@ -410,9 +410,9 @@ adapter->clients[i] = client; adapter->client_count++; - - if (adapter->client_register) - if (adapter->client_register(client)) + + if (adapter->client_register) + if (adapter->client_register(client)) printk(KERN_DEBUG "i2c-core.o: warning: client_register seems " "to have failed for client %02x at adapter %s\n", client->addr,adapter->name); @@ -421,7 +421,7 @@ if(client->flags & I2C_CLIENT_ALLOW_USE) client->usage_count = 0; - + return 0; } @@ -440,12 +440,12 @@ client->name); return -ENODEV; } - - if( (client->flags & I2C_CLIENT_ALLOW_USE) && + + if( (client->flags & I2C_CLIENT_ALLOW_USE) && (client->usage_count>0)) return -EBUSY; - - if (adapter->client_unregister != NULL) + + if (adapter->client_unregister != NULL) if ((res = adapter->client_unregister(client))) { printk(KERN_ERR "i2c-core.o: client_unregister [%s] failed, " "client not detached",client->name); @@ -471,7 +471,7 @@ void i2c_dec_use_client(struct i2c_client *client) { - + if (client->driver->dec_use != NULL) client->driver->dec_use(client); @@ -479,65 +479,65 @@ client->adapter->dec_use(client->adapter); } -struct i2c_client *i2c_get_client(int driver_id, int adapter_id, +struct i2c_client *i2c_get_client(int driver_id, int adapter_id, struct i2c_client *prev) { int i,j; - + /* Will iterate through the list of clients in each adapter of adapters-list - in search for a client that matches the search criteria. driver_id or - adapter_id are ignored if set to 0. If both are ignored this returns + in search for a client that matches the search criteria. driver_id or + adapter_id are ignored if set to 0. If both are ignored this returns first client found. */ - - i = j = 0; - - /* set starting point */ + + i = j = 0; + + /* set starting point */ if(prev) { if(!(prev->adapter)) return (struct i2c_client *) -EINVAL; - + for(j=0; j < I2C_ADAP_MAX; j++) if(prev->adapter == adapters[j]) break; - + /* invalid starting point? */ if (I2C_ADAP_MAX == j) { printk(KERN_WARNING " i2c-core.o: get_client adapter for client:[%s] not found\n", prev->name); return (struct i2c_client *) -ENODEV; - } - + } + for(i=0; i < I2C_CLIENT_MAX; i++) if(prev == adapters[j]->clients[i]) break; - + /* invalid starting point? */ if (I2C_CLIENT_MAX == i) { printk(KERN_WARNING " i2c-core.o: get_client client:[%s] not found\n", prev->name); return (struct i2c_client *) -ENODEV; - } - + } + i++; /* start from one after prev */ } - + for(; j < I2C_ADAP_MAX; j++) { if(!adapters[j]) continue; - + if(adapter_id && (adapters[j]->id != adapter_id)) continue; - + for(; i < I2C_CLIENT_MAX; i++) { if(!adapters[j]->clients[i]) continue; - + if(driver_id && (adapters[j]->clients[i]->driver->id != driver_id)) continue; - if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE) + if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE) return adapters[j]->clients[i]; } i = 0; @@ -549,12 +549,12 @@ int i2c_use_client(struct i2c_client *client) { if(client->flags & I2C_CLIENT_ALLOW_USE) { - if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) + if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) client->usage_count++; else { - if(client->usage_count > 0) + if(client->usage_count > 0) return -EBUSY; - else + else client->usage_count++; } } @@ -575,9 +575,9 @@ return -EPERM; } } - + i2c_dec_use_client(client); - + return 0; } @@ -589,7 +589,7 @@ #ifdef CONFIG_PROC_FS /* This function generates the output for /proc/bus/i2c */ -int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, void *private) { int i; @@ -615,7 +615,7 @@ } /* This function generates the output for /proc/bus/i2c-? */ -ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, +ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, loff_t *ppos) { struct inode * inode = file->f_dentry->d_inode; @@ -626,7 +626,7 @@ int order[I2C_CLIENT_MAX]; if (count > 4000) - return -EINVAL; + return -EINVAL; len_total = file->f_pos + count; /* Too bad if this gets longer (unlikely) */ if (len_total > 4000) @@ -641,12 +641,12 @@ sorted by address */ order_nr=0; for (j = 0; j < I2C_CLIENT_MAX; j++) { - if ((client = adapters[i]->clients[j]) && + if ((client = adapters[i]->clients[j]) && (client->driver->id != I2C_DRIVERID_I2CDEV)) { - for(k = order_nr; - (k > 0) && + for(k = order_nr; + (k > 0) && adapters[i]->clients[order[k-1]]-> - addr > client->addr; + addr > client->addr; k--) order[k] = order[k-1]; order[k] = j; @@ -665,7 +665,7 @@ len = len - file->f_pos; if (len > count) len = count; - if (len < 0) + if (len < 0) len = 0; if (copy_to_user (buf,kbuf+file->f_pos, len)) { kfree(kbuf); @@ -689,7 +689,7 @@ printk("i2c-core.o: /proc/bus/ does not exist"); i2cproc_cleanup(); return -ENOENT; - } + } proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); if (!proc_bus_i2c) { printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); @@ -702,14 +702,13 @@ return 0; } -int i2cproc_cleanup(void) +static void i2cproc_cleanup(void) { if (i2cproc_initialized >= 1) { remove_proc_entry("i2c",proc_bus); i2cproc_initialized -= 2; } - return 0; } @@ -751,10 +750,10 @@ msg.flags = client->flags & I2C_M_TEN; msg.len = count; (const char *)msg.buf = buf; - + DEB2(printk(KERN_DEBUG "i2c-core.o: master_send: writing %d bytes on %s.\n", count,client->adapter->name)); - + I2C_LOCK(adap); ret = adap->algo->master_xfer(adap,&msg,1); I2C_UNLOCK(adap); @@ -784,14 +783,14 @@ DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: reading %d bytes on %s.\n", count,client->adapter->name)); - + I2C_LOCK(adap); ret = adap->algo->master_xfer(adap,&msg,1); I2C_UNLOCK(adap); - + DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n", ret, count, client->addr)); - + /* if everything went ok (i.e. 1 msg transmitted), return #bytes * transmitted, else error code. */ @@ -852,7 +851,7 @@ found = 0; for (i = 0; !found && (address_data->force[i] != I2C_CLIENT_END); i += 3) { - if (((adap_id == address_data->force[i]) || + if (((adap_id == address_data->force[i]) || (address_data->force[i] == ANY_I2C_BUS)) && (addr == address_data->force[i+1])) { DEB2(printk(KERN_DEBUG "i2c-core.o: found force parameter for adapter %d, addr %04x\n", @@ -862,7 +861,7 @@ found = 1; } } - if (found) + if (found) continue; /* If this address is in one of the ignores, we can forget about @@ -870,7 +869,7 @@ for (i = 0; !found && (address_data->ignore[i] != I2C_CLIENT_END); i += 2) { - if (((adap_id == address_data->ignore[i]) || + if (((adap_id == address_data->ignore[i]) || ((address_data->ignore[i] == ANY_I2C_BUS))) && (addr == address_data->ignore[i+1])) { DEB2(printk(KERN_DEBUG "i2c-core.o: found ignore parameter for adapter %d, " @@ -890,11 +889,11 @@ found = 1; } } - if (found) + if (found) continue; - /* Now, we will do a detection, but only if it is in the normal or - probe entries */ + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ for (i = 0; !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); i += 1) { @@ -939,7 +938,7 @@ "addr %04x\n", adap_id,addr)); } } - if (!found) + if (!found) continue; /* OK, so we really should examine this address. First check @@ -1087,11 +1086,11 @@ I2C_SMBUS_I2C_BLOCK_DATA,&data); } -/* Simulate a SMBus command using the i2c protocol +/* Simulate a SMBus command using the i2c protocol No checking of parameters is done! */ -static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, unsigned short flags, - char read_write, u8 command, int size, + char read_write, u8 command, int size, union i2c_smbus_data * data) { /* So we need to generate a series of msgs. In the case of writing, we @@ -1101,7 +1100,7 @@ unsigned char msgbuf0[34]; unsigned char msgbuf1[34]; int num = read_write == I2C_SMBUS_READ?2:1; - struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, + struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, { addr, flags | I2C_M_RD, 0, msgbuf1 } }; int i; @@ -1179,7 +1178,7 @@ case I2C_SMBUS_BYTE_DATA: data->byte = msgbuf1[0]; break; - case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_WORD_DATA: case I2C_SMBUS_PROC_CALL: data->word = msgbuf1[0] | (msgbuf1[1] << 8); break; @@ -1189,7 +1188,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, - char read_write, u8 command, int size, + char read_write, u8 command, int size, union i2c_smbus_data * data) { s32 res; @@ -1207,7 +1206,7 @@ /* You should always define `functionality'; the 'else' is just for - backward compatibility. */ + backward compatibility. */ u32 i2c_get_functionality (struct i2c_adapter *adap) { if (adap->algo->functionality) @@ -1233,122 +1232,12 @@ init_MUTEX(&adap_lock); init_MUTEX(&driver_lock); - - i2cproc_init(); - - return 0; -} - -#ifndef MODULE -#ifdef CONFIG_I2C_CHARDEV - extern int i2c_dev_init(void); -#endif -#ifdef CONFIG_I2C_ALGOBIT - extern int i2c_algo_bit_init(void); -#endif -#ifdef CONFIG_I2C_PHILIPSPAR - extern int i2c_bitlp_init(void); -#endif -#ifdef CONFIG_I2C_ELV - extern int i2c_bitelv_init(void); -#endif -#ifdef CONFIG_I2C_VELLEMAN - extern int i2c_bitvelle_init(void); -#endif -#ifdef CONFIG_I2C_BITVIA - extern int i2c_bitvia_init(void); -#endif -#ifdef CONFIG_I2C_ALGOPCF - extern int i2c_algo_pcf_init(void); -#endif -#ifdef CONFIG_I2C_ELEKTOR - extern int i2c_pcfisa_init(void); -#endif - -#ifdef CONFIG_I2C_ALGO8XX - extern int i2c_algo_8xx_init(void); -#endif -#ifdef CONFIG_I2C_RPXLITE - extern int i2c_rpx_init(void); -#endif - -#ifdef CONFIG_I2C_ALGO_SIBYTE - extern int i2c_algo_sibyte_init(void); - extern int i2c_sibyte_init(void); -#endif -#ifdef CONFIG_I2C_MAX1617 - extern int i2c_max1617_init(void); -#endif - -#ifdef CONFIG_I2C_PROC - extern int sensors_init(void); -#endif - -/* This is needed for automatic patch generation: sensors code starts here */ -/* This is needed for automatic patch generation: sensors code ends here */ - -int __init i2c_init_all(void) -{ - /* --------------------- global ----- */ - i2c_init(); - -#ifdef CONFIG_I2C_CHARDEV - i2c_dev_init(); -#endif - /* --------------------- bit -------- */ -#ifdef CONFIG_I2C_ALGOBIT - i2c_algo_bit_init(); -#endif -#ifdef CONFIG_I2C_PHILIPSPAR - i2c_bitlp_init(); -#endif -#ifdef CONFIG_I2C_ELV - i2c_bitelv_init(); -#endif -#ifdef CONFIG_I2C_VELLEMAN - i2c_bitvelle_init(); -#endif - - /* --------------------- pcf -------- */ -#ifdef CONFIG_I2C_ALGOPCF - i2c_algo_pcf_init(); -#endif -#ifdef CONFIG_I2C_ELEKTOR - i2c_pcfisa_init(); -#endif - - /* --------------------- 8xx -------- */ -#ifdef CONFIG_I2C_ALGO8XX - i2c_algo_8xx_init(); -#endif -#ifdef CONFIG_I2C_RPXLITE - i2c_rpx_init(); -#endif - - /* --------------------- SiByte -------- */ -#ifdef CONFIG_I2C_ALGO_SIBYTE - i2c_algo_sibyte_init(); - i2c_sibyte_init(); -#endif -#ifdef CONFIG_I2C_MAX1617 - i2c_max1617_init(); -#endif - - /* -------------- proc interface ---- */ -#ifdef CONFIG_I2C_PROC - sensors_init(); -#endif -/* This is needed for automatic patch generation: sensors code starts here */ -/* This is needed for automatic patch generation: sensors code ends here */ + i2cproc_init(); return 0; } -#endif - - - EXPORT_SYMBOL(i2c_add_adapter); EXPORT_SYMBOL(i2c_del_adapter); EXPORT_SYMBOL(i2c_add_driver); @@ -1385,7 +1274,6 @@ EXPORT_SYMBOL(i2c_get_functionality); EXPORT_SYMBOL(i2c_check_functionality); -#ifdef MODULE MODULE_AUTHOR("Simon G. Vogl "); MODULE_DESCRIPTION("I2C-Bus main module"); MODULE_LICENSE("GPL"); @@ -1393,13 +1281,5 @@ MODULE_PARM(i2c_debug, "i"); MODULE_PARM_DESC(i2c_debug,"debug level"); -int init_module(void) -{ - return i2c_init(); -} - -void cleanup_module(void) -{ - i2cproc_cleanup(); -} -#endif +module_init(i2c_init); +module_exit(i2cproc_cleanup); --- linux-2.4.27/drivers/i2c/i2c-dev.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/i2c-dev.c @@ -1,5 +1,5 @@ /* - i2c-dev.c - i2c-bus driver, char device interface + i2c-dev.c - i2c-bus driver, char device interface Copyright (C) 1995-97 Simon G. Vogl Copyright (C) 1998-99 Frodo Looijaard @@ -25,7 +25,7 @@ /* The I2C_RDWR ioctl code is written by Kolja Waschk */ -/* The devfs code is contributed by Philipp Matthias Hahn +/* The devfs code is contributed by Philipp Matthias Hahn */ /* $Id: i2c-dev.c,v 1.40 2001/08/25 01:28:01 mds Exp $ */ @@ -50,19 +50,14 @@ #include #include -#ifdef MODULE -extern int init_module(void); -extern int cleanup_module(void); -#endif /* def MODULE */ - /* struct file_operations changed too often in the 2.1 series for nice code */ -static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, loff_t *offset); -static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, loff_t *offset); -static int i2cdev_ioctl (struct inode *inode, struct file *file, +static int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static int i2cdev_open (struct inode *inode, struct file *file); @@ -73,13 +68,8 @@ static int i2cdev_command(struct i2c_client *client, unsigned int cmd, void *arg); -#ifdef MODULE -static -#else -extern -#endif - int __init i2c_dev_init(void); -static int i2cdev_cleanup(void); +static int __init i2c_dev_init(void); +static void i2cdev_cleanup(void); static struct file_operations i2cdev_fops = { owner: THIS_MODULE, @@ -185,7 +175,7 @@ return ret; } -int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, +int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct i2c_client *client = (struct i2c_client *)file->private_data; @@ -198,14 +188,14 @@ unsigned long funcs; #ifdef DEBUG - printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", MINOR(inode->i_rdev),cmd, arg); #endif /* DEBUG */ switch ( cmd ) { case I2C_SLAVE: case I2C_SLAVE_FORCE: - if ((arg > 0x3ff) || + if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) return -EINVAL; if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) @@ -224,8 +214,8 @@ sizeof(unsigned long)))?-EFAULT:0; case I2C_RDWR: - if (copy_from_user(&rdwr_arg, - (struct i2c_rdwr_ioctl_data *)arg, + if (copy_from_user(&rdwr_arg, + (struct i2c_rdwr_ioctl_data *)arg, sizeof(rdwr_arg))) return -EFAULT; @@ -233,9 +223,9 @@ * be sent at once */ if (rdwr_arg.nmsgs > 42) return -EINVAL; - + rdwr_pa = (struct i2c_msg *) - kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), + kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL); if (rdwr_pa == NULL) return -ENOMEM; @@ -312,9 +302,9 @@ (struct i2c_smbus_ioctl_data *) arg, sizeof(struct i2c_smbus_ioctl_data))) return -EFAULT; - if ((data_arg.size != I2C_SMBUS_BYTE) && + if ((data_arg.size != I2C_SMBUS_BYTE) && (data_arg.size != I2C_SMBUS_QUICK) && - (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && (data_arg.size != I2C_SMBUS_WORD_DATA) && (data_arg.size != I2C_SMBUS_PROC_CALL) && (data_arg.size != I2C_SMBUS_BLOCK_DATA) && @@ -325,9 +315,9 @@ #endif return -EINVAL; } - /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, so the check is valid if size==I2C_SMBUS_QUICK too. */ - if ((data_arg.read_write != I2C_SMBUS_READ) && + if ((data_arg.read_write != I2C_SMBUS_READ) && (data_arg.read_write != I2C_SMBUS_WRITE)) { #ifdef DEBUG printk(KERN_DEBUG "i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", @@ -339,7 +329,7 @@ /* Note that command values are always valid! */ if ((data_arg.size == I2C_SMBUS_QUICK) || - ((data_arg.size == I2C_SMBUS_BYTE) && + ((data_arg.size == I2C_SMBUS_BYTE) && (data_arg.read_write == I2C_SMBUS_WRITE))) /* These are special: we do not use data */ return i2c_smbus_xfer(client->adapter, client->addr, @@ -358,13 +348,13 @@ if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || (data_arg.size == I2C_SMBUS_BYTE)) datasize = sizeof(data_arg.data->byte); - else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || (data_arg.size == I2C_SMBUS_PROC_CALL)) datasize = sizeof(data_arg.data->word); else /* size == I2C_SMBUS_BLOCK_DATA */ datasize = sizeof(data_arg.data->block); - if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || (data_arg.read_write == I2C_SMBUS_WRITE)) { if (copy_from_user(&temp, data_arg.data, datasize)) return -EFAULT; @@ -372,7 +362,7 @@ res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, data_arg.read_write, data_arg.command,data_arg.size,&temp); - if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || (data_arg.read_write == I2C_SMBUS_READ))) { if (copy_to_user(data_arg.data, &temp, datasize)) return -EFAULT; @@ -479,7 +469,7 @@ return -1; } -int __init i2c_dev_init(void) +static int __init i2c_dev_init(void) { int res; @@ -509,7 +499,7 @@ return 0; } -int i2cdev_cleanup(void) +static void i2cdev_cleanup(void) { int res; @@ -517,9 +507,9 @@ if ((res = i2c_del_driver(&i2cdev_driver))) { printk("i2c-dev.o: Driver deregistration failed, " "module not removed.\n"); - return res; + return; } - i2cdev_initialized --; + i2cdev_initialized --; } if (i2cdev_initialized >= 1) { @@ -531,30 +521,17 @@ #endif printk("i2c-dev.o: unable to release major %d for i2c bus\n", I2C_MAJOR); - return res; + return; } i2cdev_initialized --; } - return 0; } EXPORT_NO_SYMBOLS; -#ifdef MODULE - MODULE_AUTHOR("Frodo Looijaard and Simon G. Vogl "); MODULE_DESCRIPTION("I2C /dev entries driver"); MODULE_LICENSE("GPL"); -int init_module(void) -{ - return i2c_dev_init(); -} - -int cleanup_module(void) -{ - return i2cdev_cleanup(); -} - -#endif /* def MODULE */ - +module_init(i2c_dev_init); +module_exit(i2cdev_cleanup); --- linux-2.4.27/drivers/i2c/i2c-elektor.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/i2c-elektor.c @@ -181,16 +181,12 @@ static void pcf_isa_inc_use(struct i2c_adapter *adap) { -#ifdef MODULE MOD_INC_USE_COUNT; -#endif } static void pcf_isa_dec_use(struct i2c_adapter *adap) { -#ifdef MODULE MOD_DEC_USE_COUNT; -#endif } @@ -219,7 +215,7 @@ pcf_isa_unreg, }; -int __init i2c_pcfisa_init(void) +static int __init i2c_pcfisa_init(void) { #ifdef __alpha__ /* check to see we have memory mapped PCF8584 connected to the @@ -289,10 +285,14 @@ return 0; } +static void i2c_pcfisa_exit(void) +{ + i2c_pcf_del_bus(&pcf_isa_ops); + pcf_isa_exit(); +} EXPORT_NO_SYMBOLS; -#ifdef MODULE MODULE_AUTHOR("Hans Berglund "); MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); MODULE_LICENSE("GPL"); @@ -304,15 +304,5 @@ MODULE_PARM(mmapped, "i"); MODULE_PARM(i2c_debug, "i"); -int init_module(void) -{ - return i2c_pcfisa_init(); -} - -void cleanup_module(void) -{ - i2c_pcf_del_bus(&pcf_isa_ops); - pcf_isa_exit(); -} - -#endif +module_init(i2c_pcfisa_init); +module_exit(i2c_pcfisa_exit); --- linux-2.4.27/drivers/i2c/i2c-elv.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/i2c-elv.c @@ -75,7 +75,7 @@ PortData |=2; } outb(PortData, DATA); -} +} static int bit_elv_getscl(void *data) { @@ -90,7 +90,7 @@ static int bit_elv_init(void) { if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { - return -ENODEV; + return -ENODEV; } else { /* test for ELV adap. */ if (inb(base+1) & 0x80) { /* BUSY should be high */ @@ -131,16 +131,12 @@ static void bit_elv_inc_use(struct i2c_adapter *adap) { -#ifdef MODULE MOD_INC_USE_COUNT; -#endif } static void bit_elv_dec_use(struct i2c_adapter *adap) { -#ifdef MODULE MOD_DEC_USE_COUNT; -#endif } /* ------------------------------------------------------------------------ @@ -164,10 +160,10 @@ bit_elv_inc_use, bit_elv_dec_use, bit_elv_reg, - bit_elv_unreg, + bit_elv_unreg, }; -int __init i2c_bitelv_init(void) +static int __init i2c_bitelv_init(void) { printk(KERN_INFO "i2c-elv.o: i2c ELV parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); if (base==0) { @@ -194,24 +190,19 @@ } +static void __exit i2c_bitelv_exit(void) +{ + i2c_bit_del_bus(&bit_elv_ops); + bit_elv_exit(); +} + EXPORT_NO_SYMBOLS; -#ifdef MODULE MODULE_AUTHOR("Simon G. Vogl "); MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter"); MODULE_LICENSE("GPL"); MODULE_PARM(base, "i"); -int init_module(void) -{ - return i2c_bitelv_init(); -} - -void cleanup_module(void) -{ - i2c_bit_del_bus(&bit_elv_ops); - bit_elv_exit(); -} - -#endif +module_init(i2c_bitelv_init); +module_exit(i2c_bitelv_exit); --- /dev/null +++ linux-2.4.27/drivers/i2c/i2c-frodo.c @@ -0,0 +1,114 @@ + +/* + * linux/drivers/i2c/i2c-frodo.c + * + * Author: Abraham van der Merwe + * + * An I2C adapter driver for the 2d3D, Inc. StrongARM SA-1110 + * Development board (Frodo). + * + * This source code is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +static void frodo_setsda (void *data,int state) +{ + if (state) + frodo_cpld_set (FRODO_CPLD_I2C,FRODO_I2C_SDA_OUT); + else + frodo_cpld_clear (FRODO_CPLD_I2C,FRODO_I2C_SDA_OUT); +} + +static void frodo_setscl (void *data,int state) +{ + if (state) + frodo_cpld_set (FRODO_CPLD_I2C,FRODO_I2C_SCL_OUT); + else + frodo_cpld_clear (FRODO_CPLD_I2C,FRODO_I2C_SCL_OUT); +} + +static int frodo_getsda (void *data) +{ + return ((frodo_cpld_read (FRODO_CPLD_I2C) & FRODO_I2C_SDA_IN) != 0); +} + +static int frodo_getscl (void *data) +{ + return ((frodo_cpld_read (FRODO_CPLD_I2C) & FRODO_I2C_SCL_IN) != 0); +} + +static struct i2c_algo_bit_data bit_frodo_data = { + setsda: frodo_setsda, + setscl: frodo_setscl, + getsda: frodo_getsda, + getscl: frodo_getscl, + udelay: 80, + mdelay: 80, + timeout: 100 +}; + +static int frodo_client_register (struct i2c_client *client) +{ + return (0); +} + +static int frodo_client_unregister (struct i2c_client *client) +{ + return (0); +} + +static void frodo_inc_use (struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +static void frodo_dec_use (struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +static struct i2c_adapter frodo_ops = { + name: "Frodo adapter driver", + id: I2C_HW_B_FRODO, + algo: NULL, + algo_data: &bit_frodo_data, + inc_use: frodo_inc_use, + dec_use: frodo_dec_use, + client_register: frodo_client_register, + client_unregister: frodo_client_unregister +}; + +static int __init i2c_frodo_init (void) +{ + return (i2c_bit_add_bus (&frodo_ops)); +} + +EXPORT_NO_SYMBOLS; + +static void __exit i2c_frodo_exit (void) +{ + i2c_bit_del_bus (&frodo_ops); +} + +MODULE_AUTHOR ("Abraham van der Merwe "); +MODULE_DESCRIPTION ("I2C-Bus adapter routines for Frodo"); +MODULE_LICENSE ("GPL"); +EXPORT_NO_SYMBOLS; + +module_init (i2c_frodo_init); +module_exit (i2c_frodo_exit); + --- /dev/null +++ linux-2.4.27/drivers/i2c/i2c-guide.c @@ -0,0 +1,199 @@ +/************************************************************************************\ +Copyright : Copyright (C) 1995-2000 Simon G. Vogl + Copyright 2002 IDERs Incorporated +File Name : i2c-guide.c +Description : this i2c driver uses the GPIO port B pin 0 and pin 1 on the cs89712. +Notes : To change the bit rate, change the structure i2c_algo_bit_data + : to 10 10 100 +Contact : tsong@iders.ca +License : This source code is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. +\************************************************************************************/ + +#include +#include +#include +#include +#include /* for 2.0 kernels to get NULL */ +#include /* for 2.0 kernels to get ENODEV */ +#include + +#include // io operation ep_writel() +#include // io operation clps_writel() +#include // io operation clps_writel() + +#include +#include + +/* ----- global defines ----------------------------------------------- */ + +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + /* Pin Port Inverted name */ +#define I2C_SDA 0x08 /* port B ctrl pin 3 (inv) */ +#define I2C_SCL 0x04 /* port B ctrl pin 2 (inv) */ + +#define I2C_SDAIN 0x08 /* use the same pin with output */ +#define I2C_SCLIN 0x04 /* use the same pin with output */ + +#define I2C_DMASK 0xf7 /* inverse of I2C_SDA */ +#define I2C_CMASK 0xfb /* inverse of I2c_SCL */ + +#define PORTB_PIN0_SDA_OUTPUT 0x08 /* pin 3 direction of port B output */ +#define PORTB_PIN0_SDA_INPUT 0xf7 /* pin 3 direction of port B input */ + +#define PORTB_PIN1_SCL_OUTPUT 0x04 /* pin 2 direction of port B output */ +#define PORTB_PIN1_SCL_INPUT 0xfb /* pin 2 direction of port B input */ + +int base = 0; +#define DEFAULT_BASE PBDR + +/* ----- local functions --------------------------------------------------- */ + +static void bit_guide_setscl(void* data, int state) +{ + if (state) { + // set port B pin2 input + clps_writeb((clps_readb(PBDDR)) & PORTB_PIN1_SCL_INPUT, PBDDR); + } + else { + // clear + clps_writeb((clps_readb(PBDR)) & I2C_CMASK, PBDR); + // set port B pin2 output + clps_writeb((clps_readb(PBDDR)) | PORTB_PIN1_SCL_OUTPUT, PBDDR); + } +} + +static void bit_guide_setsda(void* data, int state) +{ + if (state) { + clps_writeb((clps_readb(PBDDR)) & PORTB_PIN0_SDA_INPUT, PBDDR); + // float pin 0 (actually drive high by pull up resistor) + // clps_writeb((clps_readb(PBDR)) | I2C_SDA, PBDR); // set Jan4 ori: eff + // printk("set sda high, state=%i\n",state); + } + else { + // clear + clps_writeb((clps_readb(PBDR)) & I2C_DMASK, PBDR); + // set port B pin 0 output + clps_writeb((clps_readb(PBDDR)) | PORTB_PIN0_SDA_OUTPUT, PBDDR); + } +} + +static int bit_guide_getscl(void *data) +{ + return ( 0 != ( (clps_readb(PBDR)) & I2C_SCLIN ) ); +} + +static int bit_guide_getsda(void *data) +{ + // set port B pin 0 input Jan4 ori eff + clps_writeb((clps_readb(PBDDR)) & PORTB_PIN0_SDA_INPUT, PBDDR); + return ( 0 != ( (clps_readb(PBDR) ) & I2C_SDAIN ) ); +} + +static int bit_guide_init(void) +{ + bit_guide_setsda((void*)base,1); + bit_guide_setscl((void*)base,1); + return 0; +} + +static int bit_guide_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_guide_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_guide_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_guide_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +/* last line (us, ms, timout) + * us dominates the bit rate: 10us means: 100Kbit/sec(25 means 40kbps) + * 10ms not known + * 100ms timeout + */ +static struct i2c_algo_bit_data bit_guide_data = { + NULL, + bit_guide_setsda, + bit_guide_setscl, + bit_guide_getsda, + bit_guide_getscl, + 50, 10, 100, /* orginal (non-guide) value 10, 10, 100 */ +}; + +static struct i2c_adapter bit_guide_ops = { + "Guide Port B: PIN2-SCL/PIN3-SDA", + I2C_HW_B_GUIDE, + NULL, + &bit_guide_data, + bit_guide_inc_use, + bit_guide_dec_use, + bit_guide_reg, + bit_guide_unreg, +}; + +static int __init i2c_bitguide_init(void) +{ + printk("i2c-guide.o: Guide i2c port B adapter module.\n"); + clps_writeb((clps_readb(PBDDR)) & 0xfd, PBDDR); // set service reuest pb1 as input + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_guide_data.data=(void*)DEFAULT_BASE; + if (bit_guide_init()==0) { + if(i2c_bit_add_bus(&bit_guide_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_guide_data.data=(void*)base; + if (bit_guide_init()==0) { + if(i2c_bit_add_bus(&bit_guide_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-guide.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("T. C. Song "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Guide (cs89712) GPIO port B"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(base, "i"); + +module_init(i2c_bitguide_init); +/* for completeness, we should have a module_exit() function, but the + GUIDE requires this to always be loaded. If it is unloaded, the + operation of the GUIDE is undefined. + Nobody has written the i2c_bitguide_exit() routine yet, so it is not included. +module_exit(i2c_bitguide_exit); +*/ --- /dev/null +++ linux-2.4.27/drivers/i2c/i2c-omaha.c @@ -0,0 +1,276 @@ +/* ------------------------------------------------------------------------- * + Copyright ARM Limited 2002. All rights reserved. + + i2c driver for Omaha + + Notes:Based on i2c-elv.c + + The S3C2400X01 has better support for I2C, but bit oriented operations + are directly supported by the other I2C layers, so we use that method + of performing I2C operations. + + Copyright (C) 1995-2000 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; +#define DEB3(x) if (i2c_debug>=3) x +#define DEBE(x) x // error messages +#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) { x; } + /* debug the protocol by showing transferred bits */ + +/* Register and bitdefs for Omaha */ + +// Port G control registers +static volatile unsigned int pgcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGCON); +static volatile unsigned int pgdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGDAT); + +static volatile unsigned int opencr = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_OPENCR); + +static int base = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGCON); + +// Open drain control registers +#define OPC_CMD BIT2 +#define OPC_DAT BIT3 + +// data bits in GPIO Port G data register +#define OMAHA_SDA BIT5 +#define OMAHA_SCL BIT6 +#define IIC_WP BIT3 // Write Protect for EEPROM + +// input/out select bits in GPIO G control register +#define IIC_BITS (BIT12|BIT10|BIT6); + + +/* ----- local functions ---------------------------------------------- */ + + +static void bit_omaha_setscl(void *data, int state) +{ + unsigned int tmp; + + if (state) + { + tmp = __raw_readl(pgdat); + tmp |= OMAHA_SCL; + __raw_writel(tmp,pgdat); + } + else + { + tmp = __raw_readl(pgdat); + tmp &= ~OMAHA_SCL; + __raw_writel(tmp,pgdat); + } +} + +static void bit_omaha_setsda(void *data, int state) +{ + unsigned int tmp; + + // ensure that sda is an output at the moment + tmp = __raw_readl(pgcon); + tmp = tmp | BIT10; + __raw_writel(tmp,pgcon); + + if (state) + { + tmp = __raw_readl(pgdat); + tmp |= OMAHA_SDA; + __raw_writel(tmp,pgdat); + } + else + { + tmp = __raw_readl(pgdat); + tmp &= ~OMAHA_SDA; + __raw_writel(tmp,pgdat); + } +} + +static int bit_omaha_getscl(void *data) +{ + if (__raw_readl(pgdat) & OMAHA_SCL) + return 1; + else + return 0; +} + +static int bit_omaha_getsda(void *data) +{ + unsigned int tmp; + + // ensure that sda is an output at the moment + tmp = __raw_readl(pgcon); + tmp = tmp & ~BIT10; + __raw_writel(tmp,pgcon); + + if (__raw_readl(pgdat) & OMAHA_SDA) + return 1; + else + return 0; +} + +static int bit_omaha_init(void) +{ + // Have we got some mmapped space? + if (request_region(base, 0x100, "i2c (omaha bus adapter)") < 0 ) + { + printk("i2c-omaha.o: requested I/O region (0x%08x) is in use.\n", base); + return -ENODEV; + } + + return 0; +} + + +static int bit_omaha_reg(struct i2c_client *client) +{ + return 0; +} + + +static int bit_omaha_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_omaha_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bit_omaha_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_bit_data bit_omaha_data = { + NULL, + bit_omaha_setsda, + bit_omaha_setscl, + bit_omaha_getsda, + bit_omaha_getscl, + 10, 10, 20, /* waits, timeout */ +}; + +static struct i2c_adapter bit_omaha_ops = { + "BIT-Type Omaha I2C adapter", + I2C_HW_B_OMAHA, + NULL, + &bit_omaha_data, + bit_omaha_inc_use, + bit_omaha_dec_use, + bit_omaha_reg, + bit_omaha_unreg, +}; + +static int __init i2c_omaha_init (void) +{ + unsigned int tmp; + + printk("i2c-omaha.o: i2c omaha adapter module\n"); + + if (bit_omaha_init() == 0) { + if(i2c_bit_add_bus(&bit_omaha_ops) < 0) + { + printk("Could not add bus!\n"); + return -ENODEV; + } + } else { + printk("Could not pcf_omaha_init\n"); + return -ENODEV; + } + + // Program Port G bits to output function + tmp = __raw_readl(pgcon); + tmp |= IIC_BITS; + __raw_writel(tmp,pgcon); + + // Ensure SDA and SCL are open-drain + tmp = __raw_readl(opencr); + tmp = tmp | OPC_CMD | OPC_DAT; + __raw_writel(tmp,opencr); + + bit_omaha_setsda((void*)base,1); + bit_omaha_setscl((void*)base,1); + + // Disable WP + tmp = __raw_readl(pgdat); + tmp = tmp & ~IIC_WP; + __raw_writel(tmp,pgdat); + + return 0; +} + +static void bit_omaha_exit(void) +{ + release_region(base , 2); +} + +static void i2c_omaha_exit(void) +{ + + i2c_bit_del_bus(&bit_omaha_ops); + + bit_omaha_exit(); + +} + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("ARM Limited "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Omaha"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(base, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(clock, "i"); +MODULE_PARM(own, "i"); +MODULE_PARM(mmapped, "i"); +MODULE_PARM(i2c_debug, "i"); + + +module_init(i2c_omaha_init); +module_exit(i2c_omaha_exit); + + --- linux-2.4.27/drivers/i2c/i2c-philips-par.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/i2c-philips-par.c @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ /* With some changes from Kyösti Mälkki and even Frodo Looijaard */ @@ -72,7 +72,7 @@ static void bit_lp_setscl(void *data, int state) { - /*be cautious about state of the control register - + /*be cautious about state of the control register - touch only the one bit needed*/ if (state) { parport_write_control((struct parport *) data, @@ -126,7 +126,7 @@ static int bit_lp_getsda2(void *data) { - return (parport_read_status((struct parport *) data) & + return (parport_read_status((struct parport *) data) & PARPORT_STATUS_BUSY) ? 0 : 1; } @@ -154,7 +154,7 @@ * Encapsulate the above functions in the correct operations structure. * This is only done when more than one hardware adapter is supported. */ - + static struct i2c_algo_bit_data bit_lp_data = { NULL, bit_lp_setsda, @@ -162,7 +162,7 @@ bit_lp_getsda, bit_lp_getscl, 80, 80, 100, /* waits, timeout */ -}; +}; static struct i2c_algo_bit_data bit_lp_data2 = { NULL, @@ -171,7 +171,7 @@ bit_lp_getsda2, NULL, 80, 80, 100, /* waits, timeout */ -}; +}; static struct i2c_adapter bit_lp_ops = { "Philips Parallel port adapter", @@ -197,7 +197,7 @@ printk(KERN_DEBUG "i2c-philips-par.o: attaching to %s\n", port->name); adapter->pdev = parport_register_device(port, "i2c-philips-par", - NULL, NULL, NULL, + NULL, NULL, NULL, PARPORT_FLAG_EXCL, NULL); if (!adapter->pdev) { @@ -257,16 +257,16 @@ NULL }; -int __init i2c_bitlp_init(void) +static int __init i2c_bitlp_init(void) { printk(KERN_INFO "i2c-philips-par.o: i2c Philips parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); parport_register_driver(&i2c_driver); - + return 0; } -void __exit i2c_bitlp_exit(void) +static void __exit i2c_bitlp_exit(void) { parport_unregister_driver(&i2c_driver); } @@ -279,14 +279,5 @@ MODULE_PARM(type, "i"); -#ifdef MODULE -int init_module(void) -{ - return i2c_bitlp_init(); -} - -void cleanup_module(void) -{ - i2c_bitlp_exit(); -} -#endif +module_init(i2c_bitlp_init); +module_exit(i2c_bitlp_exit); --- linux-2.4.27/drivers/i2c/i2c-velleman.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/i2c/i2c-velleman.c @@ -65,7 +65,7 @@ } else { outb(inb(CTRL) | I2C_SCL, CTRL); } - + } static void bit_velle_setsda(void *data, int state) @@ -75,8 +75,8 @@ } else { outb(inb(CTRL) | I2C_SDA, CTRL); } - -} + +} static int bit_velle_getscl(void *data) { @@ -95,7 +95,7 @@ base)); return -ENODEV; } else { - request_region(base, (base == 0x3bc)? 3 : 8, + request_region(base, (base == 0x3bc)? 3 : 8, "i2c (Vellemann adapter)"); bit_velle_setsda((void*)base,1); bit_velle_setscl((void*)base,1); @@ -104,7 +104,7 @@ } static void __exit bit_velle_exit(void) -{ +{ release_region( base , (base == 0x3bc)? 3 : 8 ); } @@ -121,16 +121,12 @@ static void bit_velle_inc_use(struct i2c_adapter *adap) { -#ifdef MODULE MOD_INC_USE_COUNT; -#endif } static void bit_velle_dec_use(struct i2c_adapter *adap) { -#ifdef MODULE MOD_DEC_USE_COUNT; -#endif } /* ------------------------------------------------------------------------ @@ -158,7 +154,7 @@ bit_velle_unreg, }; -int __init i2c_bitvelle_init(void) +static int __init i2c_bitvelle_init(void) { printk(KERN_INFO "i2c-velleman.o: i2c Velleman K8000 adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); if (base==0) { @@ -184,24 +180,19 @@ return 0; } +static void __exit i2c_bitvelle_exit(void) +{ + i2c_bit_del_bus(&bit_velle_ops); + bit_velle_exit(); +} + EXPORT_NO_SYMBOLS; -#ifdef MODULE MODULE_AUTHOR("Simon G. Vogl "); MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter"); MODULE_LICENSE("GPL"); MODULE_PARM(base, "i"); -int init_module(void) -{ - return i2c_bitvelle_init(); -} - -void cleanup_module(void) -{ - i2c_bit_del_bus(&bit_velle_ops); - bit_velle_exit(); -} - -#endif +module_init(i2c_bitvelle_init); +module_exit(i2c_bitvelle_exit); --- linux-2.4.27/drivers/ide/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/ide/Config.in @@ -107,6 +107,9 @@ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS dep_bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN fi + if [ "$CONFIG_ARCH_RISCSTATION" = "y" ]; then + dep_bool ' RiscStation IDE' CONFIG_BLK_DEV_IDE_RISCSTATION $CONFIG_ARCH_RISCSTATION + fi if [ "$CONFIG_AMIGA" = "y" ]; then dep_bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA dep_mbool ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL --- linux-2.4.27/drivers/ide/arm/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/ide/arm/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o +obj-$(CONFIG_BLK_DEV_IDE_RISCSTATION) += rstation-ide.o EXTRA_CFLAGS := -I../ --- linux-2.4.27/drivers/ide/arm/icside.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/ide/arm/icside.c @@ -1,7 +1,7 @@ /* * linux/drivers/ide/arm/icside.c * - * Copyright (c) 1996,1997 Russell King. + * Copyright (c) 1996-2003 Russell King. * * Changelog: * 08-Jun-1996 RMK Created @@ -26,24 +26,6 @@ #include #include -#include "ide-noise.h" - -/* - * FIXME: We want to drop the the MACRO CRAP! - * - * ec->iops->in{b/w/l} - * ec->iops->in{b/w/l}_p - * ec->iops->out{b/w/l} - * ec->iops->out{b/w/l}_p - * - * the new core supports clean MMIO calls and other goodies - */ - -/* - * Maximum number of interfaces per card - */ -#define MAX_IFS 2 - #define ICS_IDENT_OFFSET 0x8a0 #define ICS_ARCIN_V5_INTRSTAT 0x000 @@ -86,17 +68,20 @@ ICS_ARCIN_V6_IDESTEPPING }; -static const card_ids icside_cids[] = { - { MANU_ICS, PROD_ICS_IDE }, - { MANU_ICS2, PROD_ICS2_IDE }, - { 0xffff, 0xffff } +struct icside_state { + unsigned int channel; + unsigned int enabled; + unsigned long irq_port; + unsigned long slot_port; + unsigned int type; + ide_hwif_t *hwif[2]; }; -typedef enum { - ics_if_unknown, - ics_if_arcin_v5, - ics_if_arcin_v6 -} iftype_t; +#define ICS_TYPE_A3IN 0 +#define ICS_TYPE_A3USER 1 +#define ICS_TYPE_V6 3 +#define ICS_TYPE_V5 15 +#define ICS_TYPE_NOTYPE ((unsigned int)-1) /* ---------------- Version 5 PCB Support Functions --------------------- */ /* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) @@ -104,8 +89,10 @@ */ static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) { - unsigned int memc_port = (unsigned int)ec->irq_data; - outb(0, memc_port + ICS_ARCIN_V5_INTROFFSET); + struct icside_state *state = ec->irq_data; + unsigned int base = state->irq_port; + + outb(0, base + ICS_ARCIN_V5_INTROFFSET); } /* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) @@ -113,17 +100,15 @@ */ static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) { - unsigned int memc_port = (unsigned int)ec->irq_data; - inb(memc_port + ICS_ARCIN_V5_INTROFFSET); + struct icside_state *state = ec->irq_data; + unsigned int base = state->irq_port; + + inb(base + ICS_ARCIN_V5_INTROFFSET); } static const expansioncard_ops_t icside_ops_arcin_v5 = { - icside_irqenable_arcin_v5, - icside_irqdisable_arcin_v5, - NULL, - NULL, - NULL, - NULL + .irqenable = icside_irqenable_arcin_v5, + .irqdisable = icside_irqdisable_arcin_v5, }; @@ -133,10 +118,21 @@ */ static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) { - unsigned int ide_base_port = (unsigned int)ec->irq_data; + struct icside_state *state = ec->irq_data; + unsigned int base = state->irq_port; - outb(0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); - outb(0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); + state->enabled = 1; + + 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; - inb(ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); - inb(ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); + state->enabled = 0; + + inb (state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + inb (state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); } /* Prototype: icside_irqprobe(struct expansion_card *ec) @@ -155,70 +153,49 @@ */ static int icside_irqpending_arcin_v6(struct expansion_card *ec) { - unsigned int ide_base_port = (unsigned int)ec->irq_data; + struct icside_state *state = ec->irq_data; - return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || - inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; + return inb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || + inb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; } static const expansioncard_ops_t icside_ops_arcin_v6 = { - icside_irqenable_arcin_v6, - icside_irqdisable_arcin_v6, - icside_irqpending_arcin_v6, - NULL, - NULL, - NULL + .irqenable = icside_irqenable_arcin_v6, + .irqdisable = icside_irqdisable_arcin_v6, + .irqpending = icside_irqpending_arcin_v6, }; -/* Prototype: icside_identifyif (struct expansion_card *ec) - * Purpose : identify IDE interface type - * Notes : checks the description string +/* + * Handle routing of interrupts. This is called before + * we write the command to the drive. */ -static iftype_t __init icside_identifyif (struct expansion_card *ec) +static void icside_maskproc(ide_drive_t *drive, int mask) { - unsigned int addr; - iftype_t iftype; - int id = 0; - - iftype = ics_if_unknown; - - addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; - - id = inb(addr) & 1; - id |= (inb(addr + 1) & 1) << 1; - id |= (inb(addr + 2) & 1) << 2; - id |= (inb(addr + 3) & 1) << 3; - - switch (id) { - case 0: /* A3IN */ - printk("icside: A3IN unsupported\n"); - break; - - case 1: /* A3USER */ - printk("icside: A3USER unsupported\n"); - break; + ide_hwif_t *hwif = HWIF(drive); + struct icside_state *state = hwif->hwif_data; + unsigned long flags; - case 3: /* ARCIN V6 */ - printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v6; - break; + local_irq_save(flags); - case 15:/* ARCIN V5 (no id) */ - printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v5; - break; + state->channel = hwif->channel; - default:/* we don't know - complain very loudly */ - printk("icside: ***********************************\n"); - printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); - printk("icside: ***********************************\n"); - printk("icside: please report this to linux@arm.linux.org.uk\n"); - printk("icside: defaulting to ARCIN V5\n"); - iftype = ics_if_arcin_v5; - break; + if (state->enabled && !mask) { + switch (hwif->channel) { + case 0: + outb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + break; + case 1: + outb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + break; + } + } else { + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); } - return iftype; + local_irq_restore(flags); } #ifdef CONFIG_BLK_DEV_IDEDMA_ICS @@ -234,125 +211,138 @@ #define NR_ENTRIES 256 #define TABLE_SIZE (NR_ENTRIES * 8) -static int ide_build_sglist(ide_hwif_t *hwif, struct request *rq) +static void ide_build_sglist(ide_drive_t *drive, struct request *rq) { - struct buffer_head *bh; + ide_hwif_t *hwif = HWIF(drive); struct scatterlist *sg = hwif->sg_table; + struct buffer_head *bh; int nents = 0; - if (rq->cmd == READ) - hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; - else - hwif->sg_dma_direction = PCI_DMA_TODEVICE; - bh = rq->bh; - do { - unsigned char *virt_addr = bh->b_data; - unsigned int size = bh->b_size; + BUG_ON(hwif->sg_dma_active); - while ((bh = bh->b_reqnext) != NULL) { - if ((virt_addr + size) != (unsigned char *)bh->b_data) - break; - size += bh->b_size; - } - memset(&sg[nents], 0, sizeof(*sg)); - sg[nents].address = virt_addr; - sg[nents].length = size; - nents++; - } while (bh != NULL); + if (rq->cmd == IDE_DRIVE_TASKFILE) { + ide_task_t *args = rq->special; - return pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction); -} + if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) + hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + else + hwif->sg_dma_direction = PCI_DMA_TODEVICE; -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); -} + 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; -/* 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; + bh = rq->bh; + do { + unsigned long lastend; - pci_unmap_sg(NULL, sg, nents, HWIF(drive)->sg_dma_direction); + memset(sg, 0, sizeof(*sg)); + sg->page = bh->b_page; + lastend = bh_phys(bh); + + do { + lastend += bh->b_size; + sg->length += bh->b_size; + + bh = bh->b_reqnext; + if (bh == NULL) + break; + } while (lastend == bh_phys(bh)); + + sg++; + nents++; + } while (bh != NULL); + } + + nents = pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction); + + hwif->sg_nents = nents; } -static int -icside_config_if(ide_drive_t *drive, int xfer_mode) + +/* + * Configure the IOMD to give the appropriate timings for the transfer + * mode being requested. We take the advice of the ATA standards, and + * calculate the cycle time based on the transfer mode, and the EIDE + * MW DMA specs that the drive provides in the IDENTIFY command. + * + * We have the following IOMD DMA modes to choose from: + * + * Type Active Recovery Cycle + * A 250 (250) 312 (550) 562 (800) + * B 187 250 437 + * C 125 (125) 125 (375) 250 (500) + * D 62 125 187 + * + * (figures in brackets are actual measured timings) + * + * However, we also need to take care of the read/write active and + * recovery timings: + * + * Read Write + * Mode Active -- Recovery -- Cycle IOMD type + * MW0 215 50 215 480 A + * MW1 80 50 50 150 C + * MW2 70 25 25 120 C + */ +static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode) { - int func = ide_dma_off; + int on = 0, cycle_time = 0, use_dma_info = 0; + + /* + * Limit the transfer speed to MW_DMA_2. + */ + if (xfer_mode > XFER_MW_DMA_2) + xfer_mode = XFER_MW_DMA_2; switch (xfer_mode) { case XFER_MW_DMA_2: - /* - * The cycle time is limited to 250ns by the r/w - * pulse width (90ns), however we should still - * have a maximum burst transfer rate of 8MB/s. - */ - drive->drive_data = 250; + cycle_time = 250; + use_dma_info = 1; break; case XFER_MW_DMA_1: - drive->drive_data = 250; + cycle_time = 250; + use_dma_info = 1; break; case XFER_MW_DMA_0: - drive->drive_data = 480; + cycle_time = 480; break; - default: - drive->drive_data = 0; + case XFER_SW_DMA_2: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + cycle_time = 480; break; } - if (!drive->init_speed) - drive->init_speed = (u8) xfer_mode; + /* + * If we're going to be doing MW_DMA_1 or MW_DMA_2, we should + * take care to note the values in the ID... + */ + if (use_dma_info && drive->id->eide_dma_time > cycle_time) + cycle_time = drive->id->eide_dma_time; - if (drive->drive_data && - ide_config_drive_speed(drive, (u8) xfer_mode) == 0) - func = ide_dma_on; + drive->drive_data = cycle_time; + + 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); } -int icside_dma_check(ide_drive_t *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); +} + +static int icside_dma_check(ide_drive_t *drive) { struct hd_driveid *id = drive->id; ide_hwif_t *hwif = HWIF(drive); - int autodma = hwif->autodma; int xfer_mode = XFER_PIO_2; + int on; - if (!id || !(id->capability & 1) || !autodma) - return hwif->ide_dma_off_quietly(drive); + if (!id || !(id->capability & 1) || !hwif->autodma) + goto out; /* * Consult the list of known "bad" drives */ - if (check_drive_bad_lists(drive)) - return hwif->ide_dma_off(drive); + if (in_drive_list(id, drive_blacklist)) { + printk("%s: Disabling DMA for %s (blacklisted)\n", + drive->name, id->model); + goto out; + } /* * Enable DMA on any drive that has multiword DMA @@ -473,192 +475,241 @@ /* * Consult the list of known "good" drives */ - if (check_drive_good_lists(drive)) { + if (in_drive_list(id, drive_whitelist)) { if (id->eide_dma_time > 150) goto out; xfer_mode = XFER_MW_DMA_1; } out: - if (icside_config_if(drive, xfer_mode)) - return hwif->ide_dma_on(drive); - return hwif->ide_dma_off(drive); -} + on = icside_set_speed(drive, xfer_mode); -int icside_dma_verbose(ide_drive_t *drive) -{ - printk(", DMA"); - return 1; + if (on) + return icside_dma_on(drive); + else + return icside_dma_off(drive); } -int icside_dma_test_irq(ide_drive_t *drive) +static int icside_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - return inb((unsigned long)hwif->hw.priv) & 1; -} -int icside_dma_host_off(ide_drive_t *drive) -{ - return 0; -} + drive->waiting_for_dma = 0; -int icside_dma_off_quietly(ide_drive_t *drive) -{ - drive->using_dma = 0; - return icside_dma_host_off(drive); -} + disable_dma(hwif->hw.dma); -int icside_dma_off(ide_drive_t *drive) -{ - printk("%s: DMA disabled\n", drive->name); - return icside_dma_off_quietly(drive); -} + /* Teardown mappings after DMA has completed. */ + pci_unmap_sg(NULL, hwif->sg_table, hwif->sg_nents, + hwif->sg_dma_direction); -int icside_dma_host_on(ide_drive_t *drive) -{ - return 0; -} + hwif->sg_dma_active = 0; -int icside_dma_on(ide_drive_t *drive) -{ - drive->using_dma = 1; - return icside_dma_host_on(drive); + return get_dma_residue(hwif->hw.dma) != 0; } -int icside_dma_begin(ide_drive_t *drive) +static int icside_dma_begin(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + /* We can not enable DMA on both channels simultaneously. */ + BUG_ON(dma_channel_active(hwif->hw.dma)); enable_dma(hwif->hw.dma); return 0; } -int icside_dma_end(ide_drive_t *drive) +static int icside_dma_count(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - - drive->waiting_for_dma = 0; - disable_dma(hwif->hw.dma); - icside_destroy_dmatable(drive); - return get_dma_residue(hwif->hw.dma) != 0; + return icside_dma_begin(drive); } -int icside_dma_count (ide_drive_t *drive) +/* + * dma_intr() is the handler for disk read/write DMA interrupts + */ +static ide_startstop_t icside_dmaintr(ide_drive_t *drive) { - return icside_dma_begin(drive); + unsigned int stat; + int dma_stat; + + dma_stat = icside_dma_end(drive); + stat = HWIF(drive)->INB(IDE_STATUS_REG); + if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | DRQ_STAT)) { + if (!dma_stat) { + struct request *rq = HWGROUP(drive)->rq; + int i; + + for (i = rq->nr_sectors; i > 0; ) { + i -= rq->current_nr_sectors; + DRIVER(drive)->end_request(drive, 1); + } + + return ide_stopped; + } + printk(KERN_ERR "%s: bad DMA status (dma_stat=%x)\n", + drive->name, dma_stat); + } + + return DRIVER(drive)->error(drive, __FUNCTION__, stat); } -int icside_dma_read(ide_drive_t *drive) +static int +icside_dma_common(ide_drive_t *drive, struct request *rq, + unsigned int dma_mode) { - ide_hwif_t *hwif = HWIF(drive); -// ide_task_t *args = HWGROUP(drive)->rq->special; - int count = 0; - u8 lba48 = (drive->addressing == 1) ? 1 : 0; - task_ioreg_t command = WIN_NOP; + ide_hwif_t *hwif = HWIF(drive); - count = icside_build_dmatable(drive, PCI_DMA_FROMDEVICE); - if (!count) - return 1; - disable_dma(hwif->hw.dma); + /* + * We can not enable DMA on both channels. + */ + BUG_ON(hwif->sg_dma_active); + BUG_ON(dma_channel_active(hwif->hw.dma)); - /* 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,40 +717,53 @@ if (!hwif->sg_table) goto failed; - hwif->dmatable_cpu = NULL; - hwif->dmatable_dma = 0; - hwif->speedproc = icside_set_speed; - hwif->autodma = autodma; + hwif->atapi_dma = 1; + hwif->mwdma_mask = 7; /* MW0..2 */ + hwif->swdma_mask = 7; /* SW0..2 */ - hwif->ide_dma_check = icside_dma_check; - hwif->ide_dma_host_off = icside_dma_host_off; + hwif->dmatable_cpu = NULL; + hwif->dmatable_dma = 0; + hwif->speedproc = icside_set_speed; + hwif->autodma = autodma; + + hwif->ide_dma_check = icside_dma_check; + hwif->ide_dma_host_off = icside_dma_host_off; hwif->ide_dma_off_quietly = icside_dma_off_quietly; - hwif->ide_dma_off = icside_dma_off; - hwif->ide_dma_host_on = icside_dma_host_on; - hwif->ide_dma_on = icside_dma_on; - hwif->ide_dma_read = icside_dma_read; - hwif->ide_dma_write = icside_dma_write; - hwif->ide_dma_count = icside_dma_count; - hwif->ide_dma_begin = icside_dma_begin; - hwif->ide_dma_end = icside_dma_end; - hwif->ide_dma_verbose = icside_dma_verbose; - hwif->ide_dma_bad_drive = check_drive_bad_lists; - hwif->ide_dma_good_drive = check_drive_good_lists; - hwif->ide_dma_test_irq = icside_dma_test_irq; + hwif->ide_dma_off = icside_dma_off; + hwif->ide_dma_host_on = icside_dma_host_on; + hwif->ide_dma_on = icside_dma_on; + hwif->ide_dma_read = icside_dma_read; + hwif->ide_dma_write = icside_dma_write; + hwif->ide_dma_count = icside_dma_count; + hwif->ide_dma_begin = icside_dma_begin; + hwif->ide_dma_end = icside_dma_end; + hwif->ide_dma_test_irq = icside_dma_test_irq; + hwif->ide_dma_verbose = icside_dma_verbose; + hwif->ide_dma_timeout = icside_dma_timeout; + hwif->ide_dma_lostirq = icside_dma_lostirq; - printk(" capable%s\n", autodma ? - ", auto-enable" : ""); + printk(" capable%s\n", hwif->autodma ? ", auto-enable" : ""); return 1; failed: - printk(" -- ERROR, unable to allocate DMA table\n"); + printk(" disabled, unable to allocate DMA table\n"); return 0; } + +static void icside_dma_exit(ide_hwif_t *hwif) +{ + if (hwif->sg_table) { + kfree(hwif->sg_table); + hwif->sg_table = NULL; + } +} +#else +#define icside_dma_init(hwif) (0) +#define icside_dma_exit(hwif) do { } while (0) #endif -static ide_hwif_t * -icside_find_hwif(unsigned long dataport) +static ide_hwif_t *icside_find_hwif(unsigned long dataport) { ide_hwif_t *hwif; int index; @@ -716,13 +780,13 @@ goto found; } - return NULL; + hwif = NULL; found: return hwif; } static ide_hwif_t * -icside_setup(unsigned long base, struct cardinfo *info, int irq) +icside_setup(unsigned long base, struct cardinfo *info, struct expansion_card *ec) { unsigned long port = base + info->dataoffset; ide_hwif_t *hwif; @@ -740,8 +804,8 @@ } hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; hwif->io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; - hwif->hw.irq = irq; - hwif->irq = irq; + hwif->hw.irq = ec->irq; + hwif->irq = ec->irq; hwif->hw.dma = NO_DMA; hwif->noprobe = 0; hwif->chipset = ide_acorn; @@ -750,33 +814,39 @@ return hwif; } -static int __init icside_register_v5(struct expansion_card *ec, int autodma) +static int __init +icside_register_v5(struct icside_state *state, struct expansion_card *ec) { unsigned long slot_port; ide_hwif_t *hwif; slot_port = ecard_address(ec, ECARD_MEMC, 0); + state->irq_port = slot_port; + ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT); ec->irqmask = 1; - ec->irq_data = (void *)slot_port; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; + ec->irq_data = state; + ec->ops = &icside_ops_arcin_v5; /* * Be on the safe side - disable interrupts */ inb(slot_port + ICS_ARCIN_V5_INTROFFSET); - hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq); + hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec); - return hwif ? 0 : -1; + state->hwif[0] = hwif; + + return hwif ? 0 : -ENODEV; } -static int __init icside_register_v6(struct expansion_card *ec, int autodma) +static int __init +icside_register_v6(struct icside_state *state, struct expansion_card *ec) { unsigned long slot_port, port; ide_hwif_t *hwif, *mate; - int sel = 0; + unsigned int sel = 0; slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST); port = ecard_address(ec, ECARD_EASI, ECARD_FAST); @@ -788,88 +858,185 @@ outb(sel, slot_port); - ec->irq_data = (void *)port; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; - /* * Be on the safe side - disable interrupts */ inb(port + ICS_ARCIN_V6_INTROFFSET_1); inb(port + ICS_ARCIN_V6_INTROFFSET_2); - hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq); - mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq); + /* + * Find and register the interfaces. + */ + hwif = icside_setup(port, &icside_cardinfo_v6_1, ec); + mate = icside_setup(port, &icside_cardinfo_v6_2, ec); -#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) { - hwif->config_data = slot_port; - hwif->select_data = sel; - hwif->hw.dma = ec->dma; - hwif->hw.priv = (void *) - (port + ICS_ARCIN_V6_INTRSTAT_1); - hwif->channel = 0; - icside_setup_dma(hwif, autodma); - hwif->drives[0].autodma = autodma; - hwif->drives[1].autodma = autodma; - } - if (mate) { - mate->config_data = slot_port; - mate->select_data = sel | 1; - mate->hw.dma = ec->dma; - mate->hw.priv = (void *) - (port + ICS_ARCIN_V6_INTRSTAT_2); - mate->channel = 1; - icside_setup_dma(mate, autodma); - mate->drives[0].autodma = autodma; - mate->drives[1].autodma = autodma; - } + state->irq_port = port; + state->slot_port = slot_port; + state->hwif[0] = hwif; + state->hwif[1] = mate; + + ec->irq_data = state; + ec->ops = &icside_ops_arcin_v6; + + hwif->maskproc = icside_maskproc; + hwif->channel = 0; + hwif->hwif_data = state; + hwif->mate = mate; + hwif->serialized = 1; + hwif->config_data = slot_port; + hwif->select_data = sel; + hwif->hw.dma = ec->dma; + + mate->maskproc = icside_maskproc; + mate->channel = 1; + mate->hwif_data = state; + mate->mate = hwif; + mate->serialized = 1; + mate->config_data = slot_port; + mate->select_data = sel | 1; + mate->hw.dma = ec->dma; + + if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) { + icside_dma_init(hwif); + icside_dma_init(mate); } -no_dma: -#endif - return hwif || mate ? 0 : -1; + return 0; } -int __init icside_init(void) +static int __init icside_probe(struct expansion_card *ec, const struct ecard_id *id) { - int autodma = 0; + struct icside_state *state; + int ret; -#ifdef CONFIG_IDEDMA_ICS_AUTO - autodma = 1; -#endif + state = kmalloc(sizeof(struct icside_state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto out; + } - ecard_startfind (); + memset(state, 0, sizeof(struct icside_state)); + state->type = ICS_TYPE_NOTYPE; - do { - struct expansion_card *ec; - int result; + { + unsigned int addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; + unsigned int type; - ec = ecard_find(0, icside_cids); - if (ec == NULL) - break; + type = inb(addr) & 1; + type |= (inb(addr + 1) & 1) << 1; + type |= (inb(addr + 2) & 1) << 2; + type |= (inb(addr + 3) & 1) << 3; - ecard_claim(ec); + state->type = type; + } - switch (icside_identifyif(ec)) { - case ics_if_arcin_v5: - result = icside_register_v5(ec, autodma); - break; + switch (state->type) { + case ICS_TYPE_A3IN: + printk(KERN_WARNING "icside: A3IN unsupported\n"); + ret = -ENODEV; + break; - case ics_if_arcin_v6: - result = icside_register_v6(ec, autodma); - break; + case ICS_TYPE_A3USER: + printk(KERN_WARNING "icside: A3USER unsupported\n"); + ret = -ENODEV; + break; - default: - result = -1; - break; - } + case ICS_TYPE_V5: + ret = icside_register_v5(state, ec); + break; - if (result) - ecard_release(ec); - } while (1); + case ICS_TYPE_V6: + ret = icside_register_v6(state, ec); + break; - return 0; + default: + printk(KERN_WARNING "icside: unknown interface type\n"); + ret = -ENODEV; + break; + } + + if (ret == 0) { + ecard_set_drvdata(ec, state); + } else { + kfree(state); + } + out: + return ret; +} + +static void __devexit icside_remove(struct expansion_card *ec) +{ + struct icside_state *state = ecard_get_drvdata(ec); + + switch (state->type) { + case ICS_TYPE_V5: + /* FIXME: tell IDE to stop using the interface */ + + /* Disable interrupts */ + inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET); + break; + + case ICS_TYPE_V6: + /* FIXME: tell IDE to stop using the interface */ + icside_dma_exit(state->hwif[1]); + icside_dma_exit(state->hwif[0]); + + if (ec->dma != NO_DMA) + free_dma(ec->dma); + + /* Disable interrupts */ + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + + /* Reset the ROM pointer/EASI selection */ + outb(0, state->slot_port); + break; + } + + ecard_set_drvdata(ec, NULL); + ec->ops = NULL; + ec->irq_data = NULL; + + kfree(state); +} + +static void icside_shutdown(struct expansion_card *ec) +{ + struct icside_state *state = ecard_get_drvdata(ec); + + switch (state->type) { + case ICS_TYPE_V5: + /* Disable interrupts */ + inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET); + break; + + case ICS_TYPE_V6: + /* Disable interrupts */ + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + + /* Reset the ROM pointer/EASI selection */ + outb(0, state->slot_port); + break; + } +} + +static const struct ecard_id icside_ids[] = { + { MANU_ICS, PROD_ICS_IDE }, + { MANU_ICS2, PROD_ICS2_IDE }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver icside_driver = { + .probe = icside_probe, + .remove = __devexit_p(icside_remove), + .shutdown = icside_shutdown, + .id_table = icside_ids, +}; + +int __init icside_init(void) +{ + return ecard_register_driver(&icside_driver); } --- /dev/null +++ linux-2.4.27/drivers/ide/arm/rstation-ide.c @@ -0,0 +1,78 @@ +/* + * linux/drivers/ide/rs-ide.c + * + * Copyright (c) 2002 Ben Dooks + * Copyright (c) 2002 Simtec Electronics + * + * Simple RiscStation IDE support +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef CONFIG_ARCH_RISCSTATION +#error "compiling this code for non-riscstation hardware is dangerous!" +#endif + +#define DRV_PREFIX "ide-rs" + +#define IRQ_PRI (40+3) +#define IRQ_SEC (40+4) + +#define PORT_BASE ((0x2b800 - 0x10000) >> 2) +#define SEC_OFF (0x400 >> 2) + +int __init rside_reg(unsigned long base, unsigned int irq); + +int __init rside_init(void) +{ + int iotcr; + + if (!machine_is_riscstation()) { + printk(DRV_PREFIX ": hardware is not a RiscStation!\n"); + return 0; + } + + /* select correct area cycle time */ + + iotcr = inb(IOMD_IOTCR); + outb((iotcr & ~3) | 1, IOMD_IOTCR); + + /* register h/w */ + + rside_reg(PORT_BASE, IRQ_PRI); + rside_reg(PORT_BASE + SEC_OFF, IRQ_SEC); + + return 0; +} + + +int __init rside_reg(unsigned long port, unsigned int irq) +{ + unsigned long addr, i; + hw_regs_t hw; + + hw.irq = irq; + + addr = port; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw.io_ports[i] = (ide_ioreg_t)addr; + addr += 0x40 >> 2; + } + + hw.io_ports[IDE_CONTROL_OFFSET] = port + ((0xb80 - 0x800) >> 2); + + printk(DRV_PREFIX ": registering channel at %08lx, %08lx, irq %d\n", + port, hw.io_ports[IDE_CONTROL_OFFSET], irq); + + return ide_register_hw(&hw, NULL); +} --- linux-2.4.27/drivers/ide/ide-probe.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/ide/ide-probe.c @@ -1297,11 +1297,11 @@ hwif->name, hwif->major); return (hwif->present = 0); } - + if (init_irq(hwif)) { int i = hwif->irq; /* - * It failed to initialise. Find the default IRQ for + * It failed to initialise. Find the default IRQ for * this port and try that. */ if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) { @@ -1319,7 +1319,7 @@ printk("%s: probed IRQ %d failed, using default.\n", hwif->name, hwif->irq); } - + init_gendisk(hwif); blk_dev[hwif->major].data = hwif; blk_dev[hwif->major].queue = ide_get_queue; --- linux-2.4.27/drivers/ide/ide-proc.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/ide/ide-proc.c @@ -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); --- linux-2.4.27/drivers/ide/ide.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/ide/ide.c @@ -218,23 +218,14 @@ static void init_hwif_data (unsigned int index) { unsigned int unit; - hw_regs_t hw; ide_hwif_t *hwif = &ide_hwifs[index]; /* bulk initialize hwif & drive info with zeros */ memset(hwif, 0, sizeof(ide_hwif_t)); - memset(&hw, 0, sizeof(hw_regs_t)); /* fill in any non-zero initial values */ hwif->index = index; - ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &hwif->irq); - memcpy(&hwif->hw, &hw, sizeof(hw)); - memcpy(hwif->io_ports, hw.io_ports, sizeof(hw.io_ports)); - hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; -#ifdef CONFIG_BLK_DEV_HD - if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) - hwif->noprobe = 1; /* may be overridden by ide_setup() */ -#endif /* CONFIG_BLK_DEV_HD */ + hwif->noprobe = 1; hwif->major = ide_hwif_to_major[index]; hwif->name[0] = 'i'; hwif->name[1] = 'd'; @@ -276,6 +267,28 @@ } /* + * Old compatability function - initialise ports using ide_default_io_base + */ +static void ide_old_init_default_hwifs(void) +{ + unsigned int index; + ide_ioreg_t base; + ide_hwif_t *hwif; + + for (index = 0; index < MAX_HWIFS; index++) { + hwif = &ide_hwifs[index]; + + base = ide_default_io_base(index); + + if (base) { + ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->hw.irq); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports)); + hwif->noprobe = 0; + } + } +} + +/* * init_ide_data() sets reasonable default values into all fields * of all instances of the hwifs and drives, but only on the first call. * Subsequent calls have no effect (they don't wipe out anything). @@ -307,6 +320,7 @@ init_hwif_data(index); /* Add default hw interfaces */ + ide_old_init_default_hwifs(); ide_init_default_hwifs(); idebus_parameter = 0; @@ -2530,6 +2544,12 @@ rapide_init(); } #endif /* CONFIG_BLK_DEV_IDE_RAPIDE */ +#ifdef CONFIG_BLK_DEV_IDE_RISCSTATION + { + extern void rside_init(void); + rside_init(); + } +#endif /* CONFIG_BLK_DEV_IDE_RISCSTATION */ #ifdef CONFIG_BLK_DEV_GAYLE { extern void gayle_init(void); --- linux-2.4.27/drivers/ide/pci/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/ide/pci/Makefile @@ -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 --- linux-2.4.27/drivers/ide/pci/sl82c105.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/ide/pci/sl82c105.c @@ -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. --- /dev/null +++ linux-2.4.27/drivers/ide/pci/sl82c105.c.2419 @@ -0,0 +1,380 @@ +/* + * linux/drivers/ide/sl82c105.c + * + * SL82C105/Winbond 553 IDE driver + * + * Maintainer unknown. + * + * Changelog: + * + * 15/11/1998 RMK Drive tuning added from Rebel.com's kernel + * sources + * 30/03/2002 RMK Add fixes specified in W83C553F errata. + * (with special thanks to Todd Inglett) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +extern char *ide_xfer_verbose (byte xfer_rate); + +/* + * SL82C105 PCI config register 0x40 bits. + */ +#define CTRL_IDE_IRQB (1 << 30) +#define CTRL_IDE_IRQA (1 << 28) +#define CTRL_LEGIRQ (1 << 11) +#define CTRL_P1F16 (1 << 5) +#define CTRL_P1EN (1 << 4) +#define CTRL_P0F16 (1 << 1) +#define CTRL_P0EN (1 << 0) + +/* + * Convert a PIO mode and cycle time to the required on/off + * times for the interface. This has protection against run-away + * timings. + */ +static unsigned int get_timing_sl82c105(ide_pio_data_t *p) +{ + unsigned int cmd_on; + unsigned int cmd_off; + + cmd_on = (ide_pio_timings[p->pio_mode].active_time + 29) / 30; + cmd_off = (p->cycle_time - 30 * cmd_on + 29) / 30; + + if (cmd_on > 32) + cmd_on = 32; + if (cmd_on == 0) + cmd_on = 1; + + if (cmd_off > 32) + cmd_off = 32; + if (cmd_off == 0) + cmd_off = 1; + + return (cmd_on - 1) << 8 | (cmd_off - 1) | (p->use_iordy ? 0x40 : 0x00); +} + +/* + * Configure the drive and chipset for PIO + */ +static void config_for_pio(ide_drive_t *drive, int pio, int report) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + ide_pio_data_t p; + unsigned short drv_ctrl = 0x909; + unsigned int xfer_mode, reg; + + reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0); + + pio = ide_get_best_pio_mode(drive, pio, 5, &p); + + switch (pio) { + default: + case 0: xfer_mode = XFER_PIO_0; break; + case 1: xfer_mode = XFER_PIO_1; break; + case 2: xfer_mode = XFER_PIO_2; break; + case 3: xfer_mode = XFER_PIO_3; break; + case 4: xfer_mode = XFER_PIO_4; break; + } + + if (ide_config_drive_speed(drive, xfer_mode) == 0) + drv_ctrl = get_timing_sl82c105(&p); + + if (drive->using_dma == 0) { + /* + * If we are actually using MW DMA, then we can not + * reprogram the interface drive control register. + */ + pci_write_config_word(dev, reg, drv_ctrl); + pci_read_config_word(dev, reg, &drv_ctrl); + + if (report) { + printk("%s: selected %s (%dns) (%04X)\n", drive->name, + ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl); + } + } +} + +/* + * Configure the drive and the chipset for DMA + */ +static int config_for_dma(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + unsigned short drv_ctrl = 0x909; + unsigned int reg; + + reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0); + + if (ide_config_drive_speed(drive, XFER_MW_DMA_2) == 0) + drv_ctrl = 0x0240; + + pci_write_config_word(dev, reg, drv_ctrl); + + return 0; +} + + +/* + * Check to see if the drive and + * chipset is capable of DMA mode + */ +static int sl82c105_check_drive(ide_drive_t *drive) +{ + ide_dma_action_t dma_func = ide_dma_off_quietly; + + do { + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if (!hwif->autodma) + break; + + if (!id || !(id->capability & 1)) + break; + + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + break; + } + + if (id->field_valid & 2) { + if (id->dma_mword & 7 || id->dma_1word & 7) + dma_func = ide_dma_on; + break; + } + + if (ide_dmaproc(ide_dma_good_drive, drive)) { + dma_func = ide_dma_on; + break; + } + } while (0); + + return HWIF(drive)->dmaproc(dma_func, drive); +} + +/* + * The SL82C105 holds off all IDE interrupts while in DMA mode until + * all DMA activity is completed. Sometimes this causes problems (eg, + * when the drive wants to report an error condition). + * + * 0x7e is a "chip testing" register. Bit 2 resets the DMA controller + * state machine. We need to kick this to work around various bugs. + */ +static inline void sl82c105_reset_host(struct pci_dev *dev) +{ + u16 val; + + pci_read_config_word(dev, 0x7e, &val); + pci_write_config_word(dev, 0x7e, val | (1 << 2)); + pci_write_config_word(dev, 0x7e, val & ~(1 << 2)); +} + +/* + * If we get an IRQ timeout, it might be that the DMA state machine + * got confused. Fix from Todd Inglett. Details from Winbond. + * + * This function is called when the IDE timer expires, the drive + * indicates that it is READY, and we were waiting for DMA to complete. + */ +static int sl82c105_lostirq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA; + unsigned long dma_base = hwif->dma_base; + + printk("sl82c105: lost IRQ: resetting host\n"); + + /* + * Check the raw interrupt from the drive. + */ + pci_read_config_dword(dev, 0x40, &val); + if (val & mask) + printk("sl82c105: drive was requesting IRQ, but host lost it\n"); + + /* + * Was DMA enabled? If so, disable it - we're resetting the + * host. The IDE layer will be handling the drive for us. + */ + val = inb(dma_base); + if (val & 1) { + outb(val & ~1, dma_base); + printk("sl82c105: DMA was enabled\n"); + } + + sl82c105_reset_host(dev); + + /* ide_dmaproc would return 1, so we do as well */ + return 1; +} + +/* + * ATAPI devices can cause the SL82C105 DMA state machine to go gaga. + * Winbond recommend that the DMA state machine is reset prior to + * setting the bus master DMA enable bit. + * + * The generic IDE core will have disabled the BMEN bit before this + * function is called. + */ +static void sl82c105_before_bm_enable(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + sl82c105_reset_host(dev); +} + +/* + * Our very own dmaproc. We need to intercept various calls + * to fix up the SL82C105 specific behaviour. + */ +static int sl82c105_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return sl82c105_check_drive(drive); + + case ide_dma_on: + if (config_for_dma(drive)) + func = ide_dma_off; + /* fall through */ + + case ide_dma_off_quietly: + case ide_dma_off: + config_for_pio(drive, 4, 0); + break; + + case ide_dma_read: + case ide_dma_write: + case ide_dma_begin: + case ide_dma_timeout: + sl82c105_before_bm_enable(drive); + break; + + case ide_dma_lostirq: + return sl82c105_lostirq(drive); + + default: + break; + } + return ide_dmaproc(func, drive); +} + +/* + * We only deal with PIO mode here - DMA mode 'using_dma' is not + * initialised at the point that this function is called. + */ +static void tune_sl82c105(ide_drive_t *drive, byte pio) +{ + config_for_pio(drive, pio, 1); + + /* + * We support 32-bit I/O on this interface, and it + * doesn't have problems with interrupts. + */ + drive->io_32bit = 1; + drive->unmask = 1; +} + +/* + * Return the revision of the Winbond bridge + * which this function is part of. + */ +static unsigned int sl82c105_bridge_revision(struct pci_dev *dev) +{ + struct pci_dev *bridge; + unsigned char rev; + + /* + * The bridge should be part of the same device, but function 0. + */ + bridge = pci_find_slot(dev->bus->number, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + if (!bridge) + return -1; + + /* + * Make sure it is a Winbond 553 and is an ISA bridge. + */ + if (bridge->vendor != PCI_VENDOR_ID_WINBOND || + bridge->device != PCI_DEVICE_ID_WINBOND_83C553 || + bridge->class >> 8 != PCI_CLASS_BRIDGE_ISA) + return -1; + + /* + * We need to find function 0's revision, not function 1 + */ + pci_read_config_byte(bridge, PCI_REVISION_ID, &rev); + + return rev; +} + +/* + * Enable the PCI device + */ +unsigned int __init pci_init_sl82c105(struct pci_dev *dev, const char *msg) +{ + u32 val; + + pci_read_config_dword(dev, 0x40, &val); + val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1EN | CTRL_P1F16; + pci_write_config_dword(dev, 0x40, val); + + return dev->irq; +} + +void __init dma_init_sl82c105(ide_hwif_t *hwif, unsigned long dma_base) +{ + unsigned int bridge_rev; + byte dma_state; + + dma_state = inb(dma_base + 2); + bridge_rev = sl82c105_bridge_revision(hwif->pci_dev); + if (bridge_rev <= 5) { + hwif->autodma = 0; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n", + hwif->name, bridge_rev); + dma_state &= ~0x60; + } else { + dma_state |= 0x60; + hwif->autodma = 1; + } + outb(dma_state, dma_base + 2); + + ide_setup_dma(hwif, dma_base, 8); + + if (bridge_rev <= 5) + hwif->dmaproc = NULL; + else + hwif->dmaproc = sl82c105_dmaproc; +} + +/* + * Initialise the chip + */ +void __init ide_init_sl82c105(ide_hwif_t *hwif) +{ + hwif->tuneproc = tune_sl82c105; +} + --- linux-2.4.27/drivers/input/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/input/Config.in @@ -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 --- linux-2.4.27/drivers/input/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/input/Makefile @@ -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. --- /dev/null +++ linux-2.4.27/drivers/input/mx1ts.c @@ -0,0 +1,508 @@ +/* + * linux/drivers/misc/mx1ts.c + * + * Copyright (C) 2003 Blue Mug, Inc. for Motorola, Inc. + * + * Cloned from ucb1x00_ts.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "mx1ts.h" + +#define DEV_IRQ_ID "mx1-ts" + +struct mx1_ts { + struct input_dev idev; +#ifdef CONFIG_PM + struct pm_dev *pmdev; +#endif + + wait_queue_head_t irq_wait; + struct completion init_exit; + int use_count; + u16 x_res; + u16 y_res; + + int restart:1; +}; + +static struct mx1_ts mx1ts; +static u8 mx1_performing_auto_calibration = 0; +static u16 mx1_cal_auto_zero = 0; +static u16 mx1_cal_range_x = 0; +static u16 mx1_cal_range_y = 0; + +static int mx1_ts_startup(struct mx1_ts *ts); +static void mx1_ts_shutdown(struct mx1_ts *ts); + +static void mx1_ts_pendata_int(int irq, void *dev_id, struct pt_regs *regs); +static void mx1_ts_touch_int(int irq, void *dev_id, struct pt_regs *regs); +static void mx1_ts_compare_int(int irq, void *dev_id, struct pt_regs *regs); + +static void mx1_ts_enable_pen_touch_interrupt(void); +static void mx1_ts_disable_pen_touch_interrupt(void); +static void mx1_ts_enable_pen_up_interrupt(void); +static void mx1_ts_disable_pen_up_interrupt(void); +static void mx1_ts_enable_auto_sample(void); +static void mx1_ts_disable_auto_sample(void); +static void mx1_ts_start_auto_calibration(void); + +static inline void mx1_reg_write(unsigned int reg, unsigned int val) +{ + *((volatile unsigned int *)reg) = val; +} + +static inline unsigned int mx1_reg_read(unsigned int reg) +{ + return *((volatile unsigned int *)reg); +} + +static inline void mx1_reg_clear_bit(unsigned int reg, unsigned int bit) +{ + *((volatile unsigned int *)reg) &= ~bit; +} + +static inline void mx1_reg_set_bit(unsigned int reg, unsigned int bit) +{ + *((volatile unsigned int *)reg) |= bit; +} + +static inline void mx1_ts_evt_add(struct mx1_ts *ts, u16 pressure, u16 x, u16 y) +{ + input_report_abs(&ts->idev, ABS_X, (int)x - 32768); + input_report_abs(&ts->idev, ABS_Y, (int)y - 32768); + input_report_abs(&ts->idev, ABS_PRESSURE, (int)pressure); +} + +static inline void mx1_ts_flush_fifo(void) +{ + int i; + for (i = 0; i < 12; i++) + if (mx1_reg_read(ASP_ISTATR) & (ASP_PFF | ASP_PDR)) + mx1_reg_read(ASP_PADFIFO); +} + +static int mx1_ts_open(struct input_dev *idev) +{ + struct mx1_ts *ts = (struct mx1_ts *)idev; + + mx1_performing_auto_calibration = 0; + return mx1_ts_startup(ts); +} + +static void mx1_ts_close(struct input_dev *idev) +{ + struct mx1_ts *ts = (struct mx1_ts *)idev; + + mx1_ts_shutdown(ts); +} + +static inline int mx1_ts_enable_irqs(void) +{ + int result; + + result = request_irq(ASP_PENDATA_IRQ, + mx1_ts_pendata_int, + SA_INTERRUPT, + DEV_IRQ_ID, + DEV_IRQ_ID); + if (result) { + printk("Couldn't request pen data IRQ.\n"); + return result; + } + + result = request_irq(ASP_TOUCH_IRQ, + mx1_ts_touch_int, + SA_INTERRUPT, + DEV_IRQ_ID, + DEV_IRQ_ID); + if (result) { + printk("Couldn't request pen touch IRQ.\n"); + free_irq(ASP_PENDATA_IRQ, DEV_IRQ_ID); + return result; + } + + return result; +} + +static inline int mx1_ts_disable_irqs(void) +{ + free_irq(ASP_PENDATA_IRQ, DEV_IRQ_ID); + free_irq(ASP_TOUCH_IRQ, DEV_IRQ_ID); + + return 0; +} + +static inline int mx1_ts_register(struct mx1_ts *ts) +{ + ts->idev.name = "Touchscreen panel"; + ts->idev.open = mx1_ts_open; + ts->idev.close = mx1_ts_close; + + __set_bit(EV_ABS, ts->idev.evbit); + __set_bit(ABS_X, ts->idev.absbit); + __set_bit(ABS_Y, ts->idev.absbit); + __set_bit(ABS_PRESSURE, ts->idev.absbit); + + ts->idev.absmin[ABS_X] = 0; + ts->idev.absmax[ABS_X] = (u32)0x0000FFFF; + ts->idev.absfuzz[ABS_X] = 50; + ts->idev.absflat[ABS_X] = 0; + + ts->idev.absmin[ABS_Y] = 0; + ts->idev.absmax[ABS_Y] = (u32)0x0000FFFF; + ts->idev.absfuzz[ABS_Y] = 50; + ts->idev.absflat[ABS_Y] = 0; + + input_register_device(&ts->idev); + + return 0; +} + +static inline void mx1_ts_deregister(struct mx1_ts *ts) +{ + input_unregister_device(&ts->idev); +} + +/* + * Handle the touch interrupt, generated when the pen is pressed/ + * released. + */ +static void mx1_ts_touch_int(int irq, void *dev_id, struct pt_regs *regs) +{ + /* Clear the interrupt. */ + mx1_reg_set_bit(ASP_ISTATR, ASP_PEN); + + mx1_ts_disable_pen_touch_interrupt(); + mx1_ts_start_auto_calibration(); + mx1_ts_enable_pen_up_interrupt(); +} + +/* + * Handle the pen data ready interrupt, generated when pen data is + * in the FIFO. + */ +static void mx1_ts_pendata_int(int irq, void *dev_id, struct pt_regs *regs) +{ + static unsigned int auto_zero, pen_x, pen_y, pen_u; + + if (mx1_reg_read(ASP_ISTATR) & 0x400) { + mx1_reg_set_bit(ASP_ISTATR, 0x400); + + mx1_ts_disable_auto_sample(); + mx1_ts_disable_pen_up_interrupt(); + mx1_ts_enable_pen_touch_interrupt(); + + mx1_ts_evt_add(&mx1ts, 0, pen_x, pen_y); + + mx1_ts_flush_fifo(); + + return; + } + + if (mx1_performing_auto_calibration) { + unsigned int value; + + mx1_cal_auto_zero = mx1_reg_read(ASP_PADFIFO) & 0xFFFF; + mx1_cal_range_x = mx1_reg_read(ASP_PADFIFO) & 0xFFFF; + mx1_cal_range_y = mx1_reg_read(ASP_PADFIFO) & 0xFFFF; + + if ((mx1_cal_auto_zero >= mx1_cal_range_x) || + (mx1_cal_auto_zero >= mx1_cal_range_y)) { + /* Invalid data. */ + mx1_ts_start_auto_calibration(); + return; + } + + mx1_cal_range_x -= mx1_cal_auto_zero; + mx1_cal_range_y -= mx1_cal_auto_zero; + + value = mx1_reg_read(ASP_ACNTLCR); + value &= ~0x04000000; /* XXX Undocumented. */ + mx1_reg_write(ASP_ACNTLCR, value); + + mx1_performing_auto_calibration = 0; + + mx1_ts_enable_auto_sample(); + } else { + /* There could be more than one sample in the FIFO, but we're + * only going to read one per call. The interrupt will be + * generated as long as there is data in the FIFO. */ + + if ((mx1_reg_read(ASP_ISTATR) & ASP_PDR) != ASP_PDR) { + return; + } + + auto_zero = mx1_reg_read(ASP_PADFIFO); + if (auto_zero > (mx1_cal_auto_zero + 0x200)) { + return; + } + + pen_x = mx1_reg_read(ASP_PADFIFO); + pen_y = mx1_reg_read(ASP_PADFIFO); + pen_u = mx1_reg_read(ASP_PADFIFO); + + pen_x = (u32)(((pen_x - mx1_cal_auto_zero) << 16) / + mx1_cal_range_x); + pen_y = (u32)(((pen_y - mx1_cal_auto_zero) << 16) / + mx1_cal_range_y); + + mx1_ts_evt_add(&mx1ts, pen_u, pen_x, pen_y); + } +} + +static void mx1_ts_reset_asp(void) +{ + unsigned int value; + + mx1_ts_flush_fifo(); + + /* Soft reset the ASP module */ + mx1_reg_write(ASP_ACNTLCR, ASP_SWRST); + + /* Read back the reset value of the control register */ + value = mx1_reg_read(ASP_ACNTLCR); + + /* Enable the clock and wait for a short while */ + value |= ASP_CLKEN; + mx1_reg_write(ASP_ACNTLCR, value); + udelay(100); + + /* Set the value of the conrtol register. */ + value = ASP_CLKEN | ASP_NM | ASP_SW6 | ASP_BGE; + mx1_reg_write(ASP_ACNTLCR, value); + + /* Set the clock divide ratio to 2. */ + mx1_reg_write(ASP_CLKDIV, 0x01); + + /* Set the sample rate control register. These values should yield + * about 150 samples per second, which seems to give good smooth + * lines. */ + value = (0x2 << ASP_DMCNT_SCALE) | /* Decimation ratio is 3 */ + (0x1 << ASP_IDLECNT_SCALE) | /* Idle count is 1 clock */ + (0x2 << ASP_DSCNT_SCALE); /* Data setup is 2 clocks */ + mx1_reg_write(ASP_PSMPLRG, value); + + /* Disable the compare function. */ + mx1_reg_write(ASP_CMPCNTL, 0); +} + +static void mx1_ts_enable_auto_sample(void) +{ + unsigned int value; + + mx1_ts_flush_fifo(); + + value = mx1_reg_read(ASP_ACNTLCR); + + /* Set the mode to X then Y */ + value &= ~ASP_MODE_MASK; + value |= ASP_MODE_ONLY_Y; + + /* Enable auto zero. */ + value |= ASP_AZE; + + /* Enable auto sample. */ + value |= ASP_AUTO; + + /* Enable pen A/D. */ + value |= ASP_PADE; + mx1_reg_write(ASP_ACNTLCR, value); + + /* Enable pen data ready and full interrupt. */ + value = mx1_reg_read(ASP_ICNTLR); + value |= ASP_PFFE | ASP_PDRE; + mx1_reg_write(ASP_ICNTLR, value); +} + +static void mx1_ts_disable_auto_sample(void) +{ + unsigned int value; + + value = mx1_reg_read(ASP_ACNTLCR); + + /* Set the mode to none */ + value &= ~ASP_MODE_MASK; + + /* Disable auto zero. */ + value &= ~ASP_AZE; + + /* Disable auto sample. */ + value &= ~ASP_AUTO; + + /* Disable pen A/D. */ + value &= ~ASP_PADE; + mx1_reg_write(ASP_ACNTLCR, value); + + /* Disable pen data ready and full interrupt. */ + value = mx1_reg_read(ASP_ICNTLR); + value &= ~(ASP_PFFE | ASP_PDRE); + mx1_reg_write(ASP_ICNTLR, value); +} + +static void mx1_ts_enable_pen_touch_interrupt(void) +{ + unsigned int value; + + /* Enable pen touch interrupt. */ + value = mx1_reg_read(ASP_ICNTLR); + value |= ASP_EDGE | ASP_PIRQE; + mx1_reg_write(ASP_ICNTLR, value); +} + +static void mx1_ts_disable_pen_touch_interrupt(void) +{ + unsigned int value; + + /* Enable pen touch interrupt. */ + value = mx1_reg_read(ASP_ICNTLR); + value &= ~ASP_PIRQE; + mx1_reg_write(ASP_ICNTLR, value); +} + +static void mx1_ts_enable_pen_up_interrupt(void) +{ + unsigned int value; + + /* Enable pen up interrupt. XXX: This feature is undocumented. */ + value = mx1_reg_read(ASP_ICNTLR); + value |= ASP_PUPE; + mx1_reg_write(ASP_ICNTLR, value); +} + +static void mx1_ts_disable_pen_up_interrupt(void) +{ + unsigned int value; + + /* Enable pen up interrupt. XXX: This feature is undocumented. */ + value = mx1_reg_read(ASP_ICNTLR); + value &= ~ASP_PUPE; + mx1_reg_write(ASP_ICNTLR, value); +} + +static void mx1_ts_start_auto_calibration(void) +{ + unsigned int value; + + mx1_performing_auto_calibration = 1; + + value = mx1_reg_read(ASP_ACNTLCR); + + /* Set the mode to X then Y */ + value &= ~ASP_MODE_MASK; + value |= ASP_MODE_ONLY_X; + + /* Enable auto zero. */ + value |= ASP_AZE; + + /* Enable auto calibrate. XXX: Undocumented bitfield. */ + value |= 0x04000000; + + /* Enable auto sample. */ + value |= ASP_AUTO; + + /* Enable pen A/D. */ + value |= ASP_PADE; + mx1_reg_write(ASP_ACNTLCR, value); + + /* Enable pen data ready and full interrupt. */ + value = mx1_reg_read(ASP_ICNTLR); + value |= ASP_PFFE | ASP_PDRE | ASP_PUPE; + mx1_reg_write(ASP_ICNTLR, value); +} + +static int mx1_ts_startup(struct mx1_ts *ts) +{ + int ret = 0; + + if (ts->use_count++ != 0) + goto out; + + /* + * Reset the ASP. + */ + mx1_ts_reset_asp(); + + + /* + * XXX: Figure out if we need this... + * If we do this at all, we should allow the user to + * measure and read the X and Y resistance at any time. + */ + //ts->x_res = mx1_ts_read_xres(ts); + //ts->y_res = mx1_ts_read_yres(ts); + + mx1_ts_enable_pen_touch_interrupt(); + + out: + if (ret) + ts->use_count--; + return ret; +} + +/* + * Release touchscreen resources. Disable IRQs. + */ +static void mx1_ts_shutdown(struct mx1_ts *ts) +{ + if (--ts->use_count == 0) { + unsigned int value; + + /* Turn off the ADC and associated circuitry. */ + value = mx1_reg_read(ASP_ACNTLCR); + value &= !(ASP_CLKEN | ASP_PADE | ASP_BGE); + mx1_reg_write(ASP_ACNTLCR, value); + } +} + +/* + * Initialization. + */ +static int __init mx1_ts_init(void) +{ + int ret = 0; + struct mx1_ts *ts = &mx1ts; + + mx1_ts_reset_asp(); + + /* + * Enable the IRQ's + */ + if ((ret = mx1_ts_enable_irqs())) + return ret; + + return mx1_ts_register(ts); +} + +static void __exit mx1_ts_exit(void) +{ + struct mx1_ts *ts = &mx1ts; + + mx1_ts_disable_irqs(); + mx1_ts_deregister(ts); +} + +module_init(mx1_ts_init); +module_exit(mx1_ts_exit); + +MODULE_AUTHOR("Jon McClintock "); +MODULE_DESCRIPTION("MX1 touchscreen driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/input/mx1ts.h @@ -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) --- /dev/null +++ linux-2.4.27/drivers/l3/Config.in @@ -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 --- /dev/null +++ linux-2.4.27/drivers/l3/Makefile @@ -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 + --- /dev/null +++ linux-2.4.27/drivers/l3/l3-algo-bit.c @@ -0,0 +1,175 @@ +/* + * L3 bus algorithm module. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Note that L3 buses can share the same pins as I2C buses, so we must + * _not_ generate an I2C start condition. An I2C start condition is + * defined as a high-to-low transition of the data line while the clock + * is high. Therefore, we must only change the data line while the + * clock is low. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define setdat(adap,val) adap->setdat(adap->data, val) +#define setclk(adap,val) adap->setclk(adap->data, val) +#define setmode(adap,val) adap->setmode(adap->data, val) +#define setdatin(adap) adap->setdir(adap->data, 1) +#define setdatout(adap) adap->setdir(adap->data, 0) +#define getdat(adap) adap->getdat(adap->data) + +/* + * Send one byte of data to the chip. Data is latched into the chip on + * the rising edge of the clock. + */ +static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte) +{ + int i; + + for (i = 0; i < 8; i++) { + setclk(adap, 0); + udelay(adap->data_hold); + setdat(adap, byte & 1); + udelay(adap->data_setup); + setclk(adap, 1); + udelay(adap->clock_high); + byte >>= 1; + } +} + +/* + * Send a set of bytes to the chip. We need to pulse the MODE line + * between each byte, but never at the start nor at the end of the + * transfer. + */ +static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (i) { + udelay(adap->mode_hold); + setmode(adap, 0); + udelay(adap->mode); + } + setmode(adap, 1); + udelay(adap->mode_setup); + sendbyte(adap, buf[i]); + } +} + +/* + * Read one byte of data from the chip. Data is latched into the chip on + * the rising edge of the clock. + */ +static unsigned int readbyte(struct l3_algo_bit_data *adap) +{ + unsigned int byte = 0; + int i; + + for (i = 0; i < 8; i++) { + setclk(adap, 0); + udelay(adap->data_hold + adap->data_setup); + setclk(adap, 1); + if (getdat(adap)) + byte |= 1 << i; + udelay(adap->clock_high); + } + + return byte; +} + +/* + * Read a set of bytes from the chip. We need to pulse the MODE line + * between each byte, but never at the start nor at the end of the + * transfer. + */ +static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (i) { + udelay(adap->mode_hold); + setmode(adap, 0); + } + setmode(adap, 1); + udelay(adap->mode_setup); + buf[i] = readbyte(adap); + } +} + +static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num) +{ + struct l3_algo_bit_data *adap = l3_adap->algo_data; + int i; + + /* + * If we share an I2C bus, ensure that it is in STOP mode + */ + setclk(adap, 1); + setdat(adap, 1); + setmode(adap, 1); + setdatout(adap); + udelay(adap->mode); + + for (i = 0; i < num; i++) { + struct l3_msg *pmsg = &msgs[i]; + + if (!(pmsg->flags & L3_M_NOADDR)) { + setmode(adap, 0); + udelay(adap->mode_setup); + sendbyte(adap, pmsg->addr); + udelay(adap->mode_hold); + } + + if (pmsg->flags & L3_M_RD) { + setdatin(adap); + readbytes(adap, pmsg->buf, pmsg->len); + } else { + setdatout(adap); + sendbytes(adap, pmsg->buf, pmsg->len); + } + } + + /* + * Ensure that we leave the bus in I2C stop mode. + */ + setclk(adap, 1); + setdat(adap, 1); + setmode(adap, 0); + setdatin(adap); + + return num; +} + +static struct l3_algorithm l3_bit_algo = { + name: "L3 bit-shift algorithm", + xfer: l3_xfer, +}; + +int l3_bit_add_bus(struct l3_adapter *adap) +{ + adap->algo = &l3_bit_algo; + return l3_add_adapter(adap); +} + +int l3_bit_del_bus(struct l3_adapter *adap) +{ + return l3_del_adapter(adap); +} + +EXPORT_SYMBOL(l3_bit_add_bus); +EXPORT_SYMBOL(l3_bit_del_bus); --- /dev/null +++ linux-2.4.27/drivers/l3/l3-bit-sa1100.c @@ -0,0 +1,277 @@ +/* + * linux/drivers/l3/l3-bit-sa1100.c + * + * Copyright (C) 2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is a combined I2C and L3 bus driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NAME "l3-bit-sa1100-gpio" + +struct bit_data { + unsigned int sda; + unsigned int scl; + unsigned int l3_mode; +}; + +static int getsda(void *data) +{ + struct bit_data *bits = data; + + return GPLR & bits->sda; +} + +#ifdef CONFIG_I2C_BIT_SA1100_GPIO +static void i2c_setsda(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPDR &= ~bits->sda; + else { + GPCR = bits->sda; + GPDR |= bits->sda; + } + local_irq_restore(flags); +} + +static void i2c_setscl(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPDR &= ~bits->scl; + else { + GPCR = bits->scl; + GPDR |= bits->scl; + } + local_irq_restore(flags); +} + +static int i2c_getscl(void *data) +{ + struct bit_data *bits = data; + + return GPLR & bits->scl; +} + +static struct i2c_algo_bit_data i2c_bit_data = { + setsda: i2c_setsda, + setscl: i2c_setscl, + getsda: getsda, + getscl: i2c_getscl, + udelay: 10, + mdelay: 10, + timeout: 100, +}; + +static struct i2c_adapter i2c_adapter = { + name: NAME, + algo_data: &i2c_bit_data, +// inc_use: i2c_inc_use, +// dec_use: i2c_dec_use, +}; + +#define LOCK &i2c_adapter.lock + +static int __init i2c_init(struct bit_data *bits) +{ + i2c_bit_data.data = bits; + return i2c_bit_add_bus(&i2c_adapter); +} + +static void i2c_exit(void) +{ + i2c_bit_del_bus(&i2c_adapter); +} + +#else +static DECLARE_MUTEX(l3_lock); +#define LOCK &l3_lock +#define i2c_init(bits) (0) +#define i2c_exit() do { } while (0) +#endif + +#ifdef CONFIG_L3_BIT_SA1100_GPIO +/* + * iPAQs need the clock line driven hard high and low. + */ +static void l3_setscl(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPSR = bits->scl; + else + GPCR = bits->scl; + GPDR |= bits->scl; + local_irq_restore(flags); +} + +static void l3_setsda(void *data, int state) +{ + struct bit_data *bits = data; + + if (state) + GPSR = bits->sda; + else + GPCR = bits->sda; +} + +static void l3_setdir(void *data, int in) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (in) + GPDR &= ~bits->sda; + else + GPDR |= bits->sda; + local_irq_restore(flags); +} + +static void l3_setmode(void *data, int state) +{ + struct bit_data *bits = data; + + if (state) + GPSR = bits->l3_mode; + else + GPCR = bits->l3_mode; +} + +static struct l3_algo_bit_data l3_bit_data = { + data: NULL, + setdat: l3_setsda, + setclk: l3_setscl, + setmode: l3_setmode, + setdir: l3_setdir, + getdat: getsda, + data_hold: 1, + data_setup: 1, + clock_high: 1, + mode_hold: 1, + mode_setup: 1, +}; + +static struct l3_adapter l3_adapter = { + owner: THIS_MODULE, + name: NAME, + algo_data: &l3_bit_data, + lock: LOCK, +}; + +static int __init l3_init(struct bit_data *bits) +{ + l3_bit_data.data = bits; + return l3_bit_add_bus(&l3_adapter); +} + +static void __exit l3_exit(void) +{ + l3_bit_del_bus(&l3_adapter); +} +#else +#define l3_init(bits) (0) +#define l3_exit() do { } while (0) +#endif + +static struct bit_data bit_data; + +static int __init bus_init(void) +{ + struct bit_data *bit = &bit_data; + unsigned long flags; + int ret; + + if (machine_is_assabet() || machine_is_pangolin()) { + bit->sda = GPIO_GPIO15; + bit->scl = GPIO_GPIO18; + bit->l3_mode = GPIO_GPIO17; + } + +#if defined(CONFIG_SA1100_H3600) || defined(CONFIG_SA1100_H3100) + if (machine_is_h3600() || machine_is_h3100()) { + bit->sda = GPIO_H3600_L3_DATA; + bit->scl = GPIO_H3600_L3_CLOCK; + bit->l3_mode = GPIO_H3600_L3_MODE; + } +#endif + +#ifdef CONFIG_SA1100_STORK + if (machine_is_stork()) { + bit->sda = GPIO_STORK_L3_I2C_SDA; + bit->scl = GPIO_STORK_L3_I2C_SCL; + bit->l3_mode = GPIO_STORK_L3_MODE; + } +#endif + + if (!bit->sda) + return -ENODEV; + + /* + * Default level for L3 mode is low. + * We set SCL and SDA high (i2c idle state). + */ + local_irq_save(flags); + GPDR &= ~(bit->scl | bit->sda); + GPCR = bit->l3_mode | bit->scl | bit->sda; + GPDR |= bit->l3_mode; + local_irq_restore(flags); + + if (machine_is_assabet()) { + /* + * Release reset on UCB1300, ADI7171 and UDA1341. We + * need to do this here so that we can communicate on + * the I2C/L3 buses. + */ + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + mdelay(1); + ASSABET_BCR_clear(ASSABET_BCR_CODEC_RST); + mdelay(1); + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + } + + ret = i2c_init(bit); + if (ret == 0 && bit->l3_mode) { + ret = l3_init(bit); + if (ret) + i2c_exit(); + } + + return ret; +} + +static void __exit bus_exit(void) +{ + l3_exit(); + i2c_exit(); +} + +module_init(bus_init); +module_exit(bus_exit); --- /dev/null +++ linux-2.4.27/drivers/l3/l3-core.c @@ -0,0 +1,377 @@ +/* + * linux/drivers/l3/l3-core.c + * + * Copyright (C) 2001 Russell King + * + * General structure taken from i2c-core.c by Simon G. Vogl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * See linux/Documentation/l3 for further documentation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DECLARE_MUTEX(adapter_lock); +static LIST_HEAD(adapter_list); + +static DECLARE_MUTEX(driver_lock); +static LIST_HEAD(driver_list); + +/** + * l3_add_adapter - register a new L3 bus adapter + * @adap: l3_adapter structure for the registering adapter + * + * Make the adapter available for use by clients using name adap->name. + * The adap->adapters list is initialised by this function. + * + * Returns 0; + */ +int l3_add_adapter(struct l3_adapter *adap) +{ + INIT_LIST_HEAD(&adap->clients); + down(&adapter_lock); + list_add(&adap->adapters, &adapter_list); + up(&adapter_lock); + return 0; +} + +/** + * l3_del_adapter - unregister a L3 bus adapter + * @adap: l3_adapter structure to unregister + * + * Remove an adapter from the list of available L3 Bus adapters. + * + * Returns 0; + */ +int l3_del_adapter(struct l3_adapter *adap) +{ + down(&adapter_lock); + list_del(&adap->adapters); + up(&adapter_lock); + return 0; +} + +static struct l3_adapter *__l3_get_adapter(const char *name) +{ + struct list_head *l; + + list_for_each(l, &adapter_list) { + struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters); + + if (strcmp(adap->name, name) == 0) + return adap; + } + + return NULL; +} + +/** + * l3_get_adapter - get a reference to an adapter + * @name: driver name + * + * Obtain a l3_adapter structure for the specified adapter. If the adapter + * is not currently load, then load it. The adapter will be locked in core + * until all references are released via l3_put_adapter. + */ +struct l3_adapter *l3_get_adapter(const char *name) +{ + struct l3_adapter *adap; + int try; + + for (try = 0; try < 2; try ++) { + down(&adapter_lock); + adap = __l3_get_adapter(name); + if (adap && !try_inc_mod_count(adap->owner)) + adap = NULL; + up(&adapter_lock); + + if (adap) + break; + + if (try == 0) + request_module(name); + } + + return adap; +} + +/** + * l3_put_adapter - release a reference to an adapter + * @adap: driver to release reference + * + * Indicate to the L3 core that you no longer require the adapter reference. + * The adapter module may be unloaded when there are no references to its + * data structure. + * + * You must not use the reference after calling this function. + */ +void l3_put_adapter(struct l3_adapter *adap) +{ + if (adap && adap->owner) + __MOD_DEC_USE_COUNT(adap->owner); +} + +/** + * l3_add_driver - register a new L3 device driver + * @driver - driver structure to make available + * + * Make the driver available for use by clients using name driver->name. + * The driver->drivers list is initialised by this function. + * + * Returns 0; + */ +int l3_add_driver(struct l3_driver *driver) +{ + down(&driver_lock); + list_add(&driver->drivers, &driver_list); + up(&driver_lock); + return 0; +} + +/** + * l3_del_driver - unregister a L3 device driver + * @driver: driver to remove + * + * Remove an driver from the list of available L3 Bus device drivers. + * + * Returns 0; + */ +int l3_del_driver(struct l3_driver *driver) +{ + down(&driver_lock); + list_del(&driver->drivers); + up(&driver_lock); + return 0; +} + +static struct l3_driver *__l3_get_driver(const char *name) +{ + struct list_head *l; + + list_for_each(l, &driver_list) { + struct l3_driver *drv = list_entry(l, struct l3_driver, drivers); + + if (strcmp(drv->name, name) == 0) + return drv; + } + + return NULL; +} + +/** + * l3_get_driver - get a reference to a driver + * @name: driver name + * + * Obtain a l3_driver structure for the specified driver. If the driver is + * not currently load, then load it. The driver will be locked in core + * until all references are released via l3_put_driver. + */ +struct l3_driver *l3_get_driver(const char *name) +{ + struct l3_driver *drv; + int try; + + for (try = 0; try < 2; try ++) { + down(&adapter_lock); + drv = __l3_get_driver(name); + if (drv && !try_inc_mod_count(drv->owner)) + drv = NULL; + up(&adapter_lock); + + if (drv) + break; + + if (try == 0) + request_module(name); + } + + return drv; +} + +/** + * l3_put_driver - release a reference to a driver + * @drv: driver to release reference + * + * Indicate to the L3 core that you no longer require the driver reference. + * The driver module may be unloaded when there are no references to its + * data structure. + * + * You must not use the reference after calling this function. + */ +void l3_put_driver(struct l3_driver *drv) +{ + if (drv && drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); +} + +/** + * l3_attach_client - attach a client to an adapter and driver + * @client: client structure to attach + * @adap: adapter (module) name + * @drv: driver (module) name + * + * Attempt to attach a client (a user of a device driver) to a particular + * driver and adapter. If the specified driver or adapter aren't registered, + * request_module is used to load the relevant modules. + * + * Returns 0 on success, or negative error code. + */ +int l3_attach_client(struct l3_client *client, const char *adap, const char *drv) +{ + struct l3_adapter *adapter = l3_get_adapter(adap); + struct l3_driver *driver = l3_get_driver(drv); + int ret = -ENOENT; + + if (!adapter) + printk(KERN_ERR "%s: unable to get adapter: %s\n", + __FUNCTION__, adap); + if (!driver) + printk(KERN_ERR "%s: unable to get driver: %s\n", + __FUNCTION__, drv); + + if (adapter && driver) { + ret = 0; + + client->adapter = adapter; + client->driver = driver; + + list_add(&client->__adap, &adapter->clients); + + if (driver->attach_client) + ret = driver->attach_client(client); + } + + if (ret) { + l3_put_driver(driver); + l3_put_adapter(adapter); + } + return ret; +} + +/** + * l3_detach_client - detach a client from an adapter and driver + * @client: client structure to detach + * + * Detach the client from the adapter and driver. + */ +int l3_detach_client(struct l3_client *client) +{ + struct l3_adapter *adapter = client->adapter; + struct l3_driver *driver = client->driver; + + driver->detach_client(client); + + client->adapter = NULL; + client->driver = NULL; + + l3_put_driver(driver); + l3_put_adapter(adapter); + + list_del(&client->__adap); + + return 0; +} + +/** + * l3_transfer - transfer information on an L3 bus + * @adap: adapter structure to perform transfer on + * @msgs: array of l3_msg structures describing transfer + * @num: number of l3_msg structures + * + * Transfer the specified messages to/from a device on the L3 bus. + * + * Returns number of messages successfully transferred, otherwise negative + * error code. + */ +int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num) +{ + int ret = -ENOSYS; + + if (adap->algo->xfer) { + down(adap->lock); + ret = adap->algo->xfer(adap, msgs, num); + up(adap->lock); + } + return ret; +} + +/** + * l3_write - send data to a device on an L3 bus + * @client: registered client structure + * @addr: L3 bus address + * @buf: buffer for bytes to send + * @len: number of bytes to send + * + * Send len bytes pointed to by buf to device address addr on the L3 bus + * described by client. + * + * Returns the number of bytes transferred, or negative error code. + */ +int l3_write(struct l3_client *client, int addr, const char *buf, int len) +{ + struct l3_adapter *adap = client->adapter; + struct l3_msg msg; + int ret; + + msg.addr = addr; + msg.flags = 0; + msg.buf = (char *)buf; + msg.len = len; + + ret = l3_transfer(adap, &msg, 1); + return ret == 1 ? len : ret; +} + +/** + * l3_read - receive data from a device on an L3 bus + * @client: registered client structure + * @addr: L3 bus address + * @buf: buffer for bytes to receive + * @len: number of bytes to receive + * + * Receive len bytes from device address addr on the L3 bus described by + * client to a buffer pointed to by buf. + * + * Returns the number of bytes transferred, or negative error code. + */ +int l3_read(struct l3_client *client, int addr, char *buf, int len) +{ + struct l3_adapter *adap = client->adapter; + struct l3_msg msg; + int ret; + + msg.addr = addr; + msg.flags = L3_M_RD; + msg.buf = buf; + msg.len = len; + + ret = l3_transfer(adap, &msg, 1); + return ret == 1 ? len : ret; +} + +EXPORT_SYMBOL(l3_add_adapter); +EXPORT_SYMBOL(l3_del_adapter); +EXPORT_SYMBOL(l3_get_adapter); +EXPORT_SYMBOL(l3_put_adapter); + +EXPORT_SYMBOL(l3_add_driver); +EXPORT_SYMBOL(l3_del_driver); +EXPORT_SYMBOL(l3_get_driver); +EXPORT_SYMBOL(l3_put_driver); + +EXPORT_SYMBOL(l3_attach_client); +EXPORT_SYMBOL(l3_detach_client); + +EXPORT_SYMBOL(l3_transfer); +EXPORT_SYMBOL(l3_write); +EXPORT_SYMBOL(l3_read); --- /dev/null +++ linux-2.4.27/drivers/l3/l3-sa1111.c @@ -0,0 +1,118 @@ +/* + * L3 SA1111 algorithm/adapter module. + * + * By Russell King, + * gratuitously ripped from sa1111-uda1341.c by John Dorsey. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static inline unsigned char l3_sa1111_recv_byte(unsigned char addr) +{ + unsigned char dat; + + L3_CAR = addr; + while ((SASR0 & SASR0_L3RD) == 0) + mdelay(1); + dat = L3_CDR; + SASCR = SASCR_RDD; + return dat; +} + +static void l3_sa1111_recv_msg(struct l3_msg *msg) +{ + int len = msg->len; + char *p = msg->buf; + + if (len > 1) { + SACR1 |= SACR1_L3MB; + while ((len--) > 1) + *p++ = l3_sa1111_recv_byte(msg->addr); + } + SACR1 &= ~SACR1_L3MB; + *p = l3_sa1111_recv_byte(msg->addr); +} + +static inline void l3_sa1111_send_byte(unsigned char addr, unsigned char dat) +{ + L3_CAR = addr; + L3_CDR = dat; + while ((SASR0 & SASR0_L3WD) == 0) + mdelay(1); + SASCR = SASCR_DTS; +} + +static void l3_sa1111_send_msg(struct l3_msg *msg) +{ + int len = msg->len; + char *p = msg->buf; + + if (len > 1) { + SACR1 |= SACR1_L3MB; + while ((len--) > 1) + l3_sa1111_send_byte(msg->addr, *p++); + } + SACR1 &= ~SACR1_L3MB; + l3_sa1111_send_byte(msg->addr, *p); +} + +static int l3_sa1111_xfer(struct l3_adapter *adap, struct l3_msg msgs[], int num) +{ + int i; + + for (i = 0; i < num; i++) { + struct l3_msg *pmsg = &msgs[i]; + + if (pmsg->flags & L3_M_RD) + l3_sa1111_recv_msg(pmsg); + else + l3_sa1111_send_msg(pmsg); + } + + return num; +} + +static struct l3_algorithm l3_sa1111_algo = { + name: "L3 SA1111 algorithm", + xfer: l3_sa1111_xfer, +}; + +static DECLARE_MUTEX(sa1111_lock); + +static struct l3_adapter l3_sa1111_adapter = { + owner: THIS_MODULE, + name: "l3-sa1111", + algo: &l3_sa1111_algo, + lock: &sa1111_lock, +}; + +static int __init l3_sa1111_init(void) +{ + int ret = -ENODEV; + if ((machine_is_assabet() && machine_has_neponset()) || + machine_is_jornada720() || machine_is_accelent_sa() || + machine_is_badge4()) + ret = l3_add_adapter(&l3_sa1111_adapter); + return ret; +} + +static void __exit l3_sa1111_exit(void) +{ + l3_del_adapter(&l3_sa1111_adapter); +} + +module_init(l3_sa1111_init); +module_exit(l3_sa1111_exit); --- linux-2.4.27/drivers/media/video/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/media/video/Config.in @@ -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 --- linux-2.4.27/drivers/media/video/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/media/video/Makefile @@ -16,7 +16,7 @@ obj-n := obj- := -SUB_DIRS := +SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) @@ -47,7 +47,8 @@ obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o i2c-old.o obj-$(CONFIG_VIDEO_ZORAN_BUZ) += saa7111.o saa7185.o obj-$(CONFIG_VIDEO_ZORAN_DC10) += saa7110.o adv7175.o -obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o +obj-$(CONFIG_VIDEO_CYBERPRO) += cyberpro.o i2c-old.o saa7111.o +obj-$(CONFIG_VIDEO_LML33) += bt856.o bt819.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_PLANB) += planb.o obj-$(CONFIG_VIDEO_VINO) += saa7191.o indycam.o vino.o --- /dev/null +++ linux-2.4.27/drivers/media/video/cyberpro.c @@ -0,0 +1,2091 @@ +/* + * CyberPro 2000 video capture driver for the Rebel.com NetWinder + * + * (C) 1999-2000 Russell King + * + * Re-written from Rebel.com's vidcap driver. + * + * Architecture + * ------------ + * The NetWinder video capture consists of a SAA7111 video decoder chip + * connected to the CyberPro feature bus. The video data is captured to + * the VGA memory, where the CyberPro can overlay (by chromakeying) the + * data onto the VGA display. + * + * The CyberPro also has some nifty features, including a second overlay + * and picture in picture mode. We do not currently use these features. + * + * Power Saving + * ------------ + * Please note that rev.5 NetWinders have the ability to hold the SAA7111 + * decoder chip into reset, which saves power. The only time at which + * this is done is when the driver is unloaded, which implies that this + * is compiled as a module. + * + * In this case, you will want the kernel to automatically load this + * driver when required. Place the following line in /etc/modules.conf + * to enable this: + * + * alias char-major-81-0 cyberpro + * + * The relevant modules will be automatically loaded by modprobe on a + * as and when needed basis. + * + * Capture resolution + * ------------------ + * The maximum useful capture resolution is: + * 625-line UK: 716x576 + * 525-line US: ? + * + * Bugs + * ---- + * 1. The CyberPro chip seems to be prone to randomly scribbling over VGA + * memory [hopefully fixed with new capture enable/freeze stuff] + * 2. read()ing pauses video capture, and sometimes triggers bug 1. + * 3. mmap() is not supported (requires BM-DMA - see bug 4) + * 4. Really, we want to do scatter BM-DMA. Is the CyberPro capable of this? + * The Cyberpro seems to randomly scribble to various PCI addresses if you + * transfer >16 words. + * 5. We shouldn't ignore O_NONBLOCK when reading a frame. + * 6. The incoming stream on the NetWinder is CCIR656, which is YUV422. + * CyberPro docs also call the format we capture and overlay "YUV422", + * but we actually seem to have Y, U, Y, V bytes (is this YUYV format?) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("CyberPro v4l video grabber"); +MODULE_LICENSE("GPL"); + +#include "../../video/cyber2000fb.h" + +/* + * These enable various experimental features. Most of these + * are just plain broken or just don't work at the moment. + */ +/* + * Enable this if you want mmap() access. (see bug 4) + */ +#undef USE_MMAP + +/* + * Enable this if you want mmio access. (slow) + */ +#define USE_MMIO + +/* + * The V4L API is unclear whether VIDIOCSCAPTURE call is allowed while + * capture is running. The default is to disallow the call. + * + * Define this if you do want to allow the call while capture is active. + */ +#undef ALLOW_SCAPTURE_WHILE_CAP + +/* + * We capture two frames + */ +#define NR_FRAMES 2 + +/* + * One frame of video is 202 pages, assuming YUV422 format, 716x576 + */ +#define NR_PAGES 202 + +struct src_info { + unsigned int offset; /* offset of source data */ + unsigned int x; /* source x */ + unsigned int y; /* source y */ + unsigned int width; /* source width */ + unsigned int height; /* source height */ + unsigned int format; /* source format */ +}; + +struct dst_info { + unsigned int x; /* destination x */ + unsigned int y; /* destination y */ + unsigned int width; /* destination width */ + unsigned int height; /* destination height */ + unsigned int chromakey; /* chromakey */ + unsigned int flags; /* flags (eg, chromakey enable) */ +}; + +struct cyberpro_vidinfo; + +struct win_info { + void (*init)(struct cyberpro_vidinfo *dp, struct win_info *wi); + void (*set_src)(struct cyberpro_vidinfo *dp, struct win_info *wi); + void (*set_win)(struct cyberpro_vidinfo *dp, struct win_info *wi); + void (*ctl)(struct cyberpro_vidinfo *dp, struct win_info *wi, int on_off); + + /* public */ + struct src_info src; + struct dst_info dst; + + /* private */ + unsigned short vid_fifo_ctl; + unsigned char vid_fmt; + unsigned char vid_disp_ctl1; + unsigned char vid_fifo_ctl1; + unsigned char vid_misc_ctl1; +}; + +struct framebuf { + unsigned int offset; /* mmap offset for this frame */ + unsigned int status; +#define FRAME_FREE 0 +#define FRAME_DONE 1 +#define FRAME_WAITING 2 +#define FRAME_GRABBING 3 + + /* + * Bus-Master DMA stuff. Note that we should + * probably use the kiovec stuff instead. + */ + unsigned long bus_addr[NR_PAGES]; /* list of pages */ + struct page *pages[NR_PAGES]; + void *buffer; + int dbg; +}; + +struct cyberpro_vidinfo { + struct video_device *dev; + struct i2c_bus *bus; + struct cyberpro_info info; /* host information */ + unsigned char *regs; + unsigned int irq; /* PCI interrupt number */ + + /* hardware configuration */ + unsigned int stream_fmt; /* format of stream from decoder*/ + + /* software settings */ + unsigned int decoder:1; /* decoder loaded */ + unsigned int interlace:1; /* interlace */ + unsigned int buf_set:1; /* VIDIOCSFBUF has been issued */ + unsigned int win_set:1; /* VIDIOCSWIN has been issued */ + unsigned int cap_active:1; /* capture is active */ + unsigned int ovl_active:1; /* overlay is active */ + unsigned int mmaped:1; /* buffer is mmap()d */ + unsigned int unused:25; + + unsigned int users; /* number of users */ + unsigned long cap_mem_offset; /* capture framebuffer offset */ + void * buffer; /* kernel capture buffer */ + unsigned int norm; /* video standard */ + + struct video_capability cap; /* capabilities */ + struct video_picture pic; /* current picture settings */ + struct video_buffer buf; /* display parameters */ + struct video_capture capt; /* video capture params */ + + struct win_info *ovl; /* overlay window set */ + struct win_info ext; /* "Extended" window info */ + struct win_info v2; /* "V2" window info */ + struct win_info x2; /* "X2" window info */ + + unsigned int bm_offset; /* Cap memory bus master offset */ + unsigned int bm_index; /* Cap page index */ + +#ifdef USE_MMAP + unsigned int frame_idx; /* currently grabbing frame */ + unsigned int frame_size; + struct framebuf frame[NR_FRAMES]; + wait_queue_head_t frame_wait; +#endif + + wait_queue_head_t vbl_wait; + + /* + * cyberpro registers + */ + unsigned char cap_mode1; + unsigned char cap_mode2; + unsigned char cap_miscctl; + unsigned char vfac1; + unsigned char vfac3; +}; + +/* + * Our access methods. + */ +#define cyberpro_writel(val,reg,dp) writel(val, (dp)->regs + (reg)) +#define cyberpro_writew(val,reg,dp) writew(val, (dp)->regs + (reg)) +#define cyberpro_writeb(val,reg,dp) writeb(val, (dp)->regs + (reg)) + +#define cyberpro_readb(reg,dp) readb((dp)->regs + (reg)) + +static inline void +cyberpro_grphw(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp) +{ + cyberpro_writew((reg & 255) | val << 8, 0x3ce, dp); +} + +static void cyberpro_grphw8(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp) +{ + cyberpro_grphw(reg, val, dp); +} + +static unsigned char cyberpro_grphr8(int reg, struct cyberpro_vidinfo *dp) +{ + cyberpro_writeb(reg, 0x3ce, dp); + return cyberpro_readb(0x3cf, dp); +} + +static void cyberpro_grphw16(int reg, unsigned int val, struct cyberpro_vidinfo *dp) +{ + cyberpro_grphw(reg, val, dp); + cyberpro_grphw(reg + 1, val >> 8, dp); +} + +static void cyberpro_grphw24(int reg, unsigned int val, struct cyberpro_vidinfo *dp) +{ + cyberpro_grphw(reg, val, dp); + cyberpro_grphw(reg + 1, val >> 8, dp); + cyberpro_grphw(reg + 2, val >> 16, dp); +} + +#if 0 +static void +cyberpro_dbg_dump(void) +{ + int i; + unsigned char idx[] = + { 0x30, 0x3e, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad }; + printk(KERN_DEBUG); + for (i = 0; i < sizeof(idx); i++) + printk("%02x ", idx[i]); + printk("\n" KERN_DEBUG); + for (i = 0; i < sizeof(idx); i++) + printk("%02x ", cyberpro_grphr8(idx[i])); + printk("\n"); +} +#endif + +/* + * On the NetWinder, we can put the SAA7111 to sleep by holding + * it in reset. + * + * Note: once we have initialised the SAA7111, we can't put it back to + * sleep and expect it to keep its settings. Maybe a better solution + * is to register/de-register the i2c bus in open/release? + */ +static void +decoder_sleep(int sleep) +{ +#ifdef CONFIG_ARCH_NETWINDER + extern spinlock_t gpio_lock; + + spin_lock_irq(&gpio_lock); + cpld_modify(CPLD_7111_DISABLE, sleep ? CPLD_7111_DISABLE : 0); + spin_unlock_irq(&gpio_lock); + + if (!sleep) { + /* + * wait 20ms for device to wake up + */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 50); + } +#endif +} + +/* -------------------------------- I2C support ---------------------------- */ + +#define I2C_DELAY 100 + +static void +cyberpro_i2c_setlines(struct i2c_bus *bus, int ctrl, int data) +{ + struct cyberpro_vidinfo *dp = bus->data; + int v; + + v = (ctrl ? EXT_LATCH2_I2C_CLKEN : 0x00) | (data ? EXT_LATCH2_I2C_DATEN : 0x00); + cyberpro_grphw8(EXT_LATCH2, v, dp); + + udelay(I2C_DELAY); +} + +static int +cyberpro_i2c_getdataline(struct i2c_bus *bus) +{ + struct cyberpro_vidinfo *dp = bus->data; + unsigned long flags; + int v; + + save_flags(flags); + cli(); + + v = cyberpro_grphr8(EXT_LATCH2, dp); + + restore_flags(flags); + + return v & EXT_LATCH2_I2C_DAT ? 1 : 0; +} + +static void +cyberpro_i2c_attach(struct i2c_bus *bus, int id) +{ + struct cyberpro_vidinfo *dp = bus->data; + int zero = 0; + + if (id == I2C_DRIVERID_VIDEODECODER) { + __u16 norm = dp->norm; + i2c_control_device(bus, id, DECODER_SET_NORM, &norm); + i2c_control_device(bus, id, DECODER_SET_PICTURE, &dp->pic); + i2c_control_device(bus, id, DECODER_ENABLE_OUTPUT, &zero); + + dp->decoder = 1; + } +} + +static void +cyberpro_i2c_detach(struct i2c_bus *bus, int id) +{ + struct cyberpro_vidinfo *dp = bus->data; + + if (id == I2C_DRIVERID_VIDEODECODER) + dp->decoder = 0; +} + +static struct i2c_bus cyberpro_i2c_bus = { + name: "", + id: I2C_BUSID_CYBER2000, + bus_lock: SPIN_LOCK_UNLOCKED, + attach_inform: cyberpro_i2c_attach, + detach_inform: cyberpro_i2c_detach, + i2c_setlines: cyberpro_i2c_setlines, + i2c_getdataline: cyberpro_i2c_getdataline, +}; + +/*------------------------- Extended Overlay Window ------------------------- + * Initialise 1st overlay window (works) + */ +static void +cyberpro_ext_init(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + wi->vid_fifo_ctl = 0xf87c; + wi->vid_fmt = EXT_VID_FMT_YUV422; + wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF | + EXT_VID_DISP_CTL1_NOCLIP; + wi->vid_fifo_ctl1 = EXT_VID_FIFO_CTL1_INTERLEAVE | + EXT_VID_FIFO_CTL1_OE_HIGH; + wi->vid_misc_ctl1 = 0; + + cyberpro_grphw8 (EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); + cyberpro_grphw16(EXT_DDA_X_INIT, 0x0800, dp); + cyberpro_grphw16(EXT_DDA_Y_INIT, 0x0800, dp); + cyberpro_grphw16(EXT_VID_FIFO_CTL, wi->vid_fifo_ctl, dp); + cyberpro_grphw8 (EXT_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp); +} + +/* + * Set the source parameters for the extended window + */ +static void +cyberpro_ext_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int phase, pitch; + + pitch = (wi->src.width >> 2) & 0x0fff; + phase = (wi->src.width + 3) >> 2; + + wi->vid_fmt &= ~7; + switch (wi->src.format) { + case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; + case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; + case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; + case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; + case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; + } + + cyberpro_grphw24(EXT_MEM_START, wi->src.offset, dp); + cyberpro_grphw16(EXT_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); + cyberpro_grphw8 (EXT_SRC_WIN_WIDTH, phase, dp); + cyberpro_grphw8 (EXT_VID_FMT, wi->vid_fmt, dp); +} + +/* + * Set overlay1 window + */ +static void +cyberpro_ext_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int xscale, yscale; + unsigned int xoff, yoff; + + /* + * Note: the offset does not appear to be influenced by + * hardware scrolling. + */ + xoff = yoff = 0; + + xoff += wi->dst.x; + yoff += wi->dst.y; + + xscale = wi->src.width; + + if (wi->dst.width >= wi->src.width * 2) { + wi->vid_fmt |= EXT_VID_FMT_DBL_H_PIX; + xscale *= 2; + } else { + wi->vid_fmt &= ~EXT_VID_FMT_DBL_H_PIX; + } + + xscale = ((xscale - /*2*/0) * 4096) / wi->dst.width; + yscale = ((wi->src.height - /*2*/0) * 4096) / wi->dst.height; + + cyberpro_grphw16(EXT_X_START, xoff, dp); + cyberpro_grphw16(EXT_X_END, xoff + wi->dst.width, dp); + cyberpro_grphw16(EXT_Y_START, yoff, dp); + cyberpro_grphw16(EXT_Y_END, yoff + wi->dst.height, dp); + cyberpro_grphw24(EXT_COLOUR_COMPARE, wi->dst.chromakey, dp); + cyberpro_grphw16(EXT_DDA_X_INC, xscale, dp); + cyberpro_grphw16(EXT_DDA_Y_INC, yscale, dp); + cyberpro_grphw8(EXT_VID_FMT, wi->vid_fmt, dp); + + if (wi->dst.flags & VIDEO_WINDOW_CHROMAKEY) + wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_IGNORE_CCOMP; + else + wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_IGNORE_CCOMP; +} + +/* + * Enable or disable the 1st overlay window. Note that for anything + * useful to be displayed, we must have capture enabled. + */ +static void +cyberpro_ext_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) +{ + if (on) + wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; + else + wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; + + cyberpro_grphw8(EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); +} + +/*------------------------------- V2 Overlay Window ------------------------- + * Initialise 2nd overlay window (guesswork) + */ +static void +cyberpro_v2_init(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + wi->vid_fifo_ctl = 0xf87c; + wi->vid_fmt = EXT_VID_FMT_YUV422; + wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF | + EXT_VID_DISP_CTL1_NOCLIP; + wi->vid_fifo_ctl1 = 0x06; + wi->vid_misc_ctl1 = 0; + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw8 (Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); + /* No DDA init values */ + cyberpro_grphw16(Y_V2_VID_FIFO_CTL, wi->vid_fifo_ctl, dp); + cyberpro_grphw8 (Y_V2_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp); +} + +/* + * Set the source parameters for the v2 window + */ +static void +cyberpro_v2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int phase, pitch; + + pitch = (wi->src.width >> 2) & 0x0fff; + phase = (wi->src.width + 3) >> 2; + + wi->vid_fmt &= ~7; + switch (wi->src.format) { + case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; + case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; + case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; + case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; + case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; + } + + cyberpro_grphw8(REG_BANK, REG_BANK_X, dp); + cyberpro_grphw24(X_V2_VID_MEM_START, wi->src.offset, dp); + cyberpro_grphw16(X_V2_VID_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); + cyberpro_grphw8 (X_V2_VID_SRC_WIN_WIDTH, phase, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw8(Y_V2_VID_FMT, wi->vid_fmt, dp); +} + +/* + * Set v2 window + */ +static void +cyberpro_v2_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int xscale, yscale; + unsigned int xoff, yoff; + + /* + * Note: the offset does not appear to be influenced by + * hardware scrolling. + */ + xoff = yoff = 0; + + xoff += wi->dst.x; + yoff += wi->dst.y; + + xscale = (wi->src.width * 4096) / wi->dst.width; + yscale = (wi->src.height * 4096) / wi->dst.height; + + cyberpro_grphw8(REG_BANK, REG_BANK_X, dp); + cyberpro_grphw16(X_V2_X_START, xoff, dp); + cyberpro_grphw16(X_V2_X_END, xoff + wi->dst.width, dp); + cyberpro_grphw16(X_V2_Y_START, yoff, dp); + cyberpro_grphw16(X_V2_Y_END, yoff + wi->dst.height, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw16(Y_V2_DDA_X_INC, xscale, dp); + cyberpro_grphw16(Y_V2_DDA_Y_INC, yscale, dp); +} + +/* + * Enable or disable the 2nd overlay window. Note that for anything + * useful to be displayed, we must have capture enabled. + */ +static void +cyberpro_v2_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) +{ + if (on) + wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; + else + wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw8(Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); +} + +/*--------------------------- X2 Overlay Window ----------------------------- + * Initialise 3rd overlay window (guesswork) + */ +static void +cyberpro_x2_init(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + wi->vid_fmt = EXT_VID_FMT_YUV422; + wi->vid_disp_ctl1 = 0x40; + wi->vid_misc_ctl1 = 0; + + cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); + cyberpro_grphw8 (K_X2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); + cyberpro_grphw16(K_X2_DDA_X_INIT, 0x0800, dp); + cyberpro_grphw16(K_X2_DDA_Y_INIT, 0x0800, dp); +} + +/* + * Set the source parameters for the x2 window + */ +static void +cyberpro_x2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int phase, pitch; + + pitch = (wi->src.width >> 2) & 0x0fff; + phase = (wi->src.width + 3) >> 2; + + wi->vid_fmt &= ~7; + switch (wi->src.format) { + case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; + case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; + case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; + case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; + case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; + } + + cyberpro_grphw8(REG_BANK, REG_BANK_J, dp); + cyberpro_grphw24(J_X2_VID_MEM_START, wi->src.offset, dp); + cyberpro_grphw16(J_X2_VID_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); + cyberpro_grphw8 (J_X2_VID_SRC_WIN_WIDTH, phase, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); + cyberpro_grphw8(K_X2_VID_FMT, wi->vid_fmt, dp); +} + +/* + * Set x2 window + */ +static void +cyberpro_x2_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int xscale, yscale; + unsigned int xoff, yoff; + + /* + * Note: the offset does not appear to be influenced by + * hardware scrolling. + */ + xoff = yoff = 0; + + xoff += wi->dst.x; + yoff += wi->dst.y; + + xscale = (wi->src.width * 4096) / wi->dst.width; + yscale = (wi->src.height * 4096) / wi->dst.height; + + cyberpro_grphw8(REG_BANK, REG_BANK_J, dp); + cyberpro_grphw16(J_X2_X_START, xoff, dp); + cyberpro_grphw16(J_X2_X_END, xoff + wi->dst.width, dp); + cyberpro_grphw16(J_X2_Y_START, yoff, dp); + cyberpro_grphw16(J_X2_Y_END, yoff + wi->dst.height, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); + cyberpro_grphw16(K_X2_DDA_X_INC, xscale, dp); + cyberpro_grphw16(K_X2_DDA_Y_INC, yscale, dp); +} + +/* + * Enable or disable the 3rd overlay window. Note that for anything + * useful to be displayed, we must have capture enabled. + */ +static void +cyberpro_x2_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) +{ + if (on) + wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; + else + wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; + + cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); + cyberpro_grphw8(K_X2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); +} + +/* ------------------------------------------------------------------------- */ + +#if 0 +static void reset_seq(struct cyberpro_vidinfo *dp) +{ + unsigned char ext_mem_ctl = cyberpro_grphr8(0x70, dp); + + cyberpro_grphw8(ext_mem_ctl | 0x80, 0x70, dp); + cyberpro_grphw8(ext_mem_ctl, 0x70, dp); +} +#endif + +#ifdef USE_MMAP +/* + * Buffer support + */ +static int +cyberpro_alloc_frame_buffer(struct cyberpro_vidinfo *dp, + struct framebuf *frame) +{ + unsigned long addr; + void *buffer; + int pgidx; + + if (frame->buffer) + return 0; + + /* + * Allocate frame buffer + */ + buffer = vmalloc(NR_PAGES * PAGE_SIZE); + + if (frame->buffer) { + vfree(buffer); + return 0; + } + + if (!buffer) + return -ENOMEM; + + printk("Buffer allocated @ %p [", buffer); + + frame->buffer = buffer; + frame->dbg = 1; + + /* + * Don't leak information from the kernel. + */ + memset(buffer, 0x5a, NR_PAGES * PAGE_SIZE); + + /* + * Now, reserve all the pages, and calculate + * each pages' bus address. + */ + addr = (unsigned long)buffer; + for (pgidx = 0; pgidx < NR_PAGES; pgidx++, addr += PAGE_SIZE) { + struct page *page; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + /* + * The page should be present. If not, + * vmalloc has gone nuts. + */ + pgd = pgd_offset_k(addr); + if (pgd_none(*pgd)) + BUG(); + pmd = pmd_offset(pgd, addr); + if (pmd_none(*pmd)) + BUG(); + pte = pte_offset(pmd, addr); + if (!pte_present(*pte)) + BUG(); + + page = pte_page(*pte); + + frame->bus_addr[pgidx] = virt_to_bus((void *)page_address(page)); + frame->pages[pgidx] = page; + SetPageReserved(page); + + printk("%08lx (%08lx) ", page_address(page), frame->bus_addr[pgidx]); + } + printk("\n"); + + return 0; +} + +static void +cyberpro_frames_free_one(struct cyberpro_vidinfo *dp, struct framebuf *frame) +{ + void *buffer; + int pgidx; + + frame->status = FRAME_FREE; + buffer = frame->buffer; + frame->buffer = NULL; + + if (buffer) { + for (pgidx = 0; pgidx < NR_PAGES; pgidx++) { + frame->bus_addr[pgidx] = 0; + ClearPageReserved(frame->pages[pgidx]); + frame->pages[pgidx] = NULL; + } + vfree(buffer); + } +} + +static void +cyberpro_busmaster_frame(struct cyberpro_vidinfo *dp, struct framebuf *frame) +{ + unsigned long bus_addr; + + bus_addr = frame->bus_addr[dp->bm_index]; + + if (frame->dbg) { + printk("Frame%d: %06x -> %08lx\n", + dp->frame_idx, + dp->bm_offset, + bus_addr); + } + + cyber2000_outw(dp->bm_offset, BM_VID_ADDR_LOW); + cyber2000_outw(dp->bm_offset >> 16, BM_VID_ADDR_HIGH); + + cyber2000_outw(bus_addr, BM_ADDRESS_LOW); + cyber2000_outw(bus_addr >> 16, BM_ADDRESS_HIGH); + + /* + * One page-full only + */ + cyber2000_outw(1023, BM_LENGTH); + + /* + * Load length + */ + cyber2000_outw(BM_CONTROL_INIT, BM_CONTROL); + + /* + * Enable transfer + */ + cyber2000_outw(BM_CONTROL_ENABLE|BM_CONTROL_IRQEN, BM_CONTROL); + + dp->bm_offset += 1024; + dp->bm_index += 1; +} + +static void cyberpro_busmaster_interrupt(struct cyberpro_vidinfo *dp) +{ + struct framebuf *frame = dp->frame + dp->frame_idx; + + /* + * Disable Busmaster operations + */ + cyber2000_outw(0, BM_CONTROL); + + if (frame->status == FRAME_GRABBING) { + /* + * We are still grabbing this frame to system + * memory. Transfer next page if there are + * more, or else flag this frame as complete. + */ + if (dp->bm_index < NR_PAGES) + cyberpro_busmaster_frame(dp); + else { + unsigned int idx; + + frame->status = FRAME_DONE; + frame->dbg = 0; + + idx = dp->frame_idx + 1; + if (idx >= NR_FRAMES) + idx = 0; + + dp->frame_idx = idx; + + wake_up(&dp->frame_wait); + } + } +} + +static void cyberpro_frames_vbl(struct cyberpro_vidinfo *dp, unsigned int stat) +{ + struct framebuf *frame = dp->frame + dp->frame_idx; + + /* + * No point capturing frames if the grabber isn't active. + */ + if (stat & EXT_ROM_UCB4GH_FREEZE) + return; + + /* + * If the next buffer is ready for grabbing, + * set up the bus master registers for the + * transfer. + */ + if (frame->status == FRAME_WAITING) { + frame->status = FRAME_GRABBING; + + dp->bm_offset = dp->cap_mem_offset; + dp->bm_index = 0; + + cyberpro_busmaster_frame(dp, frame); + } +} + +static void __init cyberpro_frames_init(struct cyberpro_vidinfo *dp) +{ + unsigned int offset, maxsize; + int i; + + init_waitqueue_head(&dp->frame_wait); + + maxsize = 2 * dp->cap.maxwidth * dp->cap.maxheight; + dp->frame_size = PAGE_ALIGN(maxsize); + dp->frame_idx = 0; + + for (i = offset = 0; i < NR_FRAMES; i++) { + dp->frame[i].offset = offset; + dp->frame[i].status = FRAME_FREE; + offset += dp->frame_size; + } +} + +static void cyberpro_frames_free(struct cyberpro_vidinfo *dp) +{ + int i; + + dp->mmaped = 0; + + /* + * Free all frame buffers + */ + for (i = 0; i < NR_FRAMES; i++) + cyberpro_frames_free_one(dp, dp->frame + i); +} + +#else +#define cyberpro_frames_vbl(dp,stat) do { } while (0) +#define cyberpro_frames_init(dp) do { } while (0) +#define cyberpro_frames_free(dp) do { } while (0) +#endif + +/* + * CyberPro Interrupts + * ------------------- + * + * We don't really know how to signal an IRQ clear to the chip. However, + * disabling and re-enabling the capture interrupt enable seems to do what + * we want. + */ +static void cyberpro_interrupt(int nr, void *dev_id, struct pt_regs *regs) +{ + struct cyberpro_vidinfo *dp = dev_id; + unsigned char old_grphidx; + unsigned int status; + + /* + * Save old graphics index register + */ + old_grphidx = cyberpro_readb(0x3ce, dp); + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + + /* + * Was it due to the Capture VSYNC? + */ + if (status & EXT_ROM_UCB4GH_INTSTAT) { + /* + * Frob the IRQ enable bit to drop the request. + */ + cyberpro_grphw8(VFAC_CTL3, dp->vfac3 & ~VFAC_CTL3_CAP_IRQ, dp); + cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); + + cyberpro_frames_vbl(dp, status); + wake_up(&dp->vbl_wait); + } + + /* + * Restore graphics controller index + */ + cyberpro_writeb(old_grphidx, 0x3ce, dp); + +#ifdef USE_MMAP + /* + * Do Bus-Master IRQ stuff + */ + if (cyber2000_inb(BM_CONTROL) & (1 << 7)) + cyberpro_busmaster_interrupt(dp); +#endif +} + +static void cyberpro_capture(struct cyberpro_vidinfo *dp, int on) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned int status; + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + + add_wait_queue(&dp->vbl_wait, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + + if (!!on ^ !(status & EXT_ROM_UCB4GH_FREEZE)) { + if (on) { + schedule_timeout(40 * HZ / 1000); + dp->vfac1 &= ~(VFAC_CTL1_FREEZE_CAPTURE|VFAC_CTL1_FREEZE_CAPTURE_SYNC); + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + } else { + dp->vfac1 |= VFAC_CTL1_FREEZE_CAPTURE_SYNC; + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + if (!(status & EXT_ROM_UCB4GH_FREEZE)) + schedule_timeout(40 * HZ / 1000); + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dp->vbl_wait, &wait); +} + +static void cyberpro_capture_one(struct cyberpro_vidinfo *dp) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + unsigned int status; + unsigned long policy, rt_priority; + + policy = tsk->policy; + rt_priority = tsk->rt_priority; + + tsk->policy = SCHED_FIFO; + tsk->rt_priority = 1; + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + + add_wait_queue(&dp->vbl_wait, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + + schedule_timeout(40 * HZ / 1000); + dp->vfac1 &= ~(VFAC_CTL1_FREEZE_CAPTURE|VFAC_CTL1_FREEZE_CAPTURE_SYNC); + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(40 * HZ / 1000); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(40 * HZ / 1000); + + dp->vfac1 |= VFAC_CTL1_FREEZE_CAPTURE_SYNC; + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + + set_current_state(TASK_UNINTERRUPTIBLE); + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + + current->state = TASK_RUNNING; + remove_wait_queue(&dp->vbl_wait, &wait); + + tsk->policy = policy; + tsk->rt_priority = rt_priority; +} + +static void cyberpro_capture_set_win(struct cyberpro_vidinfo *dp) +{ + unsigned int xstart, xend, ystart, yend; + + xstart = 4 + dp->capt.x; + xend = xstart + dp->capt.width; + + if (dp->cap_mode1 & EXT_CAP_MODE1_8BIT) { + /* 8-bit capture */ + xstart *= 2; + xend *= 2; + } + + xstart -= 1; + xend -= 1; + + ystart = 18 + dp->capt.y; + yend = ystart + dp->capt.height / 2; + + cyberpro_grphw16(CAP_X_START, xstart, dp); + cyberpro_grphw16(CAP_X_END, xend + 1, dp); + cyberpro_grphw16(CAP_Y_START, ystart, dp); + cyberpro_grphw16(CAP_Y_END, yend + 2, dp); + + /* + * This should take account of capt.decimation + */ + cyberpro_grphw16(CAP_DDA_X_INIT, 0x0800, dp); + cyberpro_grphw16(CAP_DDA_X_INC, 0x1000, dp); + cyberpro_grphw16(CAP_DDA_Y_INIT, 0x0800, dp); + cyberpro_grphw16(CAP_DDA_Y_INC, 0x1000, dp); + + cyberpro_grphw8(CAP_PITCH, dp->capt.width >> 2, dp); +} + +static void cyberpro_set_interlace(struct cyberpro_vidinfo *dp) +{ + /* + * set interlace mode + */ + if (dp->interlace) { + dp->vfac3 |= VFAC_CTL3_CAP_INTERLACE; + dp->cap_miscctl &= ~CAP_CTL_MISC_ODDEVEN; + dp->ovl->src.height = dp->capt.height; + } else { + dp->vfac3 &= ~VFAC_CTL3_CAP_INTERLACE; + dp->cap_miscctl |= CAP_CTL_MISC_ODDEVEN; + dp->ovl->src.height = dp->capt.height / 2; + } + + cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); + cyberpro_grphw8(CAP_CTL_MISC, dp->cap_miscctl, dp); + + dp->ovl->set_src(dp, dp->ovl); + + if (dp->win_set) + dp->ovl->set_win(dp, dp->ovl); +} + +/* + * Calculate and set the address of the capture buffer. Note we + * also update the extended memory buffer for the overlay window. + * + * base: phys base address of display + * width: pixel width of display + * height: height of display + * depth: depth of display (8/16/24) + * bytesperline: number of bytes on a line + * + * We place the capture buffer 16K after the screen. + */ +static int +cyberpro_set_buffer(struct cyberpro_vidinfo *dp, struct video_buffer *b) +{ + unsigned long screensize, maxbufsz; + + if (b->height <= 0 || b->width <= 0 || b->bytesperline <= 0) + return -EINVAL; + + maxbufsz = dp->cap.maxwidth * dp->cap.maxheight * 2; + screensize = b->height * b->bytesperline + 16384; + + if ((screensize + maxbufsz) >= dp->info.fb_size) + return -EINVAL; + + dp->buf.base = b->base; + dp->buf.width = b->width; + dp->buf.height = b->height; + dp->buf.depth = b->depth; + dp->buf.bytesperline = b->bytesperline; + dp->cap_mem_offset = screensize >> 2; + + cyberpro_grphw24(CAP_MEM_START, dp->cap_mem_offset, dp); + + /* + * Setup the overlay source information. + */ + dp->ovl->src.offset = dp->cap_mem_offset; + dp->ovl->set_src(dp, dp->ovl); + + return 0; +} + +static void cyberpro_hw_init(struct cyberpro_vidinfo *dp) +{ + unsigned char old; + + /* + * Enable access to bus-master registers + */ + dp->info.enable_extregs(dp->info.info); + + dp->vfac1 = VFAC_CTL1_PHILIPS | + VFAC_CTL1_FREEZE_CAPTURE | + VFAC_CTL1_FREEZE_CAPTURE_SYNC; + dp->vfac3 = VFAC_CTL3_CAP_IRQ; + + dp->cap_miscctl = CAP_CTL_MISC_DISPUSED | + CAP_CTL_MISC_SYNCTZOR | + CAP_CTL_MISC_SYNCTZHIGH; + + /* + * Setup bus-master mode + */ + cyberpro_grphw8(BM_CTRL1, 0x88, dp); + cyberpro_grphw8(PCI_BM_CTL, PCI_BM_CTL_ENABLE, dp); + cyberpro_grphw8(BM_CTRL0, 0x44, dp); + cyberpro_grphw8(BM_CTRL1, 0x84, dp); + + cyberpro_grphw24(CAP_MEM_START, 0, dp); + + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); + cyberpro_grphw8(VFAC_CTL2, 0, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw8(EXT_TV_CTL, 0x80, dp); + + cyberpro_grphw8(EXT_CAP_CTL1, 0x3f, dp); /* disable PIP */ + cyberpro_grphw8(EXT_CAP_CTL2, 0xc0 | EXT_CAP_CTL2_ODDFRAMEIRQ, dp); + + /* + * Configure capture mode to match the + * external video processor format + */ + cyberpro_grphw8(EXT_CAP_MODE1, dp->cap_mode1, dp); + cyberpro_grphw8(EXT_CAP_MODE2, dp->cap_mode2, dp); + + /* setup overlay */ + cyberpro_grphw16(EXT_FIFO_CTL, 0x1010, dp); +// cyberpro_grphw16(EXT_FIFO_CTL, 0x1b0f, dp); + + /* + * Always reset the capture parameters on each open. + */ + dp->capt.x = 0; + dp->capt.y = 0; + dp->capt.width = dp->cap.maxwidth; + dp->capt.height = dp->cap.maxheight; + dp->capt.decimation = 0; + dp->capt.flags = 0; + + cyberpro_capture_set_win(dp); + + /* + * Enable VAFC + */ + old = cyberpro_grphr8(EXT_LATCH1, dp); + cyberpro_grphw8(EXT_LATCH1, old | EXT_LATCH1_VAFC_EN, dp); + + /* + * Enable capture (we hope that VSYNC=1) + */ + dp->vfac1 |= VFAC_CTL1_CAPTURE; + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + + /* + * The overlay source format is always the + * same as the capture stream format. + */ + dp->ovl->src.width = dp->capt.width; + dp->ovl->src.height = dp->capt.height; + dp->ovl->src.format = dp->stream_fmt; + + /* + * Initialise the overlay windows + */ + dp->ext.init(dp, &dp->ext); + dp->v2.init(dp, &dp->v2); + dp->x2.init(dp, &dp->x2); +} + +static void cyberpro_deinit(struct cyberpro_vidinfo *dp) +{ + unsigned char old; + + /* + * Stop any bus-master activity + */ + cyberpro_writew(0, BM_CONTROL, dp); + + /* + * Shut down overlay + */ + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 0); + dp->ovl_active = 0; + + /* + * Shut down capture + */ + if (dp->cap_active) + cyberpro_capture(dp, 0); + dp->cap_active = 0; + + /* + * Disable all capture + */ + cyberpro_grphw8(VFAC_CTL1, 0, dp); + + /* + * Disable VAFC + */ + old = cyberpro_grphr8(EXT_LATCH1, dp); + cyberpro_grphw8(EXT_LATCH1, old & ~EXT_LATCH1_VAFC_EN, dp); + + /* + * Disable interrupt (this allows it to float) + */ + dp->vfac3 &= ~VFAC_CTL3_CAP_IRQ; + cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); + + /* + * Switch off bus-master mode + */ + cyberpro_grphw8(PCI_BM_CTL, 0, dp); + + /* + * Disable access to bus-master registers + */ + dp->info.disable_extregs(dp->info.info); +} + +static int cyberpro_grabber_open(struct video_device *dev, int flags) +{ + struct cyberpro_vidinfo *dp = dev->priv; + int ret, one = 1; + + MOD_INC_USE_COUNT; + + ret = -EBUSY; + if (flags || dp->users) + goto out; + + dp->users += 1; + + if (dp->users == 1) { + ret = request_irq(dp->irq, cyberpro_interrupt, SA_SHIRQ, + dp->info.dev_name, dp); + + if (ret) { + dp->users -= 1; + goto out; + } + + /* + * Initialise the VGA chip + */ + cyberpro_hw_init(dp); + + /* + * Enable the IRQ. This allows the IRQ to work as expected + * even if the IRQ line is missing the pull-up resistor. + */ + enable_irq(dp->irq); + + i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &one); + } + + ret = 0; +out: + if (ret) + MOD_DEC_USE_COUNT; + return ret; +} + +static void cyberpro_grabber_close(struct video_device *dev) +{ + struct cyberpro_vidinfo *dp = dev->priv; + + if (dp->users == 1) { + int zero = 0; + + /* + * Disable the IRQ. This prevents problems with missing + * pull-up resistors on the PCI interrupt line. + */ + disable_irq(dp->irq); + + cyberpro_frames_free(dp); + + /* + * Turn off the SAA7111 decoder + */ + i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &zero); + + /* + * Disable grabber + */ + cyberpro_deinit(dp); + + free_irq(dp->irq, dp); + } + + dp->users -= 1; + + MOD_DEC_USE_COUNT; +} + +/* + * Our general plan here is: + * 1. Set the CyberPro to perform a BM-DMA of one frame to this memory + * 2. Copy the frame to the userspace + * + * However, BM-DMA seems to be unreliable at the moment, especially on + * rev. 4 NetWinders. + */ +static long +cyberpro_grabber_read(struct video_device *dev, char *buf, + unsigned long count, int noblock) +{ + struct cyberpro_vidinfo *dp = dev->priv; + int ret = -EINVAL; + +#ifdef USE_MMIO + unsigned long maxbufsz = dp->capt.width * dp->capt.height * 2; + char *disp = dp->info.fb + (dp->cap_mem_offset << 2); + + /* + * If the buffer is mmap()'d, we shouldn't be using read() + */ + if (dp->mmaped) + return -EINVAL; + + if (count > maxbufsz) + count = maxbufsz; + + if (dp->cap_active) + cyberpro_capture(dp, 0); + else + cyberpro_capture_one(dp); + + ret = (int)count; + if (copy_to_user(buf, disp, count)) + ret = -EFAULT; + + /* + * unfreeze capture + */ + if (dp->cap_active) + cyberpro_capture(dp, 1); +#endif + + return ret; +} + +/* + * We don't support writing to the grabber + * (In theory, we could allow writing to a separate region of VGA memory, + * and display this using the second overlay window. This would allow us + * to do video conferencing for example). + */ +static long +cyberpro_grabber_write(struct video_device *dev, const char *buf, + unsigned long count, int noblock) +{ + return -EINVAL; +} + +static int +cyberpro_grabber_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct cyberpro_vidinfo *dp = dev->priv; + + switch (cmd) { + case VIDIOCGCAP: + return copy_to_user(arg, &dp->cap, sizeof(dp->cap)) + ? -EFAULT : 0; + + case VIDIOCGCHAN: + { + struct video_channel chan; + + chan.channel = 0; + strcpy(chan.name, "Composite"); + chan.tuners = 0; + chan.flags = 0; + chan.type = VIDEO_TYPE_CAMERA; + chan.norm = dp->norm; + + return copy_to_user(arg, &chan, sizeof(chan)) ? -EFAULT : 0; + } + + case VIDIOCGPICT: + return copy_to_user(arg, &dp->pic, sizeof(dp->pic)) + ? -EINVAL : 0; + + case VIDIOCGWIN: + { + struct video_window win; + + win.x = dp->ovl->dst.x; + win.y = dp->ovl->dst.y; + win.width = dp->ovl->dst.width; + win.height = dp->ovl->dst.height; + win.chromakey = dp->ovl->dst.chromakey; + win.flags = VIDEO_WINDOW_CHROMAKEY | + (dp->interlace ? VIDEO_WINDOW_INTERLACE : 0); + win.clips = NULL; + win.clipcount = 0; + + return copy_to_user(arg, &win, sizeof(win)) + ? -EINVAL : 0; + } + + case VIDIOCGFBUF: + return copy_to_user(arg, &dp->buf, sizeof(dp->buf)) + ? -EINVAL : 0; + + case VIDIOCGUNIT: + { + struct video_unit unit; + + unit.video = dev->minor; + unit.vbi = VIDEO_NO_UNIT; + unit.radio = VIDEO_NO_UNIT; + unit.audio = VIDEO_NO_UNIT; + unit.teletext = VIDEO_NO_UNIT; + + return copy_to_user(arg, &unit, sizeof(unit)) + ? -EINVAL : 0; + } + + case VIDIOCGCAPTURE: + return copy_to_user(arg, &dp->capt, sizeof(dp->capt)) + ? -EFAULT : 0; + + case VIDIOCSCHAN: + { + struct video_decoder_capability vdc; + struct video_channel v; + int ok; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v.channel != 0) + return -EINVAL; + + i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_GET_CAPABILITIES, &vdc); + + switch (v.norm) { + case VIDEO_MODE_PAL: + ok = vdc.flags & VIDEO_DECODER_PAL; + break; + case VIDEO_MODE_NTSC: + ok = vdc.flags & VIDEO_DECODER_NTSC; + break; + case VIDEO_MODE_AUTO: + ok = vdc.flags & VIDEO_DECODER_AUTO; + break; + default: + ok = 0; + } + if (!ok) + return -EINVAL; + + dp->norm = v.norm; + + i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_SET_NORM, &v.norm); + + return 0; + } + + case VIDIOCSPICT: + { + struct video_picture p; + + if (copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + + if (p.palette != dp->stream_fmt || + p.depth != 8) + return -EINVAL; + + dp->pic = p; + + /* p.depth sets the capture depth */ + /* p.palette sets the capture palette */ + + i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_SET_PICTURE, &p); + + return 0; + } + + case VIDIOCSWIN: /* set the size & position of the overlay window */ + { + struct video_window w; + int diff; + + if (!dp->buf_set) + return -EINVAL; + + if (copy_from_user(&w, arg, sizeof(w))) + return -EFAULT; + + if (w.clipcount) + return -EINVAL; + + /* + * Bound the overlay window by the size of the screen + */ + if (w.x < 0) + w.x = 0; + if (w.y < 0) + w.y = 0; + + if (w.x > dp->buf.width) + w.x = dp->buf.width; + if (w.y > dp->buf.height) + w.y = dp->buf.height; + + if (w.width < dp->capt.width) + w.width = dp->capt.width; + if (w.height < dp->capt.height) + w.height = dp->capt.height; + + if (w.x + w.width > dp->buf.width) + w.width = dp->buf.width - w.x; + if (w.y + w.height > dp->buf.height) + w.height = dp->buf.height - w.y; + + /* + * We've tried to make the values fit, but + * they just won't. + */ + if (w.width < dp->capt.width || w.height < dp->capt.height) + return -EINVAL; + + diff = dp->ovl->dst.x != w.x || + dp->ovl->dst.y != w.y || + dp->ovl->dst.width != w.width || + dp->ovl->dst.height != w.height || + dp->ovl->dst.chromakey != w.chromakey || + dp->ovl->dst.flags != w.flags; + + if (!dp->win_set || diff) { + dp->ovl->dst.x = w.x; + dp->ovl->dst.y = w.y; + dp->ovl->dst.width = w.width; + dp->ovl->dst.height = w.height; + dp->ovl->dst.chromakey = w.chromakey; + dp->ovl->dst.flags = w.flags; + + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 0); + + dp->ovl->set_win(dp, dp->ovl); + + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 1); + + diff = w.flags & VIDEO_WINDOW_INTERLACE ? 1 : 0; + if (!dp->win_set || dp->interlace != diff) { + dp->interlace = diff; + cyberpro_set_interlace(dp); + } + } + + dp->win_set = 1; + + return 0; + } + + case VIDIOCSFBUF: /* set frame buffer info */ + { + struct video_buffer b; + int ret; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (dp->cap_active) + return -EINVAL; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + ret = cyberpro_set_buffer(dp, &b); + if (ret == 0) { + dp->buf_set = 1; + dp->win_set = 0; + } + + return ret; + } + + case VIDIOCCAPTURE: + { + int on; + + if (get_user(on, (int *)arg)) + return -EFAULT; + + if (( on && dp->ovl_active) || + (!on && !dp->ovl_active)) + return 0; + + if (on && (!dp->buf_set || !dp->win_set)) + return -EINVAL; + + cyberpro_capture(dp, on); + dp->cap_active = on; + dp->ovl->ctl(dp, dp->ovl, on); + dp->ovl_active = on; + + return 0; + } + +#ifdef USE_MMAP + case VIDIOCSYNC: + { + DECLARE_WAITQUEUE(wait, current); + int buf; + + /* + * The buffer must have been mmaped + * for this call to work. + */ + if (!dp->mmaped) + return -EINVAL; + + if (get_user(buf, (int *)arg)) + return -EFAULT; + + if (buf < 0 || buf >= NR_FRAMES) + return -EINVAL; + + switch (dp->frame[buf].status) { + case FRAME_FREE: + return -EINVAL; + + case FRAME_WAITING: + case FRAME_GRABBING: + add_wait_queue(&dp->frame_wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) + break; + if (dp->frame[buf].status == FRAME_DONE) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dp->frame_wait, &wait); + if (signal_pending(current)) + return -EINTR; + /*FALLTHROUGH*/ + case FRAME_DONE: + dp->frame[buf].status = FRAME_FREE; + break; + } + return 0; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vmap; + + /* + * The buffer must have been mmaped + * for this call to work. + */ + if (!dp->mmaped) + return -EINVAL; + + if (copy_from_user(&vmap, arg, sizeof(vmap))) + return -EFAULT; + + /* + * We can only capture in our source format/size. + */ + if (vmap.frame >= NR_FRAMES || + vmap.format != dp->stream_fmt || + vmap.width != dp->capt.width || + vmap.height != dp->capt.height) + return -EINVAL; + + if (dp->frame[vmap.frame].status == FRAME_WAITING || + dp->frame[vmap.frame].status == FRAME_GRABBING) + return -EBUSY; + + dp->frame[vmap.frame].status = FRAME_WAITING; + return 0; + } + + case VIDIOCGMBUF: + { + struct video_mbuf vmb; + unsigned int i; + + vmb.frames = NR_FRAMES; + vmb.size = dp->frame_size * NR_FRAMES; + + for (i = 0; i < NR_FRAMES; i++) + vmb.offsets[i] = dp->frame[i].offset; + + return copy_to_user(arg, &vmb, sizeof(vmb)) ? -EFAULT : 0; + } +#endif + + case VIDIOCSCAPTURE: + { + struct video_capture capt; + +#ifndef ALLOW_SCAPTURE_WHILE_CAP + if (dp->cap_active) + return -EINVAL; +#endif + + if (copy_from_user(&capt, arg, sizeof(capt))) + return -EFAULT; + + if (capt.x < 0 || capt.width < 0 || + capt.y < 0 || capt.height < 0 || + capt.x + capt.width > dp->cap.maxwidth || + capt.y + capt.height > dp->cap.maxheight) + return -EINVAL; + + /* + * The capture width must be a multiple of 4 + */ + if (dp->capt.width & 3) + return -EINVAL; + + dp->capt.x = capt.x; + dp->capt.y = capt.y; + dp->capt.width = capt.width; + dp->capt.height = capt.height; +#ifdef ALLOW_SCAPTURE_WHILE_CAP + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 0); + if (dp->cap_active) + cyberpro_capture(dp, 0); +#endif + cyberpro_capture_set_win(dp); + + /* + * Update the overlay window information + */ + dp->ovl->src.width = capt.width; + dp->ovl->src.height = capt.height; + + dp->ovl->set_src(dp, dp->ovl); + if (dp->win_set) + dp->ovl->set_win(dp, dp->ovl); + +#ifdef ALLOW_SCAPTURE_WHILE_CAP + if (dp->cap_active) + cyberpro_capture(dp, 1); + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 1); +#endif + return 0; + } + + case VIDIOCGTUNER: /* no tuner */ + case VIDIOCSTUNER: + return -EINVAL; + } + + return -EINVAL; +} + +#ifdef USE_MMAP +static int +cyberpro_grabber_mmap(struct video_device *dev, const char *addr, unsigned long size) +{ + struct cyberpro_vidinfo *dp = dev->priv; + unsigned long vaddr = (unsigned long)addr; + pgprot_t prot; + int frame_idx, ret = -EINVAL; + +#if defined(__arm__) + prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_USER | L_PTE_WRITE | L_PTE_DIRTY); +#elif defined(__i386__) + prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED); + if (boot_cpu_data.x86 > 3) + pgprot_val(prot) |= _PAGE_PCD; +#else +#error "Unsupported architecture" +#endif + + /* + * The mmap() request must have the correct size. + */ + if (size != NR_FRAMES * dp->frame_size) + goto out; + + /* + * If it's already mapped, don't re-do + */ + if (dp->mmaped) + goto out; + dp->mmaped = 1; + + /* + * Map in each frame + */ + for (frame_idx = 0; frame_idx < NR_FRAMES; frame_idx++) { + struct framebuf *frame; + int pgidx; + + frame = dp->frame + frame_idx; + + ret = cyberpro_alloc_frame_buffer(dp, frame); + + /* + * If an error occurs, we can be lazy and leave what we've + * been able to do. Our release function will free any + * allocated buffers, and do_mmap_pgoff() will zap any + * inserted mappings. + */ + if (ret) + goto out2; + + /* + * Map in each page on a page by page basis. This is just + * a little on the inefficient side, but it's only run once. + */ + for (pgidx = 0; pgidx < NR_PAGES; pgidx++) { + unsigned long virt; + + virt = page_address(frame->pages[pgidx]); + + ret = remap_page_range(vaddr, virt_to_phys((void *)virt), + PAGE_SIZE, prot); + + if (ret) + goto out2; + + vaddr += PAGE_SIZE; + } + } + + out2: + if (ret) + dp->mmaped = 0; + out: + return ret; +} +#endif + +static int __init cyberpro_grabber_init_done(struct video_device *dev) +{ + struct cyberpro_vidinfo *dp; + struct cyberpro_info *info = dev->priv; + int ret; + + dp = kmalloc(sizeof(*dp), GFP_KERNEL); + if (!dp) + return -ENOMEM; + + memset(dp, 0, sizeof(*dp)); + + dev->priv = dp; + dp->info = *info; + dp->dev = dev; + dp->bus = &cyberpro_i2c_bus; + dp->regs = info->regs; + dp->irq = info->dev->irq; + + strcpy(dp->cap.name, dev->name); + dp->cap.type = dev->type; + dp->cap.channels = 1; + dp->cap.audios = 0; + dp->cap.minwidth = 32; + dp->cap.maxwidth = 716; + dp->cap.minheight = 32; + dp->cap.maxheight = 576; + + dp->pic.brightness = 32768; + dp->pic.hue = 32768; + dp->pic.colour = 32768; + dp->pic.contrast = 32768; + dp->pic.whiteness = 0; + dp->pic.depth = 8; + dp->pic.palette = VIDEO_PALETTE_YUV422; + + /* dp->buf is setup by the user */ + /* dp->cap_mem_offset setup by dp->buf */ + + dp->norm = VIDEO_MODE_AUTO; + + /* + * The extended overlay window + */ + dp->ext.init = cyberpro_ext_init; + dp->ext.set_src = cyberpro_ext_set_src; + dp->ext.set_win = cyberpro_ext_set_win; + dp->ext.ctl = cyberpro_ext_ctl; + + /* + * The V2 overlay window + */ + dp->v2.init = cyberpro_v2_init; + dp->v2.set_src = cyberpro_v2_set_src; + dp->v2.set_win = cyberpro_v2_set_win; + dp->v2.ctl = cyberpro_v2_ctl; + + /* + * The X2 overlay window + */ + dp->x2.init = cyberpro_x2_init; + dp->x2.set_src = cyberpro_x2_set_src; + dp->x2.set_win = cyberpro_x2_set_win; + dp->x2.ctl = cyberpro_x2_ctl; + + /* + * Set the overlay window which we shall be using + */ + dp->ovl = &dp->ext; + + dp->cap_mode1 = EXT_CAP_MODE1_ALTFIFO; + + /* + * Initialise hardware specific values. + * - CCIR656 8bit mode (YUV422 data) + * - Ignore Hgood signal + * - Invert Odd/Even field signal + */ + dp->cap_mode1 |= EXT_CAP_MODE1_CCIR656 | EXT_CAP_MODE1_8BIT; + dp->cap_mode2 = EXT_CAP_MODE2_FIXSONY | EXT_CAP_MODE2_DATEND | + EXT_CAP_MODE2_CCIRINVOE; + dp->stream_fmt = VIDEO_PALETTE_YUV422; + + + init_waitqueue_head(&dp->vbl_wait); + cyberpro_frames_init(dp); + + /* + * wake up the decoder + */ + decoder_sleep(0); + + dp->bus->data = dp; + strncpy(dp->bus->name, dev->name, sizeof(dp->bus->name)); + + pci_set_master(dp->info.dev); + + ret = i2c_register_bus(dp->bus); + + /* + * If we successfully registered the bus, but didn't initialise + * the decoder (because its driver is not present), request + * that it is loaded. + */ + if (ret == 0 && !dp->decoder) + request_module("saa7111"); + + /* + * If that didn't work, then we're out of luck. + */ + if (ret == 0 && !dp->decoder) { + i2c_unregister_bus(dp->bus); + ret = -ENXIO; + } + + if (ret) { + kfree(dp); + + /* + * put the decoder back to sleep + */ + decoder_sleep(1); + } + + return ret; +} + +static struct video_device cyberpro_grabber = { + name: "", + type: VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | + VID_TYPE_CHROMAKEY | VID_TYPE_SCALES | + VID_TYPE_SUBCAPTURE, + hardware: 0, + open: cyberpro_grabber_open, + close: cyberpro_grabber_close, + read: cyberpro_grabber_read, + write: cyberpro_grabber_write, + ioctl: cyberpro_grabber_ioctl, +#ifdef USE_MMAP + mmap: cyberpro_grabber_mmap, +#endif + initialize: cyberpro_grabber_init_done, +}; + +int init_cyber2000fb_viddev(void) +{ + struct cyberpro_info info; + + if (!cyber2000fb_attach(&info, 0)) + return -ENXIO; + + strncpy(cyberpro_grabber.name, info.dev_name, sizeof(cyberpro_grabber.name)); + + cyberpro_grabber.priv = &info; + + return video_register_device(&cyberpro_grabber, VFL_TYPE_GRABBER, -1); +} + +/* + * This can be cleaned up when the SAA7111 code is fixed. + */ +#ifdef MODULE +static int __init cyberpro_init(void) +{ + disable_irq(35); + return init_cyber2000fb_viddev(); +} + +static void __exit cyberpro_exit(void) +{ + video_unregister_device(&cyberpro_grabber); + kfree(cyberpro_grabber.priv); + i2c_unregister_bus(&cyberpro_i2c_bus); + + /* + * put the decoder back to sleep + */ + decoder_sleep(1); + + cyber2000fb_detach(0); +} + +module_init(cyberpro_init); +module_exit(cyberpro_exit); +#endif --- linux-2.4.27/drivers/media/video/i2c-old.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/media/video/i2c-old.c @@ -36,11 +36,20 @@ static struct i2c_driver *drivers[I2C_DRIVER_MAX]; static int bus_count = 0, driver_count = 0; +extern int saa7111_init(void); +extern int saa7185_init(void); +extern int bt819_init(void); +extern int bt856_init(void); + int i2c_init(void) { printk(KERN_INFO "i2c: initialized%s\n", scan ? " (i2c bus scan enabled)" : ""); +#if defined(CONFIG_VIDEO_CYBERPRO) + saa7111_init(); +#endif + return 0; } @@ -52,10 +61,10 @@ int i,j,ack=1; unsigned char addr; LOCK_FLAGS; - + /* probe for device */ LOCK_I2C_BUS(bus); - for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) + for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) { i2c_start(bus); ack = i2c_sendbyte(bus,addr,0); @@ -87,8 +96,8 @@ device->addr = addr; /* Attach */ - - if (driver->attach(device)!=0) + + if (driver->attach(device)!=0) { kfree(device); return; @@ -114,7 +123,7 @@ for (i = 0; i < I2C_DEVICE_MAX; i++) if (device == device->driver->devices[i]) break; - if (I2C_DEVICE_MAX == i) + if (I2C_DEVICE_MAX == i) { printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", device->name); @@ -126,7 +135,7 @@ for (i = 0; i < I2C_DEVICE_MAX; i++) if (device == device->bus->devices[i]) break; - if (I2C_DEVICE_MAX == i) + if (I2C_DEVICE_MAX == i) { printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", device->name); @@ -158,19 +167,19 @@ busses[i] = bus; bus_count++; REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); - + MOD_INC_USE_COUNT; - if (scan) + if (scan) { /* scan whole i2c bus */ LOCK_I2C_BUS(bus); - for (i = 0; i < 256; i+=2) + for (i = 0; i < 256; i+=2) { i2c_start(bus); ack = i2c_sendbyte(bus,i,0); i2c_stop(bus); - if (!ack) + if (!ack) { printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", bus->name,i); @@ -198,20 +207,20 @@ for (i = 0; i < I2C_BUS_MAX; i++) if (bus == busses[i]) break; - if (I2C_BUS_MAX == i) + if (I2C_BUS_MAX == i) { printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", bus->name); return -ENODEV; } - + MOD_DEC_USE_COUNT; - + busses[i] = NULL; bus_count--; REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); - return 0; + return 0; } /* ----------------------------------------------------------------------- */ @@ -231,9 +240,9 @@ drivers[i] = driver; driver_count++; - + MOD_INC_USE_COUNT; - + REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); /* Probe available busses */ @@ -256,7 +265,7 @@ for (i = 0; i < I2C_DRIVER_MAX; i++) if (driver == drivers[i]) break; - if (I2C_DRIVER_MAX == i) + if (I2C_DRIVER_MAX == i) { printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", driver->name); @@ -264,7 +273,7 @@ } MOD_DEC_USE_COUNT; - + drivers[i] = NULL; driver_count--; REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); @@ -328,7 +337,7 @@ int i2c_ack(struct i2c_bus *bus) { int ack; - + I2C_SET(bus,0,1); I2C_SET(bus,1,1); ack = I2C_GET(bus); @@ -339,7 +348,7 @@ int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) { int i, ack; - + I2C_SET(bus,0,0); for (i=7; i>=0; i--) (data&(1<=0; i--) + for (i=7; i>=0; i--) { I2C_SET(bus,1,1); if (I2C_GET(bus)) @@ -373,7 +382,7 @@ int i2c_read(struct i2c_bus *bus, unsigned char addr) { int ret; - + if (bus->i2c_read) return bus->i2c_read(bus, addr); --- linux-2.4.27/drivers/media/video/saa7111.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/media/video/saa7111.c @@ -20,9 +20,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include +#include #include +#include #include #include #include @@ -149,7 +149,11 @@ 0x0d, 0x00, /* 0d - HUE=0 */ 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ 0x0f, 0x00, /* 0f - reserved */ +#ifndef CONFIG_ARCH_NETWINDER 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ +#else + 0x10, 0xc8, /* 10 - OFTS=YUV-CCIR656, HDEL=0, VLRN=1, YDEL=0 */ +#endif 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ 0x12, 0x00, /* 12 - output control 2 */ 0x13, 0x00, /* 13 - output control 3 */ --- linux-2.4.27/drivers/message/i2o/i2o_core.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/message/i2o/i2o_core.c @@ -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); --- linux-2.4.27/drivers/message/i2o/i2o_pci.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/message/i2o/i2o_pci.c @@ -390,4 +390,4 @@ MODULE_PARM_DESC(dpt, "Set this if you want to drive DPT cards normally handled by dpt_i2o"); module_init(i2o_pci_core_attach); module_exit(i2o_pci_core_detach); - \ No newline at end of file + --- linux-2.4.27/drivers/misc/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/misc/Config.in @@ -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 --- linux-2.4.27/drivers/misc/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/misc/Makefile @@ -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: --- /dev/null +++ linux-2.4.27/drivers/misc/mcp-core.c @@ -0,0 +1,155 @@ +/* + * linux/drivers/misc/mcp-core.c + * + * Copyright (C) 2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * Generic MCP (Multimedia Communications Port) layer. All MCP locking + * is solely held within this file. + */ +#include +#include +#include +#include + +#include +#include + +#include "mcp.h" + +/** + * mcp_set_telecom_divisor - set the telecom divisor + * @mcp: MCP interface structure + * @div: SIB clock divisor + * + * Set the telecom divisor on the MCP interface. The resulting + * sample rate is SIBCLOCK/div. + */ +void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div) +{ + spin_lock_irq(&mcp->lock); + mcp->set_telecom_divisor(mcp, div); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_set_audio_divisor - set the audio divisor + * @mcp: MCP interface structure + * @div: SIB clock divisor + * + * Set the audio divisor on the MCP interface. + */ +void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div) +{ + spin_lock_irq(&mcp->lock); + mcp->set_audio_divisor(mcp, div); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_reg_write - write a device register + * @mcp: MCP interface structure + * @reg: 4-bit register index + * @val: 16-bit data value + * + * Write a device register. The MCP interface must be enabled + * to prevent this function hanging. + */ +void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); + mcp->reg_write(mcp, reg, val); + spin_unlock_irqrestore(&mcp->lock, flags); +} + +/** + * mcp_reg_read - read a device register + * @mcp: MCP interface structure + * @reg: 4-bit register index + * + * Read a device register and return its value. The MCP interface + * must be enabled to prevent this function hanging. + */ +unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&mcp->lock, flags); + val = mcp->reg_read(mcp, reg); + spin_unlock_irqrestore(&mcp->lock, flags); + + return val; +} + +/** + * mcp_enable - enable the MCP interface + * @mcp: MCP interface to enable + * + * Enable the MCP interface. Each call to mcp_enable will need + * a corresponding call to mcp_disable to disable the interface. + */ +void mcp_enable(struct mcp *mcp) +{ + spin_lock_irq(&mcp->lock); + if (mcp->use_count++ == 0) + mcp->enable(mcp); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_disable - disable the MCP interface + * @mcp: MCP interface to disable + * + * Disable the MCP interface. The MCP interface will only be + * disabled once the number of calls to mcp_enable matches the + * number of calls to mcp_disable. + */ +void mcp_disable(struct mcp *mcp) +{ + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); + if (--mcp->use_count == 0) + mcp->disable(mcp); + spin_unlock_irqrestore(&mcp->lock, flags); +} + + +/* + * This needs re-working + */ +static struct mcp *mcp_if; + +struct mcp *mcp_get(void) +{ + return mcp_if; +} + +int mcp_register(struct mcp *mcp) +{ + if (mcp_if) + return -EBUSY; + if (mcp->owner) + __MOD_INC_USE_COUNT(mcp->owner); + mcp_if = mcp; + return 0; +} + +EXPORT_SYMBOL(mcp_set_telecom_divisor); +EXPORT_SYMBOL(mcp_set_audio_divisor); +EXPORT_SYMBOL(mcp_reg_write); +EXPORT_SYMBOL(mcp_reg_read); +EXPORT_SYMBOL(mcp_enable); +EXPORT_SYMBOL(mcp_disable); +EXPORT_SYMBOL(mcp_get); +EXPORT_SYMBOL(mcp_register); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Core multimedia communications port driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/misc/mcp-sa1100.c @@ -0,0 +1,180 @@ +/* + * linux/drivers/misc/mcp-sa1100.c + * + * Copyright (C) 2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * SA1100 MCP (Multimedia Communications Port) driver. + * + * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mcp.h" + +static void +mcp_sa1100_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x00007f00; + mccr0 |= divisor << 8; + Ser4MCCR0 = mccr0; +} + +static void +mcp_sa1100_set_audio_divisor(struct mcp *mcp, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x0000007f; + mccr0 |= divisor; + Ser4MCCR0 = mccr0; +} + +/* + * Write data to the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static void +mcp_sa1100_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + int ret = -ETIME; + int i; + + Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); + + for (i = 0; i < 2; i++) { + udelay(mcp->rw_timeout); + if (Ser4MCSR & MCSR_CWC) { + ret = 0; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: write timed out\n"); +} + +/* + * Read data from the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static unsigned int +mcp_sa1100_read(struct mcp *mcp, unsigned int reg) +{ + int ret = -ETIME; + int i; + + Ser4MCDR2 = reg << 17 | MCDR2_Rd; + + for (i = 0; i < 2; i++) { + udelay(mcp->rw_timeout); + if (Ser4MCSR & MCSR_CRC) { + ret = Ser4MCDR2 & 0xffff; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: read timed out\n"); + + return ret; +} + +static void mcp_sa1100_enable(struct mcp *mcp) +{ + Ser4MCSR = -1; + Ser4MCCR0 |= MCCR0_MCE; +} + +static void mcp_sa1100_disable(struct mcp *mcp) +{ + Ser4MCCR0 &= ~MCCR0_MCE; +} + +struct mcp mcp_sa1100 = { + owner: THIS_MODULE, + lock: SPIN_LOCK_UNLOCKED, + sclk_rate: 11981000, + dma_audio_rd: DMA_Ser4MCP0Rd, + dma_audio_wr: DMA_Ser4MCP0Wr, + dma_telco_rd: DMA_Ser4MCP1Rd, + dma_telco_wr: DMA_Ser4MCP1Wr, + set_telecom_divisor: mcp_sa1100_set_telecom_divisor, + set_audio_divisor: mcp_sa1100_set_audio_divisor, + reg_write: mcp_sa1100_write, + reg_read: mcp_sa1100_read, + enable: mcp_sa1100_enable, + disable: mcp_sa1100_disable, +}; + +/* + * This needs re-working + */ +static int mcp_sa1100_init(void) +{ + struct mcp *mcp = &mcp_sa1100; + int ret = -ENODEV; + + if (machine_is_accelent_sa() || + machine_is_adsbitsy() || machine_is_assabet() || + machine_is_cerf() || machine_is_flexanet() || + machine_is_freebird() || machine_is_graphicsclient() || + machine_is_graphicsmaster() || machine_is_lart() || + machine_is_omnimeter() || machine_is_pfs168() || + machine_is_shannon() || machine_is_simpad() || + machine_is_simputer() || machine_is_yopy()) { + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + + Ser4MCSR = -1; + Ser4MCCR1 = 0; + Ser4MCCR0 = 0x00007f7f | MCCR0_ADM; + + /* + * Calculate the read/write timeout (us) from the bit clock + * rate. This is the period for 3 64-bit frames. Always + * round this time up. + */ + mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / + mcp->sclk_rate; + + ret = mcp_register(mcp); + } + + return ret; +} + +module_init(mcp_sa1100_init); +EXPORT_SYMBOL(mcp_sa1100_init); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/misc/mcp.h @@ -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 --- /dev/null +++ linux-2.4.27/drivers/misc/ucb1x00-audio.c @@ -0,0 +1,378 @@ +/* + * linux/drivers/misc/ucb1x00-audio.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ucb1x00.h" + +#include "../drivers/sound/sa1100-audio.h" + +#define MAGIC 0x41544154 + +struct ucb1x00_audio { + struct file_operations fops; + struct file_operations mops; + struct ucb1x00 *ucb; + audio_stream_t output_stream; + audio_stream_t input_stream; + audio_state_t state; + unsigned int rate; + int dev_id; + int mix_id; + unsigned int daa_oh_bit; + unsigned int telecom; + unsigned int magic; + unsigned int ctrl_a; + unsigned int ctrl_b; + + /* mixer info */ + unsigned int mod_cnt; + unsigned short output_level; + unsigned short input_level; +}; + +#define REC_MASK (SOUND_MASK_VOLUME | SOUND_MASK_MIC) +#define DEV_MASK REC_MASK + +static int +ucb1x00_mixer_ioctl(struct inode *ino, struct file *filp, uint cmd, ulong arg) +{ + struct ucb1x00_audio *ucba; + unsigned int val, gain; + int ret = 0; + + ucba = list_entry(filp->f_op, struct ucb1x00_audio, mops); + + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + if (cmd == SOUND_MIXER_INFO) { + struct mixer_info mi; + + strncpy(mi.id, "UCB1x00", sizeof(mi.id)); + strncpy(mi.name, "Philips UCB1x00", sizeof(mi.name)); + mi.modify_counter = ucba->mod_cnt; + return copy_to_user((void *)arg, &mi, sizeof(mi)) ? -EFAULT : 0; + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + unsigned int left, right; + + ret = get_user(val, (unsigned int *)arg); + if (ret) + goto out; + + left = val & 255; + right = val >> 8; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + gain = (left + right) / 2; + + ret = -EINVAL; + if (!ucba->telecom) { + switch(_IOC_NR(cmd)) { + case SOUND_MIXER_VOLUME: + ucba->output_level = gain | gain << 8; + ucba->mod_cnt++; + ucba->ctrl_b = (ucba->ctrl_b & 0xff00) | + ((gain * 31) / 100); + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, + ucba->ctrl_b); + ret = 0; + break; + + case SOUND_MIXER_MIC: + ucba->input_level = gain | gain << 8; + ucba->mod_cnt++; + ucba->ctrl_a = (ucba->ctrl_a & 0x7f) | + (((gain * 31) / 100) << 7); + ucb1x00_reg_write(ucba->ucb, UCB_AC_A, + ucba->ctrl_a); + ret = 0; + break; + } + } + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_VOLUME: + val = ucba->output_level; + break; + + case SOUND_MIXER_MIC: + val = ucba->input_level; + break; + + case SOUND_MIXER_RECSRC: + case SOUND_MIXER_RECMASK: + val = ucba->telecom ? 0 : REC_MASK; + break; + + case SOUND_MIXER_DEVMASK: + val = ucba->telecom ? 0 : DEV_MASK; + break; + + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + + default: + val = 0; + ret = -EINVAL; + break; + } + + if (ret == 0) + ret = put_user(val, (int *)arg); + } + out: + return ret; +} + +static int ucb1x00_audio_setrate(struct ucb1x00_audio *ucba, int rate) +{ + unsigned int div_rate = ucb1x00_clkrate(ucba->ucb) / 32; + unsigned int div; + + div = (div_rate + (rate / 2)) / rate; + if (div < 6) + div = 6; + if (div > 127) + div = 127; + + ucba->ctrl_a = (ucba->ctrl_a & ~0x7f) | div; + + if (ucba->telecom) { + ucb1x00_reg_write(ucba->ucb, UCB_TC_B, 0); + ucb1x00_set_telecom_divisor(ucba->ucb, div * 32); + ucb1x00_reg_write(ucba->ucb, UCB_TC_A, ucba->ctrl_a); + ucb1x00_reg_write(ucba->ucb, UCB_TC_B, ucba->ctrl_b); + } else { + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, 0); + ucb1x00_set_audio_divisor(ucba->ucb, div * 32); + ucb1x00_reg_write(ucba->ucb, UCB_AC_A, ucba->ctrl_a); + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, ucba->ctrl_b); + } + + ucba->rate = div_rate / div; + + return ucba->rate; +} + +static int ucb1x00_audio_getrate(struct ucb1x00_audio *ucba) +{ + return ucba->rate; +} + +static void ucb1x00_audio_startup(void *data) +{ + struct ucb1x00_audio *ucba = data; + + ucb1x00_enable(ucba->ucb); + ucb1x00_audio_setrate(ucba, ucba->rate); + + ucb1x00_reg_write(ucba->ucb, UCB_MODE, UCB_MODE_DYN_VFLAG_ENA); + + /* + * Take off-hook + */ + if (ucba->daa_oh_bit) + ucb1x00_io_write(ucba->ucb, 0, ucba->daa_oh_bit); +} + +static void ucb1x00_audio_shutdown(void *data) +{ + struct ucb1x00_audio *ucba = data; + + /* + * Place on-hook + */ + if (ucba->daa_oh_bit) + ucb1x00_io_write(ucba->ucb, ucba->daa_oh_bit, 0); + + ucb1x00_reg_write(ucba->ucb, ucba->telecom ? UCB_TC_B : UCB_AC_B, 0); + ucb1x00_disable(ucba->ucb); +} + +static int +ucb1x00_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + struct ucb1x00_audio *ucba; + int val, ret = 0; + + ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); + + /* + * Make sure we have our magic number + */ + if (ucba->magic != MAGIC) + return -ENODEV; + + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + if (val != 0) + return -EINVAL; + val = 0; + break; + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + val = 1; + break; + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + val = ucb1x00_audio_setrate(ucba, val); + break; + + case SOUND_PCM_READ_RATE: + val = ucb1x00_audio_getrate(ucba); + break; + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + val = AFMT_S16_LE; + break; + + default: + return ucb1x00_mixer_ioctl(inode, file, cmd, arg); + } + + return put_user(val, (int *)arg); +} + +static int ucb1x00_audio_open(struct inode *inode, struct file *file) +{ + struct ucb1x00_audio *ucba; + + ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); + + return sa1100_audio_attach(inode, file, &ucba->state); +} + +static struct ucb1x00_audio *ucb1x00_audio_alloc(struct ucb1x00 *ucb) +{ + struct ucb1x00_audio *ucba; + + ucba = kmalloc(sizeof(*ucba), GFP_KERNEL); + if (ucba) { + memset(ucba, 0, sizeof(*ucba)); + + ucba->magic = MAGIC; + ucba->ucb = ucb; + ucba->fops.owner = THIS_MODULE; + ucba->fops.open = ucb1x00_audio_open; + ucba->mops.owner = THIS_MODULE; + ucba->mops.ioctl = ucb1x00_mixer_ioctl; + ucba->state.output_stream = &ucba->output_stream; + ucba->state.input_stream = &ucba->input_stream; + ucba->state.data = ucba; + ucba->state.hw_init = ucb1x00_audio_startup; + ucba->state.hw_shutdown = ucb1x00_audio_shutdown; + ucba->state.client_ioctl = ucb1x00_audio_ioctl; + + /* There is a bug in the StrongARM causes corrupt MCP data to be sent to + * the codec when the FIFOs are empty and writes are made to the OS timer + * match register 0. To avoid this we must make sure that data is always + * sent to the codec. + */ + ucba->state.need_tx_for_rx = 1; + + init_MUTEX(&ucba->state.sem); + ucba->rate = 8000; + } + return ucba; +} + +static struct ucb1x00_audio *audio, *telecom; + +static int __init ucb1x00_audio_init(void) +{ + struct ucb1x00 *ucb = ucb1x00_get(); + struct ucb1x00_audio *a; + + if (!ucb) + return -ENODEV; + + a = ucb1x00_audio_alloc(ucb); + if (a) { + a->state.input_dma = ucb->mcp->dma_audio_rd; + a->state.input_id = "UCB1x00 audio in"; + a->state.output_dma = ucb->mcp->dma_audio_wr; + a->state.output_id = "UCB1x00 audio out"; + a->dev_id = register_sound_dsp(&a->fops, -1); + a->mix_id = register_sound_mixer(&a->mops, -1); + a->ctrl_a = 0; + a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA; + audio = a; + } + + a = ucb1x00_audio_alloc(ucb); + if (a) { +#if 0 + a->daa_oh_bit = UCB_IO_8; + + ucb1x00_enable(ucb); + ucb1x00_io_write(ucb, a->daa_oh_bit, 0); + ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit); + ucb1x00_disable(ucb); +#endif + + a->telecom = 1; + a->state.input_dma = ucb->mcp->dma_telco_rd; + a->state.input_id = "UCB1x00 telco in"; + a->state.output_dma = ucb->mcp->dma_telco_wr; + a->state.output_id = "UCB1x00 telco out"; + a->dev_id = register_sound_dsp(&a->fops, -1); + a->mix_id = register_sound_mixer(&a->mops, -1); + a->ctrl_a = 0; + a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA; + telecom = a; + } + + return 0; +} + +static void __exit ucb1x00_audio_exit(void) +{ + unregister_sound_dsp(telecom->dev_id); + unregister_sound_dsp(audio->dev_id); + unregister_sound_mixer(telecom->mix_id); + unregister_sound_mixer(audio->mix_id); +} + +module_init(ucb1x00_audio_init); +module_exit(ucb1x00_audio_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 telecom/audio driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/misc/ucb1x00-core.c @@ -0,0 +1,651 @@ +/* + * linux/drivers/misc/ucb1x00-core.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * The UCB1x00 core driver provides basic services for handling IO, + * the ADC, interrupts, and accessing registers. It is designed + * such that everything goes through this layer, thereby providing + * a consistent locking methodology, as well as allowing the drivers + * to be used on other non-MCP-enabled hardware platforms. + * + * Note that all locks are private to this file. Nothing else may + * touch them. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ucb1x00.h" + +/** + * ucb1x00_io_set_dir - set IO direction + * @ucb: UCB1x00 structure describing chip + * @in: bitfield of IO pins to be set as inputs + * @out: bitfield of IO pins to be set as outputs + * + * Set the IO direction of the ten general purpose IO pins on + * the UCB1x00 chip. The @in bitfield has priority over the + * @out bitfield, in that if you specify a pin as both input + * and output, it will end up as an input. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_dir |= out; + ucb->io_dir &= ~in; + + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +/** + * ucb1x00_io_write - set or clear IO outputs + * @ucb: UCB1x00 structure describing chip + * @set: bitfield of IO pins to set to logic '1' + * @clear: bitfield of IO pins to set to logic '0' + * + * Set the IO output state of the specified IO pins. The value + * is retained if the pins are subsequently configured as inputs. + * The @clear bitfield has priority over the @set bitfield - + * outputs will be cleared. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_out |= set; + ucb->io_out &= ~clear; + + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +/** + * ucb1x00_io_read - read the current state of the IO pins + * @ucb: UCB1x00 structure describing chip + * + * Return a bitfield describing the logic state of the ten + * general purpose IO pins. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function does not take any semaphores or spinlocks. + */ +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) +{ + return ucb1x00_reg_read(ucb, UCB_IO_DATA); +} + +/* + * UCB1300 data sheet says we must: + * 1. enable ADC => 5us (including reference startup time) + * 2. select input => 51*tsibclk => 4.3us + * 3. start conversion => 102*tsibclk => 8.5us + * (tsibclk = 1/11981000) + * Period between SIB 128-bit frames = 10.7us + */ + +/** + * ucb1x00_adc_enable - enable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. + * Any code wishing to use the ADC converter must call this + * function prior to using it. + * + * This function takes the ADC semaphore to prevent two or more + * concurrent uses, and therefore may sleep. As a result, it + * can only be called from process context, not interrupt + * context. + * + * You should release the ADC as soon as possible using + * ucb1x00_adc_disable. + */ +void ucb1x00_adc_enable(struct ucb1x00 *ucb) +{ + down(&ucb->adc_sem); + + ucb->adc_cr |= UCB_ADC_ENA; + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); +} + +/** + * ucb1x00_adc_read - read the specified ADC channel + * @ucb: UCB1x00 structure describing chip + * @adc_channel: ADC channel mask + * @sync: wait for syncronisation pulse. + * + * Start an ADC conversion and wait for the result. Note that + * synchronised ADC conversions (via the ADCSYNC pin) must wait + * until the trigger is asserted and the conversion is finished. + * + * This function currently spins waiting for the conversion to + * complete (2 frames max without sync). + * + * If called for a synchronised ADC conversion, it may sleep + * with the ADC semaphore held. + */ +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) +{ + unsigned int val; + + if (sync) + adc_channel |= UCB_ADC_SYNC_ENA; + + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); + + for (;;) { + val = ucb1x00_reg_read(ucb, UCB_ADC_DATA); + if (val & UCB_ADC_DAT_VAL) + break; + /* yield to other processes */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + + return UCB_ADC_DAT(val); +} + +/** + * ucb1x00_adc_disable - disable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Disable the ADC converter and release the ADC semaphore. + */ +void ucb1x00_adc_disable(struct ucb1x00 *ucb) +{ + ucb->adc_cr &= ~UCB_ADC_ENA; + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); + ucb1x00_disable(ucb); + + up(&ucb->adc_sem); +} + +#ifdef CONFIG_PM +static int ucb1x00_pm (struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct ucb1x00 *ucb = (struct ucb1x00 *)dev->data; + unsigned int isr; + + if (rqst == PM_RESUME) { + ucb1x00_enable(ucb); + isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + ucb1x00_disable(ucb); + } + + return 0; +} +#endif + +/* + * UCB1x00 Interrupt handling. + * + * The UCB1x00 can generate interrupts when the SIBCLK is stopped. + * Since we need to read an internal register, we must re-enable + * SIBCLK to talk to the chip. We leave the clock running until + * we have finished processing all interrupts from the chip. + */ +static void ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs) +{ + struct ucb1x00 *ucb = devid; + struct ucb1x00_irq *irq; + unsigned int isr, i; + + ucb1x00_enable(ucb); + isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) + if (isr & 1 && irq->fn) + irq->fn(i, irq->devid); + ucb1x00_disable(ucb); +} + +/** + * ucb1x00_hook_irq - hook a UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @fn: function to call when interrupt is triggered + * @devid: device id to pass to interrupt handler + * + * Hook the specified interrupt. You can only register one handler + * for each interrupt source. The interrupt source is not enabled + * by this function; use ucb1x00_enable_irq instead. + * + * Interrupt handlers will be called with other interrupts enabled. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -EBUSY if the interrupt has already been hooked + */ +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) +{ + struct ucb1x00_irq *irq; + int ret = -EINVAL; + + if (idx < 16) { + irq = ucb->irq_handler + idx; + ret = -EBUSY; + + spin_lock_irq(&ucb->lock); + if (irq->fn == NULL) { + irq->devid = devid; + irq->fn = fn; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + } + return ret; +} + +/** + * ucb1x00_enable_irq - enable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @edges: interrupt edges to enable + * + * Enable the specified interrupt to trigger on %UCB_RISING, + * %UCB_FALLING or both edges. The interrupt should have been + * hooked by ucb1x00_hook_irq. + */ +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + ucb1x00_enable(ucb); + if (edges & UCB_RISING) { + ucb->irq_ris_enbl |= 1 << idx; + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl |= 1 << idx; + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + } + ucb1x00_disable(ucb); + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/** + * ucb1x00_disable_irq - disable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @edges: interrupt edges to disable + * + * Disable the specified interrupt triggering on the specified + * (%UCB_RISING, %UCB_FALLING or both) edges. + */ +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + ucb1x00_enable(ucb); + if (edges & UCB_RISING) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl &= ~(1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + } + ucb1x00_disable(ucb); + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/** + * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @devid: device id. + * + * Disable the interrupt source and remove the handler. devid must + * match the devid passed when hooking the interrupt. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -ENOENT if devid does not match + */ +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) +{ + struct ucb1x00_irq *irq; + int ret; + + if (idx >= 16) + goto bad; + + irq = ucb->irq_handler + idx; + ret = -ENOENT; + + spin_lock_irq(&ucb->lock); + if (irq->devid == devid) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb->irq_fal_enbl &= ~(1 << idx); + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + ucb1x00_disable(ucb); + + irq->fn = NULL; + irq->devid = NULL; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + return ret; + +bad: + printk(KERN_ERR "%s: freeing bad irq %d\n", __FUNCTION__, idx); + return -EINVAL; +} + +/* + * Try to probe our interrupt, rather than relying on lots of + * hard-coded machine dependencies. For reference, the expected + * IRQ mappings are: + * + * Machine Default IRQ + * adsbitsy IRQ_GPCIN4 + * cerf IRQ_GPIO_UCB1200_IRQ + * flexanet IRQ_GPIO_GUI + * freebird IRQ_GPIO_FREEBIRD_UCB1300_IRQ + * graphicsclient IRQ_GRAPHICSCLIENT_UCB1200 + * graphicsmaster IRQ_GRAPHICSMASTER_UCB1200 + * lart LART_IRQ_UCB1200 + * omnimeter IRQ_GPIO23 + * pfs168 IRQ_GPIO_UCB1300_IRQ + * simpad IRQ_GPIO_UCB1300_IRQ + * shannon SHANNON_IRQ_GPIO_IRQ_CODEC + * yopy IRQ_GPIO_UCB1200_IRQ + */ +static int __init ucb1x00_detect_irq(struct ucb1x00 *ucb) +{ + unsigned long mask; + + mask = probe_irq_on(); + if (!mask) + return NO_IRQ; + + /* + * Enable the ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Cause an ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + + /* + * Wait for the conversion to complete. + */ + while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0); + ucb1x00_reg_write(ucb, UCB_ADC_CR, 0); + + /* + * Disable and clear interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, 0); + ucb1x00_reg_write(ucb, UCB_IE_FAL, 0); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Read triggered interrupt. + */ + return probe_irq_off(mask); +} + +/* + * This configures the UCB1x00 layer depending on the machine type + * we're running on. The UCB1x00 drivers should not contain any + * machine dependencies. + * + * We can get rid of some of these dependencies by using existing + * facilities provided by the kernel - namely IRQ probing. The + * machine specific files are expected to setup the IRQ levels on + * initialisation. With any luck, we'll get rid of all the + * machine dependencies here. + */ +static int __init ucb1x00_configure(struct ucb1x00 *ucb) +{ + unsigned int irq_gpio_pin = 0; + int irq, default_irq = NO_IRQ; + + if (machine_is_adsbitsy()) + default_irq = IRQ_GPCIN4; + +// if (machine_is_assabet()) +// default_irq = IRQ_GPIO23; + +#ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) + default_irq = IRQ_GPIO_UCB1200_IRQ; +#endif +#ifdef CONFIG_SA1100_FREEBIRD + if (machine_is_freebird()) + default_irq = IRQ_GPIO_FREEBIRD_UCB1300_IRQ; +#endif +#if defined(CONFIG_SA1100_GRAPHICSCLIENT) +// if (machine_is_graphicsclient()) +// default_irq = IRQ_GRAPHICSCLIENT_UCB1200; +#endif +#if defined(CONFIG_SA1100_GRAPICSMASTER) + if (machine_is_graphicsmaster()) + default_irq = IRQ_GRAPHICSMASTER_UCB1200; +#endif +#ifdef CONFIG_SA1100_LART + if (machine_is_lart()) { + default_irq = LART_IRQ_UCB1200; + irq_gpio_pin = LART_GPIO_UCB1200; + } +#endif + if (machine_is_omnimeter()) + default_irq = IRQ_GPIO23; + +#ifdef CONFIG_SA1100_PFS168 + if (machine_is_pfs168()) + default_irq = IRQ_GPIO_UCB1300_IRQ; +#endif +#ifdef CONFIG_SA1100_SIMPAD + if (machine_is_simpad()) + default_irq = IRQ_GPIO_UCB1300_IRQ; +#endif +#ifdef CONFIG_SA1100_SIMPUTER + if (machine_is_simputer()) { + default_irq = IRQ_GPIO_UCB1300_IRQ; + irq_gpio_pin = GPIO_UCB1300_IRQ; + } +#endif + if (machine_is_shannon()) + default_irq = SHANNON_IRQ_GPIO_IRQ_CODEC; +#ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) + default_irq = IRQ_GPIO_UCB1200_IRQ; +#endif +#ifdef CONFIG_SA1100_ACCELENT + if (machine_is_accelent_sa()) { + ucb->irq = IRQ_GPIO_UCB1200_IRQ; + irq_gpio_pin = GPIO_UCB1200_IRQ; + } +#endif + + /* + * Eventually, this will disappear. + */ + if (irq_gpio_pin) + set_GPIO_IRQ_edge(irq_gpio_pin, GPIO_RISING_EDGE); + + irq = ucb1x00_detect_irq(ucb); + if (irq != NO_IRQ) { + if (default_irq != NO_IRQ && irq != default_irq) + printk(KERN_ERR "UCB1x00: probed IRQ%d != default IRQ%d\n", + irq, default_irq); + if (irq == default_irq) + printk(KERN_ERR "UCB1x00: probed IRQ%d correctly. " + "Please remove machine dependencies from " + "ucb1x00-core.c\n", irq); + ucb->irq = irq; + } else { + printk(KERN_ERR "UCB1x00: IRQ probe failed, using IRQ%d\n", + default_irq); + ucb->irq = default_irq; + } + + return ucb->irq == NO_IRQ ? -ENODEV : 0; +} + +struct ucb1x00 *my_ucb; + +/** + * ucb1x00_get - get the UCB1x00 structure describing a chip + * @ucb: UCB1x00 structure describing chip + * + * Return the UCB1x00 structure describing a chip. + * + * FIXME: Currently very noddy indeed, which currently doesn't + * matter since we only support one chip. + */ +struct ucb1x00 *ucb1x00_get(void) +{ + return my_ucb; +} + +static int __init ucb1x00_init(void) +{ + struct mcp *mcp; + unsigned int id; + int ret = -ENODEV; + + mcp = mcp_get(); + if (!mcp) + goto no_mcp; + + mcp_enable(mcp); + id = mcp_reg_read(mcp, UCB_ID); + + if (id != UCB_ID_1200 && id != UCB_ID_1300) { + printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); + goto out; + } + + my_ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL); + ret = -ENOMEM; + if (!my_ucb) + goto out; + + if (machine_is_shannon()) { + /* reset the codec */ + GPDR |= SHANNON_GPIO_CODEC_RESET; + GPCR = SHANNON_GPIO_CODEC_RESET; + GPSR = SHANNON_GPIO_CODEC_RESET; + + } + + memset(my_ucb, 0, sizeof(struct ucb1x00)); + + spin_lock_init(&my_ucb->lock); + spin_lock_init(&my_ucb->io_lock); + sema_init(&my_ucb->adc_sem, 1); + + my_ucb->id = id; + my_ucb->mcp = mcp; + + ret = ucb1x00_configure(my_ucb); + if (ret) + goto out; + + ret = request_irq(my_ucb->irq, ucb1x00_irq, 0, "UCB1x00", my_ucb); + if (ret) { + printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", + my_ucb->irq, ret); + kfree(my_ucb); + my_ucb = NULL; + goto out; + } + +#ifdef CONFIG_PM + my_ucb->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ucb1x00_pm); + if (my_ucb->pmdev == NULL) + printk("ucb1x00: unable to register in PM.\n"); + else + my_ucb->pmdev->data = my_ucb; +#endif + +out: + mcp_disable(mcp); +no_mcp: + return ret; +} + +static void __exit ucb1x00_exit(void) +{ + free_irq(my_ucb->irq, my_ucb); + kfree(my_ucb); +} + +module_init(ucb1x00_init); +module_exit(ucb1x00_exit); + +EXPORT_SYMBOL(ucb1x00_get); + +EXPORT_SYMBOL(ucb1x00_io_set_dir); +EXPORT_SYMBOL(ucb1x00_io_write); +EXPORT_SYMBOL(ucb1x00_io_read); + +EXPORT_SYMBOL(ucb1x00_adc_enable); +EXPORT_SYMBOL(ucb1x00_adc_read); +EXPORT_SYMBOL(ucb1x00_adc_disable); + +EXPORT_SYMBOL(ucb1x00_hook_irq); +EXPORT_SYMBOL(ucb1x00_free_irq); +EXPORT_SYMBOL(ucb1x00_enable_irq); +EXPORT_SYMBOL(ucb1x00_disable_irq); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 core driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/misc/ucb1x00-ts.c @@ -0,0 +1,664 @@ +/* + * linux/drivers/misc/ucb1x00-ts.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 21-Jan-2002 : + * + * Added support for synchronous A/D mode. This mode is useful to + * avoid noise induced in the touchpanel by the LCD, provided that + * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. + * It is important to note that the signal connected to the ADCSYNC + * pin should provide pulses even when the LCD is blanked, otherwise + * a pen touch needed to unblank the LCD will never be read. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ucb1x00.h" + +/* + * Define this if you want the UCB1x00 stuff to talk to the input layer + */ +#undef USE_INPUT + +#ifndef USE_INPUT + +#include +#include +#include + +/* + * This structure is nonsense - millisecs is not very useful + * since the field size is too small. Also, we SHOULD NOT + * be exposing jiffies to user space directly. + */ +struct ts_event { + u16 pressure; + u16 x; + u16 y; + u16 pad; + struct timeval stamp; +}; + +#define NR_EVENTS 16 + +#else + +#include + +#endif + +struct ucb1x00_ts { +#ifdef USE_INPUT + struct input_dev idev; +#endif + struct ucb1x00 *ucb; +#ifdef CONFIG_PM + struct pm_dev *pmdev; +#endif + + wait_queue_head_t irq_wait; + struct semaphore sem; + struct completion init_exit; + struct task_struct *rtask; + int use_count; + u16 x_res; + u16 y_res; + +#ifndef USE_INPUT + struct fasync_struct *fasync; + wait_queue_head_t read_wait; + u8 evt_head; + u8 evt_tail; + struct ts_event events[NR_EVENTS]; +#endif + int restart:1; + int adcsync:1; +}; + +static struct ucb1x00_ts ucbts; +static int adcsync = UCB_NOSYNC; + +static int ucb1x00_ts_startup(struct ucb1x00_ts *ts); +static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts); + +#ifndef USE_INPUT + +#define ucb1x00_ts_evt_pending(ts) ((volatile u8)(ts)->evt_head != (ts)->evt_tail) +#define ucb1x00_ts_evt_get(ts) ((ts)->events + (ts)->evt_tail) +#define ucb1x00_ts_evt_pull(ts) ((ts)->evt_tail = ((ts)->evt_tail + 1) & (NR_EVENTS - 1)) +#define ucb1x00_ts_evt_clear(ts) ((ts)->evt_head = (ts)->evt_tail = 0) + +static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) +{ + int next_head; + + next_head = (ts->evt_head + 1) & (NR_EVENTS - 1); + if (next_head != ts->evt_tail) { + ts->events[ts->evt_head].pressure = pressure; + ts->events[ts->evt_head].x = x; + ts->events[ts->evt_head].y = y; + do_gettimeofday(&ts->events[ts->evt_head].stamp); + ts->evt_head = next_head; + + if (ts->fasync) + kill_fasync(&ts->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&ts->read_wait); + } +} + +static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) +{ + ucb1x00_ts_evt_add(ts, 0, 0, 0); +} + +/* + * User space driver interface. + */ +static ssize_t +ucb1x00_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct ucb1x00_ts *ts = filp->private_data; + char *ptr = buffer; + int err = 0; + + add_wait_queue(&ts->read_wait, &wait); + while (count >= sizeof(struct ts_event)) { + err = -ERESTARTSYS; + if (signal_pending(current)) + break; + + if (ucb1x00_ts_evt_pending(ts)) { + struct ts_event *evt = ucb1x00_ts_evt_get(ts); + + err = copy_to_user(ptr, evt, sizeof(struct ts_event)); + ucb1x00_ts_evt_pull(ts); + + if (err) + break; + + ptr += sizeof(struct ts_event); + count -= sizeof(struct ts_event); + continue; + } + + set_current_state(TASK_INTERRUPTIBLE); + err = -EAGAIN; + if (filp->f_flags & O_NONBLOCK) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&ts->read_wait, &wait); + + return ptr == buffer ? err : ptr - buffer; +} + +static unsigned int ucb1x00_ts_poll(struct file *filp, poll_table *wait) +{ + struct ucb1x00_ts *ts = filp->private_data; + int ret = 0; + + poll_wait(filp, &ts->read_wait, wait); + if (ucb1x00_ts_evt_pending(ts)) + ret = POLLIN | POLLRDNORM; + + return ret; +} + +static int ucb1x00_ts_fasync(int fd, struct file *filp, int on) +{ + struct ucb1x00_ts *ts = filp->private_data; + + return fasync_helper(fd, filp, on, &ts->fasync); +} + +static int ucb1x00_ts_open(struct inode *inode, struct file *filp) +{ + struct ucb1x00_ts *ts = &ucbts; + int ret = 0; + + ret = ucb1x00_ts_startup(ts); + if (ret == 0) + filp->private_data = ts; + + return ret; +} + +/* + * Release touchscreen resources. Disable IRQs. + */ +static int ucb1x00_ts_release(struct inode *inode, struct file *filp) +{ + struct ucb1x00_ts *ts = filp->private_data; + + down(&ts->sem); + ucb1x00_ts_fasync(-1, filp, 0); + ucb1x00_ts_shutdown(ts); + up(&ts->sem); + + return 0; +} + +static struct file_operations ucb1x00_fops = { + owner: THIS_MODULE, + read: ucb1x00_ts_read, + poll: ucb1x00_ts_poll, + open: ucb1x00_ts_open, + release: ucb1x00_ts_release, + fasync: ucb1x00_ts_fasync, +}; + +/* + * The official UCB1x00 touchscreen is a miscdevice: + * 10 char Non-serial mice, misc features + * 14 = /dev/touchscreen/ucb1x00 UCB 1x00 touchscreen + */ +static struct miscdevice ucb1x00_ts_dev = { + minor: 14, + name: "touchscreen/ucb1x00", + fops: &ucb1x00_fops, +}; + +static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts) +{ + init_waitqueue_head(&ts->read_wait); + return misc_register(&ucb1x00_ts_dev); +} + +static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts) +{ + misc_deregister(&ucb1x00_ts_dev); +} + +#else + +#define ucb1x00_ts_evt_clear(ts) do { } while (0) + +static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) +{ + input_report_abs(&ts->idev, ABS_X, x); + input_report_abs(&ts->idev, ABS_Y, y); + input_report_abs(&ts->idev, ABS_PRESSURE, pressure); +} + +static int ucb1x00_ts_open(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + + return ucb1x00_ts_startup(ts); +} + +static void ucb1x00_ts_close(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + + down(&ts->sem); + ucb1x00_ts_shutdown(ts); + up(&ts->sem); +} + +static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts) +{ + ts->idev.name = "Touchscreen panel"; + ts->idev.idproduct = ts->ucb->id; + ts->idev.open = ucb1x00_ts_open; + ts->idev.close = ucb1x00_ts_close; + + __set_bit(EV_ABS, ts->idev.evbit); + __set_bit(ABS_X, ts->idev.absbit); + __set_bit(ABS_Y, ts->idev.absbit); + __set_bit(ABS_PRESSURE, ts->idev.absbit); + + input_register_device(&ts->idev); + + return 0; +} + +static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts) +{ + input_unregister_device(&ts->idev); +} + +#endif + +/* + * Switch to interrupt mode. + */ +static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); +} + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); +} + +/* + * Switch to X plate resistance mode. Set MX to ground, PX to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * Switch to Y plate resistance mode. Set MY to ground, PY to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * This is a RT kernel thread that handles the ADC accesses + * (mainly so we can use semaphores in the UCB1200 core code + * to serialise accesses to the ADC). + */ +static int ucb1x00_thread(void *_ts) +{ + struct ucb1x00_ts *ts = _ts; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + int valid; + + ts->rtask = tsk; + + daemonize(); + reparent_to_init(); + strcpy(tsk->comm, "ktsd"); + tsk->tty = NULL; + /* + * We could run as a real-time thread. However, thus far + * this doesn't seem to be necessary. + */ +// tsk->policy = SCHED_FIFO; +// tsk->rt_priority = 1; + + /* only want to receive SIGKILL */ + spin_lock_irq(&tsk->sigmask_lock); + siginitsetinv(&tsk->blocked, sigmask(SIGKILL)); + recalc_sigpending(tsk); + spin_unlock_irq(&tsk->sigmask_lock); + + complete(&ts->init_exit); + + valid = 0; + + add_wait_queue(&ts->irq_wait, &wait); + for (;;) { + unsigned int x, y, p, val; + signed long timeout; + + ts->restart = 0; + + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); + y = ucb1x00_ts_read_ypos(ts); + p = ucb1x00_ts_read_pressure(ts); + + /* + * Switch back to interrupt mode. + */ + ucb1x00_ts_mode_int(ts); + ucb1x00_adc_disable(ts->ucb); + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100); + if (signal_pending(tsk)) + break; + + ucb1x00_enable(ts->ucb); + val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); + + if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) { + set_task_state(tsk, TASK_INTERRUPTIBLE); + + ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + ucb1x00_disable(ts->ucb); + + /* + * If we spat out a valid sample set last time, + * spit out a "pen off" sample here. + */ + if (valid) { + ucb1x00_ts_event_release(ts); + valid = 0; + } + + timeout = MAX_SCHEDULE_TIMEOUT; + } else { + ucb1x00_disable(ts->ucb); + + /* + * Filtering is policy. Policy belongs in user + * space. We therefore leave it to user space + * to do any filtering they please. + */ + if (!ts->restart) { + ucb1x00_ts_evt_add(ts, p, x, y); + valid = 1; + } + + set_task_state(tsk, TASK_INTERRUPTIBLE); + timeout = HZ / 100; + } + + schedule_timeout(timeout); + if (signal_pending(tsk)) + break; + } + + remove_wait_queue(&ts->irq_wait, &wait); + + ts->rtask = NULL; + ucb1x00_ts_evt_clear(ts); + complete_and_exit(&ts->init_exit, 0); +} + +/* + * We only detect touch screen _touches_ with this interrupt + * handler, and even then we just schedule our task. + */ +static void ucb1x00_ts_irq(int idx, void *id) +{ + struct ucb1x00_ts *ts = id; + ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + wake_up(&ts->irq_wait); +} + +static int ucb1x00_ts_startup(struct ucb1x00_ts *ts) +{ + int ret = 0; + + if (down_interruptible(&ts->sem)) + return -EINTR; + + if (ts->use_count++ != 0) + goto out; + + if (ts->rtask) + panic("ucb1x00: rtask running?"); + + init_waitqueue_head(&ts->irq_wait); + ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + if (ret < 0) + goto out; + + /* + * If we do this at all, we should allow the user to + * measure and read the X and Y resistance at any time. + */ + ucb1x00_adc_enable(ts->ucb); + ts->x_res = ucb1x00_ts_read_xres(ts); + ts->y_res = ucb1x00_ts_read_yres(ts); + ucb1x00_adc_disable(ts->ucb); + + init_completion(&ts->init_exit); + ret = kernel_thread(ucb1x00_thread, ts, 0); + if (ret >= 0) { + wait_for_completion(&ts->init_exit); + ret = 0; + } else { + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + } + + out: + if (ret) + ts->use_count--; + up(&ts->sem); + return ret; +} + +/* + * Release touchscreen resources. Disable IRQs. + */ +static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts) +{ + if (--ts->use_count == 0) { + if (ts->rtask) { + send_sig(SIGKILL, ts->rtask, 1); + wait_for_completion(&ts->init_exit); + } + + ucb1x00_enable(ts->ucb); + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); + ucb1x00_disable(ts->ucb); + } +} + +#ifdef CONFIG_PM +static int ucb1x00_ts_pm (struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *) (dev->data); + + if (rqst == PM_RESUME && ts->rtask != NULL) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ts->restart = 1; + wake_up(&ts->irq_wait); + } + return 0; +} +#endif + + +/* + * Initialisation. + */ +static int __init ucb1x00_ts_init(void) +{ + struct ucb1x00_ts *ts = &ucbts; + + ts->ucb = ucb1x00_get(); + if (!ts->ucb) + return -ENODEV; + + ts->adcsync = adcsync; + init_MUTEX(&ts->sem); + +#ifdef CONFIG_PM + ts->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ucb1x00_ts_pm); + if (ts->pmdev == NULL) + printk("ucb1x00_ts: unable to register in PM.\n"); + else + ts->pmdev->data = ts; +#endif + return ucb1x00_ts_register(ts); +} + +static void __exit ucb1x00_ts_exit(void) +{ + struct ucb1x00_ts *ts = &ucbts; + + ucb1x00_ts_deregister(ts); + +#ifdef CONFIG_PM + if (ts->pmdev) + pm_unregister(ts->pmdev); +#endif +} + +#ifndef MODULE + +/* + * Parse kernel command-line options. + * + * syntax : ucbts=[sync|nosync],... + */ +static int __init ucb1x00_ts_setup(char *str) +{ + char *p; + + while ((p = strsep(&str, ",")) != NULL) { + if (strcmp(p, "sync") == 0) + adcsync = UCB_SYNC; + } + + return 1; +} + +__setup("ucbts=", ucb1x00_ts_setup); + +#else + +MODULE_PARM(adcsync, "i"); +MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal"); + +#endif + +module_init(ucb1x00_ts_init); +module_exit(ucb1x00_ts_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/misc/ucb1x00.h @@ -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 --- linux-2.4.27/drivers/mtd/chips/cfi_probe.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/chips/cfi_probe.c @@ -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; } --- linux-2.4.27/drivers/mtd/chips/jedec_probe.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/chips/jedec_probe.c @@ -100,6 +100,8 @@ #define M29W040B 0x00E3 /* SST */ +#define SST29EE020 0x0010 +#define SST29LE020 0x0012 #define SST29EE512 0x005d #define SST29LE512 0x003d #define SST39LF800 0x2781 @@ -839,6 +841,24 @@ } }, { mfr_id: MANUFACTURER_SST, + dev_id: SST29EE020, + name: "SST 29EE020", + DevSize: SIZE_256KiB, + CmdSet: P_ID_SST_PAGE, + NumEraseRegions: 1, + regions: {ERASEINFO(0x01000,64), + } + }, { + mfr_id: MANUFACTURER_SST, + dev_id: SST29LE020, + name: "SST 29LE020", + DevSize: SIZE_256KiB, + CmdSet: P_ID_SST_PAGE, + NumEraseRegions: 1, + regions: {ERASEINFO(0x01000,64), + } + }, { + mfr_id: MANUFACTURER_SST, dev_id: SST39LF020, name: "SST 39LF020", DevSize: SIZE_256KiB, @@ -937,7 +957,20 @@ struct cfi_private *cfi) { /* Reset */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + + /* after checking the datasheets for SST, MACRONIX and ATMEL + * (oh and incidentaly the jedec spec - 3.5.3.3) the reset + * sequence is *supposed* to be 0xaa at 0x5555, 0x55 at + * 0x2aaa, 0xF0 at 0x5555 this will not affect the AMD chips + * as they will ignore the writes and dont care what address + * the F0 is written to */ + if(cfi->addr_unlock1) { + /*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/ + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + } + + cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); /* Some misdesigned intel chips do not respond for 0xF0 for a reset, * so ensure we're in read mode. Send both the Intel and the AMD command * for this. Intel uses 0xff for this, AMD uses 0xff for NOP, so --- linux-2.4.27/drivers/mtd/devices/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/devices/Config.in @@ -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 --- linux-2.4.27/drivers/mtd/devices/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/devices/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/mtd/devices/syncflash.c @@ -0,0 +1,615 @@ +/* + * MTD driver for Micron SyncFlash flash memory. + * + * Author: Jon McClintock + * + * Based loosely upon the LART flash driver, authored by Abraham vd Merwe + * . + * + * Copyright 2003, Blue Mug, Inc. for Motorola, Inc. + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * References: + * + * [1] Micron SyncFlash homepage + * - http://www.syncflash.com/ + * + * [2] MT28S4M16LC -- 4Mx16 SyncFlash memory datasheet + * - http://syncflash.com/pdfs/datasheets/mt28s4m16lc_6.pdf + * + * [3] MTD internal API documentation + * - http://www.linux-mtd.infradead.org/tech/ + * + * Limitations: + * + * Even though this driver is written for Micron SyncFlash, it is quite + * specific to the Motorola MX1 ADS development board. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* partition support */ +#define HAVE_PARTITIONS +#ifdef HAVE_PARTITIONS +#include +#endif + +#ifndef CONFIG_ARCH_MX1ADS +#error The SyncFlash driver currently only supports the MX1 ADS platform. +#endif + +/* + * General flash configuration parameters. + */ +#define BUSWIDTH 4 +#define FLASH_BLOCKSIZE (256 * 1024 * BUSWIDTH) +#define FLASH_NUMBLOCKS 16 + +#define BUSWIDTH 4 +#define FLASH_ADDRESS IO_ADDRESS(MX1ADS_FLASH_BASE) + +#define FLASH_MANUFACTURER 0x002C002C +#define FLASH_DEVICE_ID 0x00D300D3 + +/* + * The size and extent of the bootloader in flash. + */ +#define NUM_BOOTLOADER_BLOCKS 1 +#define BOOTLOADER_START 0x00000000 +#define BOOTLOADER_LEN (NUM_BOOTLOADER_BLOCKS * FLASH_BLOCKSIZE) + +/* + * The size and extent of the kernel in flash. + */ +#define NUM_KERNEL_BLOCKS 1 +#define KERNEL_START (BOOTLOADER_START + BOOTLOADER_LEN) +#define KERNEL_LEN (NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE) + +/* File system */ +#define NUM_FILESYSTEM_BLOCKS 14 +#define FILESYSTEM_START (KERNEL_START + KERNEL_LEN) +#define FILESYSTEM_LEN (NUM_FILESYSTEM_BLOCKS * FLASH_BLOCKSIZE) + + +/* + * SDRAM controller register location and values. These are very specific + * to the MX1. + */ +#define SDRAMC_REGISTER IO_ADDRESS(0x00221004) + +/* + * This the mask we use to get the start of a block from a given address. + */ +#define BLOCK_MASK (0xFFF00000) + +/* + * This is the A10 address line of the SyncFlash; it's used to initiate + * a precharge command. + */ +#define SYNCFLASH_A10 (0x00100000) + +/* + * SDRAM controller MODE settings. + */ +#define CMD_NORMAL (0x81020300) /* Normal Mode */ +#define CMD_PREC (CMD_NORMAL + 0x10000000) /* Precharge command */ +#define CMD_AUTO (CMD_NORMAL + 0x20000000) /* Auto refresh */ +#define CMD_LMR (CMD_NORMAL + 0x30000000) /* Load Mode Register */ +#define CMD_LCR (CMD_NORMAL + 0x60000000) /* LCR Command */ +#define CMD_PROGRAM (CMD_NORMAL + 0x70000000) /* SyncFlash Program */ + +/* + * SyncFlash LCR Commands adjusted for the DBMX1 AHB internal address bus . + */ +#define LCR_READ_STATUS (0x0001C000) /* 0x70 */ +#define LCR_READ_CONFIG (0x00024000) /* 0x90 */ +#define LCR_ERASE_CONFIRM (0x00008000) /* 0x20 */ +#define LCR_ERASE_NVMODE (0x0000C000) /* 0x30 */ +#define LCR_PROG_NVMODE (0x00028000) /* 0xA0 */ +#define LCR_SR_CLEAR (0x00014000) /* 0x50 */ + +/* + * Status register bits + */ +#define SR_VPS_ERROR (1 << 8) /* Power-Up status error */ +#define SR_ISM_READY (1 << 7) /* State machine isn't busy */ +#define SR_ERASE_ERROR (1 << 5) /* Erase/Unprotect error */ +#define SR_PROGRAM_ERROR (1 << 4) /* Program/Protect error */ +#define SR_DEVICE_PROTECTED (1 << 3) /* Device is protected */ +#define SR_ISM_STATUS_H (1 << 2) /* Bank ISM status, high bit */ +#define SR_ISM_STATUS_L (1 << 1) /* Bank ISM status, low bit */ +#define SR_DEVICE_ISM_STATUS (1 << 0) /* ISM is device-level */ + +#define SR_ERROR (SR_VPS_ERROR|SR_ERASE_ERROR|SR_PROGRAM_ERROR|SR_DEVICE_PROTECTED) + +#define STATUS_VALUE(a) ((a) | ((a) << 16)) + +/* + * Device configuration register offsets + */ +#define DC_MANUFACTURER (0 * BUSWIDTH) +#define DC_DEVICE_ID (1 * BUSWIDTH) +#define DC_BLOCK_PROTECT (2 * BUSWIDTH) +#define DC_DEVICE_PROTECT (3 * BUSWIDTH) + +#define FL_WORD(addr) (*(volatile unsigned long*)(addr)) + +static char module_name[] = "syncflash"; + +inline __u8 read8 (__u32 offset) +{ + return *(volatile __u8 *) (FLASH_ADDRESS + offset); +} + +inline __u32 read32 (__u32 offset) +{ + return *(volatile __u32 *) (FLASH_ADDRESS + offset); +} + +inline void write32 (__u32 x,__u32 offset) +{ + *(volatile __u32 *) (FLASH_ADDRESS + offset) = x; +} + +static __u32 read_device_configuration_register(__u32 reg_number) +{ + __u32 tmp; + + /* Setup the SDRAM controller to issue an LCR command. */ + FL_WORD(SDRAMC_REGISTER) = CMD_LCR; + + /* Perform a read to issue the Read Device Configuration Command. */ + tmp = read32(LCR_READ_CONFIG); + + /* Return the SDRAM controller to normal mode. */ + FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; + + /* Return the value of the specified register. */ + tmp = read32(reg_number); + + return tmp; +} + +/* + * Get the status of the flash devices. + */ +static __u32 flash_read_status() +{ + __u32 status, tmp; + + /* Enter the SyncFlash Program READ/WRITE mode. */ + FL_WORD(SDRAMC_REGISTER) = CMD_PROGRAM; + + /* Read the status register. */ + status = read32(LCR_READ_STATUS); + + /* Clear the status register. */ + FL_WORD(SDRAMC_REGISTER) = CMD_LCR; + tmp = read32(LCR_SR_CLEAR); + + /* Return to Normal mode. */ + FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; + + return status; +} + +/* + * Loop until both write state machines are ready. + */ +static __u32 flash_status_wait() +{ + __u32 status; + do { + status = flash_read_status(); + } while ((status & STATUS_VALUE(SR_ISM_READY)) != + STATUS_VALUE(SR_ISM_READY)); + return status; +} + +/* + * Loop until the Write State machine is ready, then do a full error + * check. Clear status and leave the flash in Read Array mode; return + * 0 for no error, -1 for error. + */ +static int flash_status_full_check() +{ + __u32 status; + + status = flash_status_wait() & STATUS_VALUE(SR_ERROR); + return status ? -EIO : 0; +} + +/* + * Return the flash to the normal mode. + */ +static void flash_normal_mode() +{ + __u32 tmp; + + /* First issue a precharge all command. */ + FL_WORD(SDRAMC_REGISTER) = CMD_PREC; + tmp = read32(SYNCFLASH_A10); + + /* Now place the SDRAM controller in Normal mode. */ + FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; +} + +/* + * Probe for SyncFlash memory on MX1ADS board. + * + * Returns 1 if we found SyncFlash memory, 0 otherwise. + */ +static int flash_probe (void) +{ + __u32 manufacturer, device_id; + + /* For some reason, the first read doesn't work, so we do it + * twice. */ + manufacturer = read_device_configuration_register(DC_MANUFACTURER); + manufacturer = read_device_configuration_register(DC_MANUFACTURER); + device_id = read_device_configuration_register(DC_DEVICE_ID); + + printk("SyncFlash probe: manufacturer 0x%08lx, device_id 0x%08lx\n", + manufacturer, device_id); + return (manufacturer == FLASH_MANUFACTURER && + device_id == FLASH_DEVICE_ID); +} + +/* + * Erase one block of flash memory at offset ``offset'' which is any + * address within the block which should be erased. + * + * Returns 0 if successful, -1 otherwise. + */ +static inline int erase_block (__u32 offset) +{ + __u32 tmp; + + /* Mask off the lower bits of the address to get the first address + * in the flash block. */ + offset &= (__u32)BLOCK_MASK; + + /* Perform a read and precharge of the bank before the LCR|ACT|WRIT + * sequence to avoid the inadvertent precharge command occurring + * during the LCR_ACT_WRIT sequence. */ + FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; + tmp = read32(offset); + FL_WORD(SDRAMC_REGISTER) = CMD_PREC; + tmp = read32(offset); + + /* Now start the actual erase. */ + + /* LCR|ACT|WRIT sequence */ + FL_WORD(SDRAMC_REGISTER) = CMD_LCR; + write32(0, offset + LCR_ERASE_CONFIRM); + + /* Return to normal mode to issue the erase confirm. */ + FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; + write32(0xD0D0D0D0, offset); + + if (flash_status_full_check()) { + printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n", + module_name, offset); + return (-1); + } + + flash_normal_mode(); + + return 0; +} + +static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) +{ + __u32 addr,len; + int i,first; + + /* sanity checks */ + if (instr->addr + instr->len > mtd->size) return (-EINVAL); + + /* + * check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + * + * skip all erase regions which are ended before the start of + * the requested erase. Actually, to save on the calculations, + * we skip to the first erase region which starts after the + * start of the requested erase, and then go back one. + */ + for (i = 0; (i < mtd->numeraseregions) && + (instr->addr >= mtd->eraseregions[i].offset); i++) ; + i--; + + /* + * ok, now i is pointing at the erase region in which this + * erase request starts. Check the start of the requested + * erase range is aligned with the erase size which is in + * effect here. + */ + if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) + return (-EINVAL); + + /* Remember the erase region we start on */ + first = i; + + /* + * next, check that the end of the requested erase is aligned + * with the erase region at that address. + * + * as before, drop back one to point at the region in which + * the address actually falls + */ + for (; + (i < mtd->numeraseregions) && + ((instr->addr + instr->len) >= mtd->eraseregions[i].offset) ; + i++) ; + i--; + + /* is the end aligned on a block boundary? */ + if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) + return (-EINVAL); + + addr = instr->addr; + len = instr->len; + + i = first; + + /* now erase those blocks */ + while (len) + { + if (erase_block (addr)) + { + instr->state = MTD_ERASE_FAILED; + return (-EIO); + } + + addr += mtd->eraseregions[i].erasesize; + len -= mtd->eraseregions[i].erasesize; + + if (addr == (mtd->eraseregions[i].offset + + (mtd->eraseregions[i].erasesize * + mtd->eraseregions[i].numblocks))) + i++; + } + + instr->state = MTD_ERASE_DONE; + if (instr->callback) instr->callback (instr); + + return (0); +} + +static int flash_read (struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + /* Sanity checks. */ + if (!len) return (0); + if (from + len > mtd->size) return (-EINVAL); + + /* Ensure that we are in normal mode. */ + flash_normal_mode(); + + /* We always read len bytes. */ + *retlen = len; + + /* first, we read bytes until we reach a dword boundary */ + if (from & (BUSWIDTH - 1)) + { + int gap = BUSWIDTH - (from & (BUSWIDTH - 1)); + while (len && gap--) *buf++ = read8(from++), len--; + } + + /* now we read dwords until we reach a non-dword boundary */ + while (len >= BUSWIDTH) + { + *((__u32 *) buf) = read32(from); + + buf += BUSWIDTH; + from += BUSWIDTH; + len -= BUSWIDTH; + } + + /* top up the last unaligned bytes */ + if (len & (BUSWIDTH - 1)) + while (len--) *buf++ = read8(from++); + + return (0); +} + +/* + * Write one dword ``x'' to flash memory at offset ``offset''. ``offset'' + * must be 32 bits, i.e. it must be on a dword boundary. + * + * Returns 0 if successful, -1 otherwise. + */ +static int flash_write_dword(__u32 offset, __u32 x) +{ + __u32 tmp; + + /* First issue a precharge all command. */ + FL_WORD(SDRAMC_REGISTER) = CMD_PREC; + tmp = read32(SYNCFLASH_A10); + + /* Enter the SyncFlash programming mode. */ + FL_WORD(SDRAMC_REGISTER) = CMD_PROGRAM; + write32(x, offset); + + /* Wait for the write to complete. */ + flash_status_wait(); + + /* Return to normal mode. */ + flash_normal_mode(); + + return 0; +} + +static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen,const u_char *buf) +{ + __u8 tmp[4]; + int i,n; + + *retlen = 0; + + /* Sanity checks */ + if (!len) return (0); + if (to + len > mtd->size) return (-EINVAL); + + /* First, we write a 0xFF.... padded byte until we reach a + * dword boundary. */ + if (to & (BUSWIDTH - 1)) + { + __u32 aligned = to & ~(BUSWIDTH - 1); + int gap = to - aligned; + + i = n = 0; + + while (gap--) tmp[i++] = 0xFF; + while (len && i < BUSWIDTH) tmp[i++] = buf[n++], len--; + while (i < BUSWIDTH) tmp[i++] = 0xFF; + + if (flash_write_dword(aligned, *((__u32 *) tmp))) + return (-EIO); + + to += n; + buf += n; + *retlen += n; + } + + /* Now we write dwords until we reach a non-dword boundary. */ + while (len >= BUSWIDTH) + { + if (flash_write_dword (to,*((__u32 *) buf))) return (-EIO); + + to += BUSWIDTH; + buf += BUSWIDTH; + *retlen += BUSWIDTH; + len -= BUSWIDTH; + } + + /* Top up the last unaligned bytes, padded with 0xFF.... */ + if (len & (BUSWIDTH - 1)) + { + i = n = 0; + + while (len--) tmp[i++] = buf[n++]; + while (i < BUSWIDTH) tmp[i++] = 0xFF; + + if (flash_write_dword (to,*((__u32 *) tmp))) return (-EIO); + + *retlen += n; + } + + return flash_status_full_check(); +} + + + +#define NB_OF(x) (sizeof (x) / sizeof (x[0])) + +static struct mtd_info mtd; + +static struct mtd_erase_region_info erase_regions[] = +{ + /* flash blocks */ + { + offset: 0x00000000, + erasesize: FLASH_BLOCKSIZE, + numblocks: FLASH_NUMBLOCKS + }, +}; + +#ifdef HAVE_PARTITIONS +static struct mtd_partition syncflash_partitions[] = +{ + /* bootloader */ + { + name: "bootloader", + offset: BOOTLOADER_START, + size: BOOTLOADER_LEN, + mask_flags: 0 + }, + /* Kernel */ + { + name: "kernel", + offset: KERNEL_START, /* MTDPART_OFS_APPEND */ + size: KERNEL_LEN, + mask_flags: 0 + }, + /* file system */ + { + name: "file system", + offset: FILESYSTEM_START, /* MTDPART_OFS_APPEND */ + size: FILESYSTEM_LEN, /* MTDPART_SIZ_FULL */ + mask_flags: 0 + } +}; +#endif + +int __init syncflash_init (void) +{ + int result; + + memset (&mtd,0,sizeof (mtd)); + + printk ("MTD driver for Micron SyncFlash.\n"); + printk ("%s: Probing for SyncFlash on MX1ADS...\n",module_name); + + if (!flash_probe ()) + { + printk (KERN_WARNING "%s: Found no SyncFlash devices\n", + module_name); + return (-ENXIO); + } + + printk ("%s: Found a SyncFlash device.\n",module_name); + + mtd.name = module_name; + mtd.type = MTD_NORFLASH; + mtd.flags = MTD_CAP_NORFLASH; + mtd.size = FLASH_BLOCKSIZE * FLASH_NUMBLOCKS; + + mtd.erasesize = FLASH_BLOCKSIZE; + mtd.numeraseregions = NB_OF(erase_regions); + mtd.eraseregions = erase_regions; + + mtd.module = THIS_MODULE; + + mtd.erase = flash_erase; + mtd.read = flash_read; + mtd.write = flash_write; + +#ifndef HAVE_PARTITIONS + result = add_mtd_device(&mtd); +#else + result = add_mtd_partitions(&mtd, + syncflash_partitions, + NB_OF(syncflash_partitions)); +#endif + + return (result); +} + +void __exit syncflash_exit (void) +{ +#ifndef HAVE_PARTITIONS + del_mtd_device (&mtd); +#else + del_mtd_partitions (&mtd); +#endif +} + +module_init (syncflash_init); +module_exit (syncflash_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jon McClintock "); +MODULE_DESCRIPTION("MTD driver for Micron MT28S4M16LC SyncFlash on MX1ADS board"); + + --- linux-2.4.27/drivers/mtd/maps/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/maps/Config.in @@ -81,10 +81,10 @@ dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310 - dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT - dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET + dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_FORTUNET + dep_tristate ' CFI Flash device mapped on Epxa' CONFIG_MTD_EPXA $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12 - dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_ARCH_EDB7212 $CONFIG_MTD_CFI dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA fi --- linux-2.4.27/drivers/mtd/maps/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/maps/Makefile @@ -3,11 +3,7 @@ # # $Id: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $ -BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/) - -ifeq ($(BELOW25),y) O_TARGET := mapslink.o -endif # Chip mappings obj-$(CONFIG_MTD_CDB89712) += cdb89712.o @@ -17,7 +13,7 @@ obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o -obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o +obj-$(CONFIG_MTD_EPXA) += epxa-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o @@ -29,9 +25,9 @@ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o ifneq ($(CONFIG_MTD_PHYSMAP),n) ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8) - obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o + obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o else - obj-$(CONFIG_MTD_PHYSMAP) += physmap.o + obj-$(CONFIG_MTD_PHYSMAP) += physmap.o endif endif obj-$(CONFIG_MTD_PNC2000) += pnc2000.o @@ -39,6 +35,9 @@ obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o +ifeq ($(CONFIG_ASSABET_NEPONSET),y) + obj-$(CONFIG_MTD_SA1100) += neponset-flash.o +endif obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o --- linux-2.4.27/drivers/mtd/maps/dc21285.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/maps/dc21285.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -18,9 +19,9 @@ #include #include +#include - -static struct mtd_info *mymtd; +static struct mtd_info *dc21285_mtd; __u8 dc21285_read8(struct map_info *map, unsigned long ofs) { @@ -44,6 +45,9 @@ void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr) { + if(machine_is_netwinder()) { + nw_en_write(); + } *CSR_ROMWRITEREG = adr & 3; adr &= ~3; *(__u8*)(map->map_priv_1 + adr) = d; @@ -51,6 +55,9 @@ void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) { + if(machine_is_netwinder()) { + nw_en_write(); + } *CSR_ROMWRITEREG = adr & 3; adr &= ~3; *(__u16*)(map->map_priv_1 + adr) = d; @@ -58,6 +65,9 @@ void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr) { + if(machine_is_netwinder()) { + nw_en_write(); + } *(__u32*)(map->map_priv_1 + adr) = d; } @@ -105,6 +115,27 @@ }; +static void +nw_en_write(void) { +#ifdef CONFIG_ARCH_NETWINDER + extern spinlock_t gpio_lock; + unsigned long flags; + + /* + * we want to write a bit pattern XXX1 to Xilinx to enable + * the write gate, which will be open for about the next 2ms. + */ + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(1, 1); + spin_unlock_irqrestore(&gpio_lock, flags); + + /* + * let the ISA bus to catch on... + */ + udelay(25); +#endif +} + /* Partition stuff */ static struct mtd_partition *dc21285_parts; @@ -112,6 +143,9 @@ int __init init_dc21285(void) { + int nr_parts = 0; + char *part_type = "none"; + /* Determine buswidth */ switch (*CSR_SA110_CNTL & (3<<14)) { case SA110_CNTL_ROMWIDTH_8: @@ -137,24 +171,64 @@ return -EIO; } - mymtd = do_map_probe("cfi_probe", &dc21285_map); - if (mymtd) { - int nrparts = 0; + if(machine_is_ebsa285()) { + dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map); + } else { + dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map); + } - mymtd->module = THIS_MODULE; + if (!dc21285_mtd) { + /* no recognised device so unmap and exit */ + iounmap((void *)dc21285_map.map_priv_1); + return -ENXIO; + } - /* partition fixup */ + dc21285_mtd->module = THIS_MODULE; + /* + * Dynamic partition selection stuff (might override the static ones) + */ #ifdef CONFIG_MTD_REDBOOT_PARTS - nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); + if (nr_parts == 0) { + int ret = parse_redboot_partitions(dc21285_mtd, &dc21285_parts); + + if (ret > 0) { + part_type = "RedBoot"; + nr_parts = ret; + } + else + { + dc21285_parts=NULL; /* ensure partition table remains clear */ + } + } #endif - if (nrparts > 0) { - add_mtd_partitions(mymtd, dc21285_parts, nrparts); - } else if (nrparts == 0) { - printk(KERN_NOTICE "RedBoot partition table failed\n"); - add_mtd_device(mymtd); +#ifdef CONFIG_MTD_CMDLINE_PARTS + if (nr_parts == 0) { + int ret = parse_cmdline_partitions(dc21285_mtd, &dc21285_parts, "sa1100"); + if (ret > 0) { + part_type = "Command Line"; + nr_parts = ret; + } + else + { + dc21285_parts=NULL; /* ensure partition table remains clear */ } + } +#endif + + if (nr_parts == 0) { + printk(KERN_NOTICE "DC21285 Flash: no partition info available, registering whole flash at once\n"); + add_mtd_device(dc21285_mtd); + } +#ifdef CONFIG_MTD_PARTITIONS + else + { + printk(KERN_NOTICE "DC21285 Flash: Using %s partition definition\n", part_type); + add_mtd_partitions(dc21285_mtd, &dc21285_parts, nr_parts); + } +#endif + if(machine_is_ebsa285()) { /* * Flash timing is determined with bits 19-16 of the * CSR_SA110_CNTL. The value is the number of wait cycles, or @@ -167,20 +241,15 @@ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); /* tristate time */ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); - - return 0; } - - iounmap((void *)dc21285_map.map_priv_1); - return -ENXIO; } static void __exit cleanup_dc21285(void) { - if (mymtd) { - del_mtd_device(mymtd); - map_destroy(mymtd); - mymtd = NULL; + if (dc21285_mtd) { + del_mtd_device(dc21285_mtd); + map_destroy(dc21285_mtd); + dc21285_mtd = NULL; } if (dc21285_map.map_priv_1) { iounmap((void *)dc21285_map.map_priv_1); --- /dev/null +++ linux-2.4.27/drivers/mtd/maps/epxa-flash.c @@ -0,0 +1,234 @@ +/* + * Flash memory access on EPXA based devices + * + * (C) 2000 Nicolas Pitre + * Copyright (C) 2001 Altera Corporation + * Copyright (C) 2001 Red Hat, Inc. + * + * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef CONFIG_EPXA10DB +#define BOARD_NAME "EPXA10DB" +#else +#define BOARD_NAME "EPXA1DB" +#endif + +static int nr_parts = 0; +static struct mtd_partition *parts; + +static struct mtd_info *mymtd; + +extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); +static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +static __u8 epxa_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +static __u16 epxa_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +static __u32 epxa_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +static struct map_info epxa_map = { + .name = "EPXA flash", + .size = FLASH_SIZE, + .buswidth = 2, + .read8 = epxa_read8, + .read16 = epxa_read16, + .read32 = epxa_read32, + .copy_from = epxa_copy_from, + .write8 = epxa_write8, + .write16 = epxa_write16, + .write32 = epxa_write32, + .copy_to = epxa_copy_to +}; + +static int __init epxa_mtd_init(void) +{ + int i; + + printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); + epxa_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_START, FLASH_SIZE); + if (!epxa_map.map_priv_1) { + printk("Failed to ioremap %s flash\n",BOARD_NAME); + return -EIO; + } + + mymtd = do_map_probe("cfi_probe", &epxa_map); + if (!mymtd) { + iounmap((void *)epxa_map.map_priv_1); + return -ENXIO; + } + + mymtd->module = THIS_MODULE; + + /* Unlock the flash device. */ + if(mymtd->unlock){ + for (i=0; inumeraseregions;i++){ + int j; + for(j=0;jeraseregions[i].numblocks;j++){ + mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); + } + } + } + +#ifdef CONFIG_MTD_REDBOOT_PARTS + nr_parts = parse_redboot_partitions(mymtd, &parts); + + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } +#endif +#ifdef CONFIG_MTD_AFS_PARTS + nr_parts = parse_afs_partitions(mymtd, &parts); + + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } +#endif + + /* No recognised partitioning schemes found - use defaults */ + nr_parts = epxa_default_partitions(mymtd, &parts); + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } + + /* If all else fails... */ + add_mtd_device(mymtd); + return 0; +} + +static void __exit epxa_mtd_cleanup(void) +{ + if (mymtd) { + if (nr_parts) + del_mtd_partitions(mymtd); + else + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (epxa_map.map_priv_1) { + iounmap((void *)epxa_map.map_priv_1); + epxa_map.map_priv_1 = 0; + } +} + + +/* + * This will do for now, once we decide which bootldr we're finally + * going to use then we'll remove this function and do it properly + * + * Partions are currently (as offsets from base of flash): + * 0x00000000 - 0x003FFFFF - bootloader (!) + * 0x00400000 - 0x00FFFFFF - Flashdisk + */ + +static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) +{ + struct mtd_partition *parts; + int ret; + int npartitions = 0; + char *names; + const char *name = "jffs"; + + printk("Using default partitions for %s\n",BOARD_NAME); + npartitions=1; + parts = kmalloc(npartitions*sizeof(*parts)+strlen(name)+1, GFP_KERNEL); + if (!parts) { + ret = -ENOMEM; + goto out; + } + memzero(parts,npartitions*sizeof(*parts)+strlen(name)); + + names = (char *)&parts[npartitions]; + parts[0].name = names; + names += strlen(name) + 1; + strcpy(parts[0].name, name); + +#ifdef CONFIG_EPXA10DB_R2 + parts[0].size = FLASH_SIZE-0x00400000; + parts[0].offset = 0x00400000; +#elif defined CONFIG_EPXA10DB_R3 + parts[0].size = 0x00800000; + parts[0].offset = 0x00800000; +#else + parts[0].size = FLASH_SIZE-0x00180000; + parts[0].offset = 0x00180000; +#endif + ret = npartitions; + + out: + *pparts = parts; + return ret; +} + + +module_init(epxa_mtd_init); +module_exit(epxa_mtd_cleanup); + +MODULE_AUTHOR("Clive Davies"); +MODULE_DESCRIPTION("Altera epxa mtd flash map"); +MODULE_LICENSE("GPL"); --- linux-2.4.27/drivers/mtd/maps/epxa10db-flash.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Flash memory access on EPXA based devices - * - * (C) 2000 Nicolas Pitre - * Copyright (C) 2001 Altera Corporation - * Copyright (C) 2001 Red Hat, Inc. - * - * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#ifdef CONFIG_EPXA10DB -#define BOARD_NAME "EPXA10DB" -#else -#define BOARD_NAME "EPXA1DB" -#endif - -static int nr_parts = 0; -static struct mtd_partition *parts; - -static struct mtd_info *mymtd; - -extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); -static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); - -static __u8 epxa_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -static __u16 epxa_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -static __u32 epxa_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); -} - -static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio((void *)(map->map_priv_1 + to), from, len); -} - - - -static struct map_info epxa_map = { - name: "EPXA flash", - size: FLASH_SIZE, - buswidth: 2, - read8: epxa_read8, - read16: epxa_read16, - read32: epxa_read32, - copy_from: epxa_copy_from, - write8: epxa_write8, - write16: epxa_write16, - write32: epxa_write32, - copy_to: epxa_copy_to -}; - - -static int __init epxa_mtd_init(void) -{ - int i; - - printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); - epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); - if (!epxa_map.map_priv_1) { - printk("Failed to ioremap %s flash\n",BOARD_NAME); - return -EIO; - } - - mymtd = do_map_probe("cfi_probe", &epxa_map); - if (!mymtd) { - iounmap((void *)epxa_map.map_priv_1); - return -ENXIO; - } - - mymtd->module = THIS_MODULE; - - /* Unlock the flash device. */ - if(mymtd->unlock){ - for (i=0; inumeraseregions;i++){ - int j; - for(j=0;jeraseregions[i].numblocks;j++){ - mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); - } - } - } - -#ifdef CONFIG_MTD_REDBOOT_PARTS - nr_parts = parse_redboot_partitions(mymtd, &parts); - - if (nr_parts > 0) { - add_mtd_partitions(mymtd, parts, nr_parts); - return 0; - } -#endif -#ifdef CONFIG_MTD_AFS_PARTS - nr_parts = parse_afs_partitions(mymtd, &parts); - - if (nr_parts > 0) { - add_mtd_partitions(mymtd, parts, nr_parts); - return 0; - } -#endif - - /* No recognised partitioning schemes found - use defaults */ - nr_parts = epxa_default_partitions(mymtd, &parts); - if (nr_parts > 0) { - add_mtd_partitions(mymtd, parts, nr_parts); - return 0; - } - - /* If all else fails... */ - add_mtd_device(mymtd); - return 0; -} - -static void __exit epxa_mtd_cleanup(void) -{ - if (mymtd) { - if (nr_parts) - del_mtd_partitions(mymtd); - else - del_mtd_device(mymtd); - map_destroy(mymtd); - } - if (epxa_map.map_priv_1) { - iounmap((void *)epxa_map.map_priv_1); - epxa_map.map_priv_1 = 0; - } -} - - -/* - * This will do for now, once we decide which bootldr we're finally - * going to use then we'll remove this function and do it properly - * - * Partions are currently (as offsets from base of flash): - * 0x00000000 - 0x003FFFFF - bootloader (!) - * 0x00400000 - 0x00FFFFFF - Flashdisk - */ - -static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) -{ - struct mtd_partition *parts; - int ret, i; - int npartitions = 0; - char *names; - const char *name = "jffs"; - - printk("Using default partitions for %s\n",BOARD_NAME); - npartitions=1; - parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL); - memzero(parts,npartitions*sizeof(*parts)+strlen(name)); - if (!parts) { - ret = -ENOMEM; - goto out; - } - i=0; - names = (char *)&parts[npartitions]; - parts[i].name = names; - names += strlen(name) + 1; - strcpy(parts[i].name, name); - -#ifdef CONFIG_EPXA10DB - parts[i].size = FLASH_SIZE-0x00400000; - parts[i].offset = 0x00400000; -#else - parts[i].size = FLASH_SIZE-0x00180000; - parts[i].offset = 0x00180000; -#endif - - out: - *pparts = parts; - return npartitions; -} - - -module_init(epxa_mtd_init); -module_exit(epxa_mtd_cleanup); - -MODULE_AUTHOR("Clive Davies"); -MODULE_DESCRIPTION("Altera epxa mtd flash map"); -MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/mtd/maps/neponset-flash.c @@ -0,0 +1,109 @@ +/* + * Flash memory access on SA11x0 based devices + * + * (C) 2000 Nicolas Pitre + * + * $Id: neponset-flash.c,v 1.18 2001/07/14 00:59:17 thockin Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +static __u8 read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + ofs); +} + +static __u16 read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + ofs); +} + +static __u32 read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + ofs); +} + +static void copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +static void write8(struct map_info *map, __u8 d, unsigned long adr) +{ + writeb(d, map->map_priv_1 + adr); +} + +static void write16(struct map_info *map, __u16 d, unsigned long adr) +{ + writew(d, map->map_priv_1 + adr); +} + +static void write32(struct map_info *map, __u32 d, unsigned long adr) +{ + writel(d, map->map_priv_1 + adr); +} + +static void copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +#define MAX_SZ (32 * 1024 * 1024) + +static struct map_info neponset_map = { + name: "Neponset", + size: MAX_SZ, + buswidth: 4, + read8: read8, + read16: read16, + read32: read32, + copy_from: copy_from, + write8: write8, + write16: write16, + write32: write32, + copy_to: copy_to, +}; + +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +static struct mtd_info *neponset_mtd; + +int __init neponset_mtd_init(void) +{ + if (!machine_is_assabet() || !machine_has_neponset()) + return -ENODEV; + + neponset_map.map_priv_1 = (unsigned int)ioremap(0x08000000, MAX_SZ); + if (!neponset_map.map_priv_1) + return -ENOMEM; + + neponset_mtd = do_map_probe("cfi_probe", &neponset_map); + if (!neponset_mtd) + return -ENXIO; + neponset_mtd->module = THIS_MODULE; + add_mtd_device(neponset_mtd); + return 0; +} + +static void __exit neponset_mtd_cleanup(void) +{ + if (neponset_mtd) + map_destroy(neponset_mtd); + if (neponset_map.map_priv_1) + iounmap((void *)neponset_map.map_priv_1); +} + +module_init(neponset_mtd_init); +module_exit(neponset_mtd_cleanup); --- linux-2.4.27/drivers/mtd/maps/sa1100-flash.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/maps/sa1100-flash.c @@ -97,6 +97,32 @@ * entries. Thanks. */ +#ifdef CONFIG_SA1100_ADSAGC +#define ADSAGC_FLASH_SIZE 0x02000000 +static struct mtd_partition adsagc_partitions[] = { + { + name: "bootROM", + size: 0x80000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "zImage", + size: 0x100000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "ramdisk.gz", + size: 0x300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "User FS", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif + #ifdef CONFIG_SA1100_ADSBITSY #define ADSBITSY_FLASH_SIZE 0x02000000 static struct mtd_partition adsbitsy_partitions[] = { @@ -123,6 +149,32 @@ }; #endif +#ifdef CONFIG_SA1100_ADSBITSYPLUS +#define ADSBITSYPLUS_FLASH_SIZE 0x02000000 +static struct mtd_partition adsbitsyplus_partitions[] = { + { + name: "bootROM", + size: 0x80000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "zImage", + size: 0x100000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "ramdisk.gz", + size: 0x300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "User FS", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif + #ifdef CONFIG_SA1100_ASSABET /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ #define ASSABET4_FLASH_SIZE 0x00400000 @@ -438,7 +490,7 @@ #endif #ifdef CONFIG_SA1100_GRAPHICSMASTER -#define GRAPHICSMASTER_FLASH_SIZE 0x01000000 +#define GRAPHICSMASTER_FLASH_SIZE 0x02000000 static struct mtd_partition graphicsmaster_partitions[] = { { name: "zImage", @@ -507,6 +559,38 @@ } #endif +#ifdef CONFIG_SA1100_HACKKIT +#define HACKKIT_FLASH_SIZE 0x01000000 +static struct mtd_partition hackkit_partitions[] = { + { + name: "BLOB", + size: 0x00040000, + offset: 0x00000000, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "config", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + }, { + name: "kernel", + size: 0x00100000, + offset: MTDPART_OFS_APPEND, + }, { + name: "initrd", + size: 0x00180000, + offset: MTDPART_OFS_APPEND, + }, { + name: "rootfs", + size: 0x700000, + offset: MTDPART_OFS_APPEND, + }, { + name: "data", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif + #ifdef CONFIG_SA1100_HUW_WEBPANEL #define HUW_WEBPANEL_FLASH_SIZE 0x01000000 static struct mtd_partition huw_webpanel_partitions[] = { @@ -555,12 +639,12 @@ offset: 0x00540000, }, { name: "JORNADA720 usr local", - size: 0 /* will expand to the end of the flash */ + size: 0, /* will expand to the end of the flash */ offset: 0x00d00000, } }; -static void jornada720_set_vpp(int vpp) +static void jornada720_set_vpp(struct map_info *map, int vpp) { if (vpp) PPSR |= 0x80; @@ -571,6 +655,27 @@ #endif +#ifdef CONFIG_SA1100_NANOENGINE +/* nanoEngine has one 28F320B3B Flash part in bank 0: */ +#define NANOENGINE_FLASH_SIZE 0x00400000 +static struct mtd_partition nanoengine_partitions[] = { + { + name: "nanoEngine boot firmware and parameter table", + size: 0x00010000, /* 32K */ + offset: 0x00000000, + mask_flags: MTD_WRITEABLE, /* force read-only */ + },{ + name: "kernel/initrd reserved", + size: 0x002f0000, + offset: 0x00010000, + },{ + name: "experimental filesystem allocation", + size: 0x00100000, + offset: 0x00300000, + } +}; +#endif + #ifdef CONFIG_SA1100_PANGOLIN #define PANGOLIN_FLASH_SIZE 0x04000000 static struct mtd_partition pangolin_partitions[] = { @@ -699,6 +804,32 @@ }; #endif /* CONFIG_SA1100_SIMPAD */ +#ifdef CONFIG_SA1100_SIMPUTER +#define SIMPUTER_FLASH_SIZE 0x02000000 +static struct mtd_partition simputer_partitions[] = { + { + name: "blob+logo", + offset: 0, + size: 0x00040000 + }, + { + name: "kernel", + offset: MTDPART_OFS_APPEND, + size: 0x000C0000 + }, + { + name: "/(cramfs)", + offset: MTDPART_OFS_APPEND, + size: 0x00200000 + }, + { + name: "/usr/local(jffs2)", + offset: MTDPART_OFS_APPEND, + size: MTDPART_SIZ_FULL /* expand till the end */ + } +}; +#endif + #ifdef CONFIG_SA1100_STORK #define STORK_FLASH_SIZE 0x02000000 static struct mtd_partition stork_partitions[] = { @@ -766,7 +897,7 @@ #endif extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); -extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); +extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); static struct mtd_partition *parsed_parts; static struct mtd_info *mymtd; @@ -787,6 +918,14 @@ */ part_type = "static"; +#ifdef CONFIG_SA1100_ADSAGC + if (machine_is_adsagc()) { + parts = adsagc_partitions; + nb_parts = ARRAY_SIZE(adsagc_partitions); + sa1100_map.size = ADSAGC_FLASH_SIZE; + sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; + } +#endif #ifdef CONFIG_SA1100_ADSBITSY if (machine_is_adsbitsy()) { parts = adsbitsy_partitions; @@ -795,6 +934,14 @@ sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; } #endif +#ifdef CONFIG_SA1100_ADSBITSYPLUS + if (machine_is_adsbitsyplus()) { + parts = adsbitsyplus_partitions; + nb_parts = ARRAY_SIZE(adsbitsyplus_partitions); + sa1100_map.size = ADSBITSYPLUS_FLASH_SIZE; + sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; + } +#endif #ifdef CONFIG_SA1100_ASSABET if (machine_is_assabet()) { parts = assabet_partitions; @@ -869,6 +1016,13 @@ sa1100_map.set_vpp = h3600_set_vpp; } #endif +#ifdef CONFIG_SA1100_HACKKIT + if (machine_is_hackkit()) { + parts = hackkit_partitions; + nb_parts = ARRAY_SIZE(hackkit_partitions); + sa1100_map.size = HACKKIT_FLASH_SIZE; + } +#endif #ifdef CONFIG_SA1100_HUW_WEBPANEL if (machine_is_huw_webpanel()) { parts = huw_webpanel_partitions; @@ -884,6 +1038,13 @@ sa1100_map.set_vpp = jornada720_set_vpp; } #endif +#ifdef CONFIG_SA1100_NANOENGINE + if (machine_is_nanoengine()) { + parts = nanoengine_partitions; + nb_parts = ARRAY_SIZE(nanoengine_partitions); + sa1100_map.size = NANOENGINE_FLASH_SIZE; + } +#endif #ifdef CONFIG_SA1100_PANGOLIN if (machine_is_pangolin()) { parts = pangolin_partitions; @@ -919,6 +1080,13 @@ sa1100_map.size = SIMPAD_FLASH_SIZE; } #endif +#ifdef CONFIG_SA1100_SIMPUTER + if (machine_is_simputer()) { + parts = simputer_partitions; + nb_parts = ARRAY_SIZE(simputer_partitions); + sa1100_map.size = SIMPUTER_FLASH_SIZE; + } +#endif #ifdef CONFIG_SA1100_STORK if (machine_is_stork()) { parts = stork_partitions; @@ -953,7 +1121,9 @@ * specific machine settings might have been set above. */ printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); - mymtd = do_map_probe("cfi_probe", &sa1100_map); + mymtd = do_map_probe("jedec_probe", &sa1100_map); + if (!mymtd) + mymtd = do_map_probe("cfi_probe", &sa1100_map); ret = -ENXIO; if (!mymtd) goto out_err; --- linux-2.4.27/drivers/mtd/nand/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/mtd/nand/Config.in @@ -33,4 +33,8 @@ fi fi +if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + dep_tristate ' SmartMedia Card on Atmel AT91RM9200' CONFIG_MTD_AT91_SMARTMEDIA $CONFIG_MTD_NAND +fi + endmenu --- linux-2.4.27/drivers/net/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/Config.in @@ -30,9 +30,15 @@ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then source drivers/acorn/net/Config.in fi + if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + tristate ' AT91RM9200 Ethernet support' CONFIG_AT91_ETHER + if [ "$CONFIG_AT91_ETHER" = "y" -o "$CONFIG_AT91_ETHER" = "m" ]; then + bool ' RMII interface? ' CONFIG_AT91_ETHER_RMII + fi + fi fi if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then - tristate ' Altera Ether00 support' CONFIG_ETHER00 + tristate ' Altera Ether00 support' CONFIG_ETHER00 fi if [ "$CONFIG_PPC" = "y" ]; then dep_tristate ' MACE (Power Mac ethernet) support' CONFIG_MACE $CONFIG_ALL_PPC --- linux-2.4.27/drivers/net/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/Makefile @@ -244,6 +244,7 @@ # non-drivers/net drivers who want mii lib obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o obj-$(CONFIG_USB_USBNET) += mii.o +obj-$(CONFIG_AT91_ETHER) += mii.o obj-$(CONFIG_IBMVETH) += ibmveth.o @@ -270,4 +271,3 @@ rcpci.o: $(rcpci-objs) $(LD) -r -o $@ $(rcpci-objs) - --- linux-2.4.27/drivers/net/am79c961a.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/am79c961a.c @@ -54,25 +54,36 @@ #ifdef __arm__ static void write_rreg(u_long base, u_int reg, u_int val) { - __asm__("str%?h %1, [%2] @ NET_RAP - str%?h %0, [%2, #-4] @ NET_RDP - " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); + __asm__("str%?h %1, [%2] @ NET_RAP\n\t" + "str%?h %0, [%2, #-4] @ NET_RDP" + : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); } static inline unsigned short read_rreg(u_long base_addr, u_int reg) { unsigned short v; - __asm__("str%?h %1, [%2] @ NET_RAP - ldr%?h %0, [%2, #-4] @ NET_RDP - " : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464)); + __asm__("str%?h %1, [%2] @ NET_RAP\n\t" + "ldr%?h %0, [%2, #-4] @ NET_RDP" + : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464)); return v; } static inline void write_ireg(u_long base, u_int reg, u_int val) { - __asm__("str%?h %1, [%2] @ NET_RAP - str%?h %0, [%2, #8] @ NET_IDP - " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); + __asm__("str%?h %1, [%2] @ NET_RAP\n\t" + "str%?h %0, [%2, #8] @ NET_IDP" + : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); +} + +static inline unsigned short read_ireg(u_long base_addr, u_int reg) +{ + u_short v; + __asm__( + "str%?h %1, [%2] @ NAT_RAP\n\t" + "str%?h %0, [%2, #8] @ NET_IDP\n\t" + : "=r" (v) + : "r" (reg), "r" (ISAIO_BASE + 0x0464)); + return v; } #define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1)) @@ -91,16 +102,16 @@ } while (length > 8) { unsigned int tmp, tmp2; - __asm__ __volatile__(" - ldm%?ia %1!, {%2, %3} - str%?h %2, [%0], #4 - mov%? %2, %2, lsr #16 - str%?h %2, [%0], #4 - str%?h %3, [%0], #4 - mov%? %3, %3, lsr #16 - str%?h %3, [%0], #4 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) - : "0" (offset), "1" (buf)); + __asm__ __volatile__( + "ldm%?ia %1!, {%2, %3}\n\t" + "str%?h %2, [%0], #4\n\t" + "mov%? %2, %2, lsr #16\n\t" + "str%?h %2, [%0], #4\n\t" + "str%?h %3, [%0], #4\n\t" + "mov%? %3, %3, lsr #16\n\t" + "str%?h %3, [%0], #4" + : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) + : "0" (offset), "1" (buf)); length -= 8; } while (length > 0) { @@ -118,36 +129,36 @@ length = (length + 1) & ~1; if ((int)buf & 2) { unsigned int tmp; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - str%?b %2, [%1], #1 - mov%? %2, %2, lsr #8 - str%?b %2, [%1], #1 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); + __asm__ __volatile__( + "ldr%?h %2, [%0], #4\n\t" + "str%?b %2, [%1], #1\n\t" + "mov%? %2, %2, lsr #8\n\t" + "str%?b %2, [%1], #1" + : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); length -= 2; } while (length > 8) { unsigned int tmp, tmp2, tmp3; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - ldr%?h %3, [%0], #4 - orr%? %2, %2, %3, lsl #16 - ldr%?h %3, [%0], #4 - ldr%?h %4, [%0], #4 - orr%? %3, %3, %4, lsl #16 - stm%?ia %1!, {%2, %3} - " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) - : "0" (offset), "1" (buf)); + __asm__ __volatile__( + "ldr%?h %2, [%0], #4\n\t" + "ldr%?h %3, [%0], #4\n\t" + "orr%? %2, %2, %3, lsl #16\n\t" + "ldr%?h %3, [%0], #4\n\t" + "ldr%?h %4, [%0], #4\n\t" + "orr%? %3, %3, %4, lsl #16\n\t" + "stm%?ia %1!, {%2, %3}" + : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) + : "0" (offset), "1" (buf)); length -= 8; } while (length > 0) { unsigned int tmp; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - str%?b %2, [%1], #1 - mov%? %2, %2, lsr #8 - str%?b %2, [%1], #1 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); + __asm__ __volatile__( + "ldr%?h %2, [%0], #4\n\t" + "str%?b %2, [%1], #1\n\t" + "mov%? %2, %2, lsr #8\n\t" + "str%?b %2, [%1], #1" + : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); length -= 2; } } @@ -254,9 +265,27 @@ write_rreg (dev->base_addr, BASERXH, 0); write_rreg (dev->base_addr, CSR0, CSR0_STOP); write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO); + write_rreg (dev->base_addr, CSR4, CSR4_APAD_XMIT|CSR4_MFCOM|CSR4_RCVCCOM|CSR4_TXSTRTM|CSR4_JABM); write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT); } +static void am79c961_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned int lnkstat, carrier; + + lnkstat = read_ireg(dev->base_addr, ISALED0) & ISALED0_LNKST; + carrier = netif_carrier_ok(dev); + + if (lnkstat && !carrier) + netif_carrier_on(dev); + else if (!lnkstat && carrier) + netif_carrier_off(dev); + + mod_timer(&priv->timer, jiffies + 5*HZ); +} + /* * Open/initialize the board. */ @@ -274,6 +303,11 @@ am79c961_init_for_open(dev); + netif_carrier_off(dev); + + priv->timer.expires = jiffies; + add_timer(&priv->timer); + netif_start_queue(dev); return 0; @@ -288,7 +322,10 @@ struct dev_priv *priv = (struct dev_priv *)dev->priv; unsigned long flags; + del_timer_sync(&priv->timer); + netif_stop_queue(dev); + netif_carrier_off(dev); spin_lock_irqsave(priv->chip_lock, flags); write_rreg (dev->base_addr, CSR0, CSR0_STOP); @@ -413,15 +450,6 @@ unsigned int hdraddr, bufaddr; unsigned int head; unsigned long flags; - - /* FIXME: I thought the 79c961 could do padding - RMK ??? */ - if(length < ETH_ZLEN) - { - skb = skb_padto(skb, ETH_ZLEN); - if(skb == NULL) - return 0; - length = ETH_ZLEN; - } head = priv->txhead; hdraddr = priv->txhdr + (head << 3); @@ -431,7 +459,7 @@ head = 0; am_writebuffer (dev, bufaddr, skb->data, length); - am_writeword (dev, hdraddr + 4, -length); + am_writeword (dev, hdraddr + 4, -skb->len); am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); priv->txhead = head; @@ -448,6 +476,8 @@ if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN) netif_stop_queue(dev); + priv->stats.tx_bytes += skb->len; + dev_kfree_skb(skb); return 0; @@ -520,6 +550,7 @@ am79c961_tx(struct net_device *dev, struct dev_priv *priv) { do { + signed short len; u_int hdraddr; u_int status; @@ -555,6 +586,8 @@ continue; } priv->stats.tx_packets ++; + len = am_readword (dev, hdraddr + 4); + priv->stats.tx_bytes += -len; } while (priv->txtail != priv->txhead); netif_wake_queue(dev); @@ -565,17 +598,23 @@ { struct net_device *dev = (struct net_device *)dev_id; struct dev_priv *priv = (struct dev_priv *)dev->priv; - u_int status; + u_int status, n = 100; - status = read_rreg(dev->base_addr, CSR0); - write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA)); + do { + status = read_rreg(dev->base_addr, CSR0); + write_rreg(dev->base_addr, CSR0, status & + (CSR0_IENA|CSR0_TINT|CSR0_RINT| + CSR0_MERR|CSR0_MISS|CSR0_CERR|CSR0_BABL)); - if (status & CSR0_RINT) - am79c961_rx(dev, priv); - if (status & CSR0_TINT) - am79c961_tx(dev, priv); - if (status & CSR0_MISS) - priv->stats.rx_dropped ++; + if (status & CSR0_RINT) + am79c961_rx(dev, priv); + if (status & CSR0_TINT) + am79c961_tx(dev, priv); + if (status & CSR0_MISS) + priv->stats.rx_dropped ++; + if (status & CSR0_CERR) + mod_timer(&priv->timer, jiffies); + } while (--n && status & (CSR0_RINT | CSR0_TINT)); } /* @@ -587,10 +626,10 @@ { struct dev_priv *priv = (struct dev_priv *)dev->priv; - spin_lock_irq(priv->chip_lock); + spin_lock_irq(&priv->chip_lock); write_rreg (dev->base_addr, CSR0, CSR0_STOP); write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); - spin_unlock_irq(priv->chip_lock); + spin_unlock_irq(&priv->chip_lock); am79c961_ramtest(dev, 0x66); am79c961_ramtest(dev, 0x99); @@ -655,6 +694,11 @@ printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]); } + spin_lock_init(&priv->chip_lock); + init_timer(&priv->timer); + priv->timer.data = (unsigned long)dev; + priv->timer.function = am79c961_timer; + if (am79c961_hw_init(dev)) goto release; --- linux-2.4.27/drivers/net/am79c961a.h~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/am79c961a.h @@ -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); --- linux-2.4.27/drivers/net/cirrus.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/cirrus.c @@ -75,6 +75,7 @@ typedef struct { struct net_device_stats stats; u16 txlen; + u16 txafter; /* Default is After5 (0) */ } cirrus_t; typedef struct { @@ -230,13 +231,19 @@ cirrus_t *priv = (cirrus_t *) dev->priv; u16 status; + /* Tx start must be done with irq disabled + * else status can be wrong */ + disable_irq (dev->irq); + netif_stop_queue (dev); - cirrus_write (dev,PP_TxCMD,TxStart (After5)); + cirrus_write (dev,PP_TxCMD,TxStart (priv->txafter)); cirrus_write (dev,PP_TxLength,skb->len); status = cirrus_read (dev,PP_BusST); + enable_irq (dev->irq); + if ((status & TxBidErr)) { printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len); priv->stats.tx_errors++; @@ -249,7 +256,6 @@ printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name); priv->stats.tx_errors++; priv->txlen = 0; - /* FIXME: store skb and send it in interrupt handler */ return (1); } @@ -310,11 +316,18 @@ } if ((RegContent (status) & TxUnderrun)) { priv->stats.tx_errors++; - priv->stats.tx_fifo_errors++; + /* Shift start tx, if underruns come too often */ + switch (priv->stats.tx_fifo_errors++) { + case 3: priv->txafter = After381; break; + case 6: priv->txafter = After1021; break; + case 9: priv->txafter = AfterAll; break; + } + } + /* Wakeup only for tx events ! */ + if ((RegContent (status) & (TxUnderrun | Rdy4Tx))) { + priv->txlen = 0; + netif_wake_queue (dev); } - /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */ - priv->txlen = 0; - netif_wake_queue (dev); break; case TxCOL: @@ -428,7 +441,7 @@ else cirrus_clear (dev,PP_RxCTL,PromiscuousA); - if ((dev->flags & IFF_ALLMULTI) && dev->mc_list) + if ((dev->flags & IFF_ALLMULTI) || dev->mc_list) cirrus_set (dev,PP_RxCTL,MulticastA); else cirrus_clear (dev,PP_RxCTL,MulticastA); --- linux-2.4.27/drivers/net/cs89x0.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/cs89x0.c @@ -115,6 +115,7 @@ */ +#include #include #include #include @@ -427,18 +428,18 @@ /* if they give us an odd I/O address, then do ONE write to the address port, to get it back to address zero, where we expect to find the EISA signature word. An IO with a base of 0x3 - will skip the test for the ADD_PORT. */ + will skip the test for the ADD_PORT. */ if (ioaddr & 1) { if (net_debug > 1) printk(KERN_INFO "%s: odd ioaddr 0x%x\n", dev->name, ioaddr); - if ((ioaddr & 2) != 2) + if ((ioaddr & 2) != 2) if ((inw((ioaddr & ~3)+ ADD_PORT) & ADD_MASK) != ADD_SIG) { printk(KERN_ERR "%s: bad signature 0x%x\n", dev->name, inw((ioaddr & ~3)+ ADD_PORT)); retval = -ENODEV; goto out2; } - ioaddr &= ~3; + ioaddr &= ~3; outw(PP_ChipID, ioaddr + ADD_PORT); } printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT)); @@ -446,7 +447,7 @@ if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) { printk(KERN_ERR "%s: incorrect signature 0x%x\n", dev->name, inw(ioaddr + DATA_PORT)); - retval = -ENODEV; + retval = -ENODEV; goto out2; } @@ -477,7 +478,7 @@ dev->base_addr); reset_chip(dev); - + /* Here we read the current configuration of the chip. If there is no Extended EEPROM then the idea is to not disturb the chip configuration, it should have been correctly setup by automatic --- linux-2.4.27/drivers/net/ether00.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/ether00.c @@ -38,6 +38,7 @@ #include #include +static int ether00_get_ethernet_address(struct net_device* dev); MODULE_AUTHOR("Clive Davies"); MODULE_DESCRIPTION("Altera Ether00 IP core driver"); @@ -734,8 +735,11 @@ int result,tmp; struct net_priv* priv; - if (!is_valid_ether_addr(dev->dev_addr)) - return -EINVAL; + if (!ether00_get_ethernet_address(dev)){ + printk("%s: Invalid ethernet MAC address. Please set using " + "ifconfig\n", dev->name); + return -EINVAL; + } dev->base_addr=(unsigned int)ioremap_nocache(base,SZ_4K); @@ -906,10 +910,9 @@ } -static void ether00_get_ethernet_address(struct net_device* dev) +static int ether00_get_ethernet_address(struct net_device* dev) { struct mtd_info *mymtd=NULL; - int i; size_t retlen; /* @@ -926,11 +929,7 @@ #ifdef CONFIG_ARCH_CAMELOT #ifdef CONFIG_MTD /* get the mtd_info structure for the first mtd device*/ - for(i=0;iname,"EPXA10DB flash")) - break; - } + mymtd=get_mtd_device(NULL,0); if(!mymtd || !mymtd->read_user_prot_reg){ printk(KERN_WARNING "%s: Failed to read MAC address from flash\n",dev->name); @@ -947,9 +946,7 @@ #endif #endif - if (!is_valid_ether_addr(dev->dev_addr)) - printk("%s: Invalid ethernet MAC address. Please set using " - "ifconfig\n", dev->name); + return (is_valid_ether_addr(dev->dev_addr)); } @@ -966,8 +963,6 @@ dev->tx_timeout=ether00_tx_timeout; dev->watchdog_timeo=TX_TIMEOUT; - ether00_get_ethernet_address(dev); - SET_MODULE_OWNER(dev); return 0; } --- linux-2.4.27/drivers/net/irda/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/irda/Config.in @@ -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 --- linux-2.4.27/drivers/net/irda/sa1100_ir.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/irda/sa1100_ir.c @@ -38,11 +38,7 @@ #include -#ifndef CONFIG_SA1100_H3600 -#define clr_h3600_egpio(x) do { } while (0) -#define set_h3600_egpio(x) do { } while (0) -#endif - +/* Yopy wants fixing */ #ifndef GPIO_IRDA_FIR #define GPIO_IRDA_FIR (0) #endif @@ -174,8 +170,8 @@ if (machine_is_assabet()) ASSABET_BCR_clear(ASSABET_BCR_IRDA_FSEL); - if (machine_is_h3600()) - clr_h3600_egpio(EGPIO_H3600_IR_FSEL); + if (machine_is_h3xxx()) + clr_h3600_egpio(IPAQ_EGPIO_IR_FSEL); if (machine_is_yopy()) PPSR &= ~GPIO_IRDA_FIR; @@ -199,8 +195,8 @@ if (machine_is_assabet()) ASSABET_BCR_set(ASSABET_BCR_IRDA_FSEL); - if (machine_is_h3600()) - set_h3600_egpio(EGPIO_H3600_IR_FSEL); + if (machine_is_h3xxx()) + set_h3600_egpio(IPAQ_EGPIO_IR_FSEL); if (machine_is_yopy()) PPSR |= GPIO_IRDA_FIR; @@ -246,10 +242,7 @@ static inline int sa1100_irda_set_power_h3600(struct sa1100_irda *si, unsigned int state) { - if (state) - set_h3600_egpio(EGPIO_H3600_IR_ON); - else - clr_h3600_egpio(EGPIO_H3600_IR_ON); + assign_h3600_egpio( IPAQ_EGPIO_IR_ON, state ); return 0; } @@ -283,7 +276,7 @@ if (machine_is_assabet()) ret = sa1100_irda_set_power_assabet(si, state); - if (machine_is_h3600()) + if (machine_is_h3xxx()) ret = sa1100_irda_set_power_h3600(si, state); if (machine_is_yopy()) ret = sa1100_irda_set_power_yopy(si, state); @@ -727,11 +720,6 @@ netif_wake_queue(dev); } -/* - * Note that we will never build up a backlog of frames; the protocol is a - * half duplex protocol which basically means we transmit a frame, we - * receive a frame, we transmit the next frame etc. - */ static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) { struct sa1100_irda *si = dev->priv; @@ -758,6 +746,8 @@ } if (!IS_FIR(si)) { + netif_stop_queue(dev); + si->tx_buff.data = si->tx_buff.head; si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, si->tx_buff.truesize); --- linux-2.4.27/drivers/net/irda/w83977af_ir.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/irda/w83977af_ir.c @@ -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; --- linux-2.4.27/drivers/net/smc9194.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/smc9194.c @@ -12,8 +12,8 @@ . AUI/TP selection ( mine has 10Base2/10BaseT select ) . . Arguments: - . io = for the base address - . irq = for the IRQ + . io = for the base address + . irq = for the IRQ . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 ) . . author: @@ -51,12 +51,21 @@ . allocation . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ" + . 06/23/01 Russell King Separate out IO functions for different bus + . types. + . Use dev->name instead of CARDNAME for printk + . Add ethtool support, full duplex support + . Add LAN91C96 support. . 11/08/01 Matt Domsch Use common crc32 function ----------------------------------------------------------------------------*/ +#define DRV_NAME "smc9194" +#define DRV_VERSION "0.15" + static const char version[] = - "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)\n"; + DRV_NAME ".c:v" DRV_VERSION " 12/15/00 by Erik Stahlman (erik@vt.edu)\n"; +#include #include #include #include @@ -69,16 +78,26 @@ #include #include #include +#include #include #include -#include -#include #include +#include #include #include #include +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_SA1100 +#include +#include +#endif + #include "smc9194.h" /*------------------------------------------------------------------------ . @@ -152,29 +171,27 @@ -------------------------------------------------------------------------*/ #define CARDNAME "SMC9194" +static const char *chip_ids[15] = { + NULL, + NULL, + NULL, + "SMC91C90/91C92", /* 3 */ + "SMC91C94/91C96", /* 4 */ + "SMC91C95", /* 5 */ + NULL, + "SMC91C100", /* 7 */ + "SMC91C100FD", /* 8 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; -/* store this information for the driver.. */ -struct smc_local { - /* - these are things that the kernel wants me to keep, so users - can find out semi-useless statistics of how well the card is - performing - */ - struct net_device_stats stats; - - /* - If I have to wait until memory is available to send - a packet, I will store the skbuff here, until I get the - desired memory. Then, I'll send it out and free it. - */ - struct sk_buff * saved_skb; - - /* - . This keeps track of how many packets that I have - . sent out. When an TX_EMPTY interrupt comes, I know - . that all of these have been sent. - */ - int packets_waiting; +static const char * interfaces[2] = { + "TP", + "AUI" }; @@ -202,6 +219,11 @@ static int smc_open(struct net_device *dev); /* + . This handles the ethtool interface +*/ +static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); + +/* . Our watchdog timed out. Called by the networking layer */ static void smc_timeout(struct net_device *dev); @@ -217,11 +239,11 @@ . This routine allows the proc file system to query the driver's . statistics. */ -static struct net_device_stats * smc_query_statistics( struct net_device *dev); +static struct net_device_stats * smc_query_statistics(struct net_device *dev); /* - . Finally, a call to set promiscuous mode ( for TCPDUMP and related - . programs ) and multicast modes. + . Finally, a call to set promiscuous mode (for TCPDUMP and related + . programs) and multicast modes. */ static void smc_set_multicast_list(struct net_device *dev); @@ -240,12 +262,12 @@ . This is a separate procedure to handle the receipt of a packet, to . leave the interrupt code looking slightly cleaner */ -static inline void smc_rcv( struct net_device *dev ); +static inline void smc_rcv(struct net_device *dev); /* . This handles a TX interrupt, which is only called when an error . relating to a packet is sent. */ -static inline void smc_tx( struct net_device * dev ); +static inline void smc_tx(struct net_device * dev); /* ------------------------------------------------------------ @@ -261,39 +283,287 @@ */ static int smc_probe(struct net_device *dev, int ioaddr); -/* - . A rather simple routine to print out a packet for debugging purposes. -*/ -#if SMC_DEBUG > 2 -static void print_packet( byte *, int ); -#endif - -#define tx_done(dev) 1 - /* this is called to actually send the packet to the chip */ -static void smc_hardware_send_packet( struct net_device * dev ); +static void smc_hardware_send_packet(struct net_device * dev); /* Since I am not sure if I will have enough room in the chip's ram . to store the packet, I call this routine, which either sends it . now, or generates an interrupt when the card is ready for the . packet */ -static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device *dev ); +static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *dev); /* this does a soft reset on the device */ -static void smc_reset( int ioaddr ); +static void smc_reset(struct net_device *dev); /* Enable Interrupts, Receive, and Transmit */ -static void smc_enable( int ioaddr ); +static void smc_enable(struct net_device *dev); /* this puts the device in an inactive state */ -static void smc_shutdown( int ioaddr ); +static void smc_shutdown(struct net_device *dev); /* This routine will find the IRQ of the driver if one is not . specified in the input to the device. */ -static int smc_findirq( int ioaddr ); +static int smc_findirq(struct net_device *dev); +#ifndef CONFIG_ASSABET_NEPONSET /* - . Function: smc_reset( int ioaddr ) + * 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 + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/ +#if SMC_DEBUG > 2 +static void print_packet(byte * buf, int length) +{ + int i; + int remainder; + int lines; + + printk("Packet of length %d \n", length); + lines = length / 16; + remainder = length % 16; + + for (i = 0; i < lines ; i ++) { + int cur; + + for (cur = 0; cur < 8; cur ++) { + byte a, b; + + a = *(buf ++); + b = *(buf ++); + printk("%02x%02x ", a, b); + } + printk("\n"); + } + for (i = 0; i < remainder/2 ; i++) { + byte a, b; + + a = *(buf ++); + b = *(buf ++); + printk("%02x%02x ", a, b); + } + if (remainder & 1) { + byte a; + + a = *buf++; + printk("%02x", a); + } + printk("\n"); +} +#else +#define print_packet(buf,len) do { } while (0) +#endif + +/* + . Function: smc_reset(struct net_device *dev) . Purpose: . This sets the SMC91xx chip to its normal state, hopefully from whatever . mess that any other DOS driver has put it in. @@ -309,36 +579,37 @@ . 5. clear all interrupts . */ -static void smc_reset( int ioaddr ) +static void smc_reset(struct net_device *dev) { + u_int ioaddr = dev->base_addr; + /* This resets the registers mostly to defaults, but doesn't affect EEPROM. That seems unnecessary */ - SMC_SELECT_BANK( 0 ); - outw( RCR_SOFTRESET, ioaddr + RCR ); + SMC_SELECT_BANK(0); + smc_outw(RCR_SOFTRESET, ioaddr, RCR); /* this should pause enough for the chip to be happy */ - SMC_DELAY( ); + SMC_DELAY(); /* Set the transmit and receive configuration registers to default values */ - outw( RCR_CLEAR, ioaddr + RCR ); - outw( TCR_CLEAR, ioaddr + TCR ); + smc_outw(RCR_CLEAR, ioaddr, RCR); + smc_outw(TCR_CLEAR, ioaddr, TCR); /* set the control register to automatically release successfully transmitted packets, to make the best use out of our limited memory */ - SMC_SELECT_BANK( 1 ); - outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL ); + SMC_SELECT_BANK(1); + smc_outw(smc_inw(ioaddr, CONTROL) | CTL_AUTO_RELEASE, ioaddr, CONTROL); /* Reset the MMU */ - SMC_SELECT_BANK( 2 ); - outw( MC_RESET, ioaddr + MMU_CMD ); + SMC_SELECT_BANK(2); + smc_outw(MC_RESET, ioaddr, MMU_CMD); /* Note: It doesn't seem that waiting for the MMU busy is needed here, but this is a place where future chipsets _COULD_ break. Be wary of issuing another MMU command right after this */ - - outb( 0, ioaddr + INT_MASK ); + SMC_SET_INT(0); } /* @@ -349,20 +620,21 @@ . 2. Enable the receiver . 3. Enable interrupts */ -static void smc_enable( int ioaddr ) +static void smc_enable(struct net_device *dev) { - SMC_SELECT_BANK( 0 ); + u_int ioaddr = dev->base_addr; + SMC_SELECT_BANK(0); /* see the header file for options in TCR/RCR NORMAL*/ - outw( TCR_NORMAL, ioaddr + TCR ); - outw( RCR_NORMAL, ioaddr + RCR ); + smc_outw(TCR_NORMAL, ioaddr, TCR); + smc_outw(RCR_NORMAL, ioaddr, RCR); /* now, enable interrupts */ - SMC_SELECT_BANK( 2 ); - outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK ); + SMC_SELECT_BANK(2); + SMC_SET_INT(SMC_INTERRUPT_MASK); } /* - . Function: smc_shutdown + . Function: smc_shutdown(struct net_device *dev) . Purpose: closes down the SMC91xxx chip. . Method: . 1. zero the interrupt mask @@ -375,26 +647,28 @@ . the manual says that it will wake up in response to any I/O requests . in the register space. Empirical results do not show this working. */ -static void smc_shutdown( int ioaddr ) +static void smc_shutdown(struct net_device *dev) { + u_int ioaddr = dev->base_addr; + /* no more interrupts for me */ - SMC_SELECT_BANK( 2 ); - outb( 0, ioaddr + INT_MASK ); + SMC_SELECT_BANK(2); + SMC_SET_INT(0); /* and tell the card to stay away from that nasty outside world */ - SMC_SELECT_BANK( 0 ); - outb( RCR_CLEAR, ioaddr + RCR ); - outb( TCR_CLEAR, ioaddr + TCR ); + SMC_SELECT_BANK(0); + smc_outb(RCR_CLEAR, ioaddr, RCR); + smc_outb(TCR_CLEAR, ioaddr, TCR); #if 0 /* finally, shut the chip down */ - SMC_SELECT_BANK( 1 ); - outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL ); + SMC_SELECT_BANK(1); + smc_outw(smc_inw(ioaddr, CONTROL), CTL_POWERDOWN, ioaddr, CONTROL); #endif } /* - . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) + . Function: smc_setmulticast(int ioaddr, int count, dev_mc_list * adds) . Purpose: . This sets the internal hardware table to filter out unwanted multicast . packets before they take up memory. @@ -411,26 +685,28 @@ */ -static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) { +static void smc_setmulticast(struct net_device *dev, int count, struct dev_mc_list * addrs) +{ + u_int ioaddr = dev->base_addr; int i; - unsigned char multicast_table[ 8 ]; + unsigned char multicast_table[8]; struct dev_mc_list * cur_addr; /* table for flipping the order of 3 bits */ unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* start with a table of all zeros: reject all */ - memset( multicast_table, 0, sizeof( multicast_table ) ); + memset(multicast_table, 0, sizeof(multicast_table)); cur_addr = addrs; - for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next ) { + for (i = 0; i < count ; i ++, cur_addr = cur_addr->next) { int position; /* do we have a pointer here? */ - if ( !cur_addr ) + if (!cur_addr) break; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ - if ( !( *cur_addr->dmi_addr & 1 ) ) + if (!(*cur_addr->dmi_addr & 1)) continue; /* only use the low order bits */ @@ -442,15 +718,15 @@ } /* now, the table can be loaded into the chipset */ - SMC_SELECT_BANK( 3 ); + SMC_SELECT_BANK(3); - for ( i = 0; i < 8 ; i++ ) { - outb( multicast_table[i], ioaddr + MULTICAST1 + i ); + for (i = 0; i < 8 ; i++) { + smc_outb(multicast_table[i], ioaddr, MULTICAST1 + i); } } /* - . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * ) + . Function: smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *) . Purpose: . Attempt to allocate memory for a packet, if chip-memory is not . available, then tell the card to generate an interrupt when it @@ -465,10 +741,10 @@ . o (NO): Enable interrupts and let the interrupt handler deal with it. . o (YES):Send it now. */ -static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * dev ) +static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device * dev) { struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned short ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; word length; unsigned short numPages; word time_out; @@ -477,15 +753,16 @@ /* Well, I want to send the packet.. but I don't know if I can send it right now... */ - if ( lp->saved_skb) { + if (lp->saved_skb) { /* THIS SHOULD NEVER HAPPEN. */ lp->stats.tx_aborted_errors++; - printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); + printk("%s: Bad Craziness - sent packet while busy.\n", + dev->name); return 1; } length = skb->len; - + if(length < ETH_ZLEN) { skb = skb_padto(skb, ETH_ZLEN); @@ -497,18 +774,18 @@ length = ETH_ZLEN; } lp->saved_skb = skb; - + /* ** The MMU wants the number of pages to be the number of 256 bytes - ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** 'pages', minus 1 (since a packet can't ever have 0 pages :)) ** ** Pkt size for allocating is data length +6 (for additional status words, ** length and ctl!) If odd size last byte is included in this header. */ - numPages = ((length & 0xfffe) + 6) / 256; + numPages = ((length & 0xfffe) + 6) / 256; - if (numPages > 7 ) { - printk(CARDNAME": Far too big packet error. \n"); + if (numPages > 7) { + printk("%s: Far too big packet error.\n", dev->name); /* freeing the packet is a good thing here... but should . any packets of this size get down here? */ dev_kfree_skb (skb); @@ -517,12 +794,13 @@ netif_wake_queue(dev); return 0; } + /* either way, a packet is waiting now */ lp->packets_waiting++; /* now, try to allocate the memory */ - SMC_SELECT_BANK( 2 ); - outw( MC_ALLOC | numPages, ioaddr + MMU_CMD ); + SMC_SELECT_BANK(2); + smc_outw(MC_ALLOC | numPages, ioaddr, MMU_CMD); /* . Performance Hack . @@ -539,21 +817,21 @@ do { word status; - status = inb( ioaddr + INTERRUPT ); - if ( status & IM_ALLOC_INT ) { + status = smc_inb(ioaddr, INTERRUPT); + if (status & IM_ALLOC_INT) { /* acknowledge the interrupt */ - outb( IM_ALLOC_INT, ioaddr + INTERRUPT ); - break; + smc_outb(IM_ALLOC_INT, ioaddr, INTERRUPT); + break; } - } while ( -- time_out ); + } while (-- time_out); - if ( !time_out ) { + if (!time_out) { /* oh well, wait until the chip finds memory later */ - SMC_ENABLE_INT( IM_ALLOC_INT ); - PRINTK2((CARDNAME": memory allocation deferred. \n")); + SMC_ENABLE_INT(IM_ALLOC_INT); + PRINTK2(("%s: memory allocation deferred.\n", dev->name)); /* it's deferred, but I'll handle it later */ - return 0; - } + return 0; + } /* or YES! I can send the packet now.. */ smc_hardware_send_packet(dev); netif_wake_queue(dev); @@ -561,46 +839,46 @@ } /* - . Function: smc_hardware_send_packet(struct net_device * ) + . Function: smc_hardware_send_packet(struct net_device *) . Purpose: . This sends the actual packet to the SMC9xxx chip. . . Algorithm: . First, see if a saved_skb is available. - . ( this should NOT be called if there is no 'saved_skb' + . (this should NOT be called if there is no 'saved_skb' . Now, find the packet number that the chip allocated . Point the data pointers at it in memory . Set the length word in the chip's memory . Dump the packet to chip memory - . Check if a last byte is needed ( odd length packet ) + . Check if a last byte is needed (odd length packet) . if so, set the control flag right . Tell the card to send it . Enable the transmit interrupt, so I know if it failed . Free the kernel data if I actually sent it. */ -static void smc_hardware_send_packet( struct net_device * dev ) +static void smc_hardware_send_packet(struct net_device *dev) { struct smc_local *lp = (struct smc_local *)dev->priv; - byte packet_no; - struct sk_buff * skb = lp->saved_skb; - word length; - unsigned short ioaddr; - byte * buf; - - ioaddr = dev->base_addr; + struct sk_buff *skb = lp->saved_skb; + word length, lastword; + u_int ioaddr = dev->base_addr; + byte packet_no; + byte *buf; - if ( !skb ) { - PRINTK((CARDNAME": In XMIT with no packet to send \n")); + if (!skb) { + PRINTK(("%s: In XMIT with no packet to send\n", dev->name)); return; } + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; buf = skb->data; /* If I get here, I _know_ there is a packet slot waiting for me */ - packet_no = inb( ioaddr + PNR_ARR + 1 ); - if ( packet_no & 0x80 ) { + packet_no = smc_inb(ioaddr, PNR_ARR + 1); + if (packet_no & 0x80) { /* or isn't there? BAD CHIP! */ - printk(KERN_DEBUG CARDNAME": Memory allocation failed. \n"); + printk(KERN_DEBUG "%s: Memory allocation failed.\n", + dev->name); dev_kfree_skb_any(skb); lp->saved_skb = NULL; netif_wake_queue(dev); @@ -608,26 +886,19 @@ } /* we have a packet address, so tell the card to use it */ - outb( packet_no, ioaddr + PNR_ARR ); + smc_outb(packet_no, ioaddr, PNR_ARR); /* point to the beginning of the packet */ - outw( PTR_AUTOINC , ioaddr + POINTER ); + smc_outw(PTR_AUTOINC, ioaddr, POINTER); - PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length )); -#if SMC_DEBUG > 2 - print_packet( buf, length ); -#endif + PRINTK3(("%s: Trying to xmit packet of length %x\n", + dev->name, length)); - /* send the packet length ( +6 for status, length and ctl byte ) - and the status word ( set to zeros ) */ -#ifdef USE_32_BIT - outl( (length +6 ) << 16 , ioaddr + DATA_1 ); -#else - outw( 0, ioaddr + DATA_1 ); - /* send the packet length ( +6 for status words, length, and ctl*/ - outb( (length+6) & 0xFF,ioaddr + DATA_1 ); - outb( (length+6) >> 8 , ioaddr + DATA_1 ); -#endif + print_packet(buf, length); + + /* send the packet length (+6 for status, length and ctl byte) + and the status word (set to zeros) */ + smc_outl((length + 6) << 16, ioaddr, DATA_1); /* send the actual data . I _think_ it's faster to send the longs first, and then @@ -636,32 +907,22 @@ . a good idea to check which is optimal? But that could take . almost as much time as is saved? */ -#ifdef USE_32_BIT - if ( length & 0x2 ) { - outsl(ioaddr + DATA_1, buf, length >> 2 ); - outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); - } - else - outsl(ioaddr + DATA_1, buf, length >> 2 ); -#else - outsw(ioaddr + DATA_1 , buf, (length ) >> 1); -#endif - /* Send the last byte, if there is one. */ + smc_outs(ioaddr, DATA_1, buf, length); - if ( (length & 1) == 0 ) { - outw( 0, ioaddr + DATA_1 ); - } else { - outb( buf[length -1 ], ioaddr + DATA_1 ); - outb( 0x20, ioaddr + DATA_1); - } + /* Send the last byte, if there is one. */ + if ((length & 1) == 0) + lastword = 0; + else + lastword = 0x2000 | buf[length - 1]; + smc_outw(lastword, ioaddr, DATA_1); /* enable the interrupts */ - SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); + SMC_ENABLE_INT(IM_TX_INT | IM_TX_EMPTY_INT); /* and let the chipset deal with it */ - outw( MC_ENQUEUE , ioaddr + MMU_CMD ); + smc_outw(MC_ENQUEUE, ioaddr, MMU_CMD); - PRINTK2((CARDNAME": Sent packet of length %d \n",length)); + PRINTK2(("%s: Sent packet of length %d\n", dev->name, length)); lp->saved_skb = NULL; dev_kfree_skb_any (skb); @@ -676,7 +937,7 @@ /*------------------------------------------------------------------------- | - | smc_init( struct net_device * dev ) + | smc_init(struct net_device * dev) | Input parameters: | dev->base_addr == 0, try to find all possible locations | dev->base_addr == 1, return failure code @@ -691,6 +952,65 @@ */ int __init smc_init(struct net_device *dev) { + int ret = -ENODEV; +#if defined(CONFIG_ASSABET_NEPONSET) + if (machine_is_assabet() && machine_has_neponset()) { + unsigned int *addr; + unsigned char ecor; + unsigned long flags; + + NCR_0 |= NCR_ENET_OSC_EN; + dev->irq = IRQ_NEPONSET_SMC9196; + + /* + * Map the attribute space. This is overkill, but clean. + */ + addr = ioremap(0x18000000 + (1 << 25), 64 * 1024 * 4); + if (!addr) + return -ENOMEM; + + /* + * Reset the device. We must disable IRQs around this. + */ + local_irq_save(flags); + ecor = readl(addr + ECOR) & ~ECOR_RESET; + writel(ecor | ECOR_RESET, addr + ECOR); + udelay(100); + + /* + * The device will ignore all writes to the enable bit while + * reset is asserted, even if the reset bit is cleared in the + * same write. Must clear reset first, then enable the device. + */ + writel(ecor, addr + ECOR); + writel(ecor | ECOR_ENABLE, addr + ECOR); + + /* + * Force byte mode. + */ + writel(readl(addr + ECSR) | ECSR_IOIS8, addr + ECSR); + local_irq_restore(flags); + + iounmap(addr); + + /* + * Wait for the chip to wake up. + */ + mdelay(1); + + /* + * Map the real registers. + */ + addr = ioremap(0x18000000, 8 * 1024); + if (!addr) + return -ENOMEM; + + ret = smc_probe(dev, (int)addr); + if (ret) + iounmap(addr); + } + +#elif defined(CONFIG_ISA) int i; int base_addr = dev->base_addr; @@ -708,7 +1028,8 @@ return 0; /* couldn't find anything */ - return -ENODEV; +#endif + return ret; } /*---------------------------------------------------------------------- @@ -718,10 +1039,11 @@ . interrupt, so an auto-detect routine can detect it, and find the IRQ, ------------------------------------------------------------------------ */ -int __init smc_findirq( int ioaddr ) +int __init smc_findirq(struct net_device *dev) { int timeout = 20; unsigned long cookie; + u_int ioaddr = dev->base_addr; /* I have to do a STI() here, because this is called from @@ -737,26 +1059,25 @@ * when done. */ - + /* enable ALLOCation interrupts ONLY. */ SMC_SELECT_BANK(2); - /* enable ALLOCation interrupts ONLY */ - outb( IM_ALLOC_INT, ioaddr + INT_MASK ); + SMC_SET_INT(IM_ALLOC_INT); /* . Allocate 512 bytes of memory. Note that the chip was just . reset so all the memory is available */ - outw( MC_ALLOC | 1, ioaddr + MMU_CMD ); + smc_outw(MC_ALLOC | 1, ioaddr, MMU_CMD); /* . Wait until positive that the interrupt has been generated */ - while ( timeout ) { + while (timeout) { byte int_status; - int_status = inb( ioaddr + INTERRUPT ); + int_status = smc_inb(ioaddr, INTERRUPT); - if ( int_status & IM_ALLOC_INT ) + if (int_status & IM_ALLOC_INT) break; /* got the interrupt */ timeout--; } @@ -775,7 +1096,7 @@ SMC_DELAY(); /* and disable all interrupts again */ - outb( 0, ioaddr + INT_MASK ); + SMC_SET_INT(0); /* clear hardware interrupts again, because that's how it was when I was called... */ @@ -785,8 +1106,87 @@ return probe_irq_off(cookie); } +static int __init smc_probe_chip(struct net_device *dev, int ioaddr) +{ + unsigned int temp; + + /* First, see if the high byte is 0x33 */ + temp = smc_inw(ioaddr, BANK_SELECT); + if ((temp & 0xFF00) != 0x3300) + return -ENODEV; + + /* The above MIGHT indicate a device, but I need to write to further + test this. */ + smc_outw(0, ioaddr, BANK_SELECT); + temp = smc_inw(ioaddr, BANK_SELECT); + if ((temp & 0xFF00) != 0x3300) + return -ENODEV; + +#ifndef CONFIG_ASSABET_NEPONSET + /* well, we've already written once, so hopefully another time won't + hurt. This time, I need to switch the bank register to bank 1, + so I can access the base address register */ + SMC_SELECT_BANK(1); + temp = smc_inw(ioaddr, BASE); + if (ioaddr != (temp >> 3 & 0x3E0)) { + printk("%s: IOADDR %x doesn't match configuration (%x)." + "Probably not a SMC chip\n", dev->name, + ioaddr, (base_address_register >> 3) & 0x3E0); + /* well, the base address register didn't match. Must not have + been a SMC chip after all. */ + return -ENODEV; + } +#endif + + return 0; +} + +/* + . If dev->irq is 0, then the device has to be banged on to see + . what the IRQ is. + . + . This banging doesn't always detect the IRQ, for unknown reasons. + . a workaround is to reset the chip and try again. + . + . Interestingly, the DOS packet driver *SETS* the IRQ on the card to + . be what is requested on the command line. I don't do that, mostly + . because the card that I have uses a non-standard method of accessing + . the IRQs, and because this _should_ work in most configurations. + . + . Specifying an IRQ is done with the assumption that the user knows + . what (s)he is doing. No checking is done!!!! + . +*/ +static int __init smc_probe_irq(struct net_device *dev) +{ + if (dev->irq < 2) { + int trials; + + trials = 3; + while (trials--) { + dev->irq = smc_findirq(dev); + if (dev->irq) + break; + /* kick the card and try again */ + smc_reset(dev); + } + } + if (dev->irq == 0) { + printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n", + dev->name); + return -ENODEV; + } + + /* + * Some machines (eg, PCs) need to cannonicalize their IRQs. + */ + dev->irq = irq_cannonicalize(dev->irq); + + return 0; +} + /*---------------------------------------------------------------------- - . Function: smc_probe( int ioaddr ) + . Function: smc_probe(struct net_device *dev, int ioaddr) . . Purpose: . Tests to see if a given ioaddr points to an SMC9xxx chip. @@ -816,16 +1216,14 @@ */ static int __init smc_probe(struct net_device *dev, int ioaddr) { + struct smc_local *smc; int i, memory, retval; static unsigned version_printed; - unsigned int bank; const char *version_string; - const char *if_string; /* registers */ word revision_register; - word base_address_register; word configuration_register; word memory_info_register; word memory_cfg_register; @@ -834,44 +1232,24 @@ if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) return -EBUSY; - /* First, see if the high byte is 0x33 */ - bank = inw( ioaddr + BANK_SELECT ); - if ( (bank & 0xFF00) != 0x3300 ) { - retval = -ENODEV; - goto err_out; - } - /* The above MIGHT indicate a device, but I need to write to further - test this. */ - outw( 0x0, ioaddr + BANK_SELECT ); - bank = inw( ioaddr + BANK_SELECT ); - if ( (bank & 0xFF00 ) != 0x3300 ) { - retval = -ENODEV; - goto err_out; - } - /* well, we've already written once, so hopefully another time won't - hurt. This time, I need to switch the bank register to bank 1, - so I can access the base address register */ - SMC_SELECT_BANK(1); - base_address_register = inw( ioaddr + BASE ); - if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) { - printk(CARDNAME ": IOADDR %x doesn't match configuration (%x)." - "Probably not a SMC chip\n", - ioaddr, base_address_register >> 3 & 0x3E0 ); - /* well, the base address register didn't match. Must not have - been a SMC chip after all. */ - retval = -ENODEV; + /* + * Do the basic probes. + */ + retval = smc_probe_chip(dev, ioaddr); + if (retval) goto err_out; - } /* check if the revision register is something that I recognize. These might need to be added to later, as future revisions could be added. */ SMC_SELECT_BANK(3); - revision_register = inw( ioaddr + REVISION ); - if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) { + revision_register = smc_inw(ioaddr, REVISION); + version_string = chip_ids[(revision_register >> 4) & 15]; + if (!version_string) { /* I don't recognize this chip, so... */ - printk(CARDNAME ": IO %x: Unrecognized revision register:" - " %x, Contact author. \n", ioaddr, revision_register ); + printk("%s: IO %x: unrecognized revision register: %x, " + "contact author.\n", dev->name, ioaddr, + revision_register); retval = -ENODEV; goto err_out; @@ -882,138 +1260,122 @@ against the hardware address, or do some other tests. */ if (version_printed++ == 0) - printk("%s", version); + printk(KERN_INFO "%s", version); /* fill in some of the fields */ dev->base_addr = ioaddr; /* - . Get the MAC address ( bank 1, regs 4 - 9 ) + . Get the MAC address (bank 1, regs 4 - 9) */ - SMC_SELECT_BANK( 1 ); - for ( i = 0; i < 6; i += 2 ) { + SMC_SELECT_BANK(1); + for (i = 0; i < 6; i += 2) { word address; - address = inw( ioaddr + ADDR0 + i ); - dev->dev_addr[ i + 1] = address >> 8; - dev->dev_addr[ i ] = address & 0xFF; + address = smc_inw(ioaddr, ADDR0 + i); + dev->dev_addr[i + 1] = address >> 8; + dev->dev_addr[i] = address & 0xFF; } + if (!is_valid_ether_addr(dev->dev_addr)) + printk("%s: Invalid ethernet MAC address. Please set using " + "ifconfig\n", dev->name); + /* get the memory information */ - SMC_SELECT_BANK( 0 ); - memory_info_register = inw( ioaddr + MIR ); - memory_cfg_register = inw( ioaddr + MCR ); - memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */ - memory *= 256 * ( memory_info_register & 0xFF ); + SMC_SELECT_BANK(0); + memory_info_register = smc_inw(ioaddr, MIR); + memory_cfg_register = smc_inw(ioaddr, MCR); + memory = (memory_cfg_register >> 9) & 0x7; /* multiplier */ + memory *= 256 * (memory_info_register & 0xFF); + + /* now, reset the chip, and put it into a known state */ + smc_reset(dev); /* - Now, I want to find out more about the chip. This is sort of - redundant, but it's cleaner to have it in both, rather than having - one VERY long probe procedure. - */ - SMC_SELECT_BANK(3); - revision_register = inw( ioaddr + REVISION ); - version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ]; - if ( !version_string ) { - /* I shouldn't get here because this call was done before.... */ - retval = -ENODEV; + * Ok, now that we have everything in a + * sane state, probe for the interrupt. + */ + retval = smc_probe_irq(dev); + if (retval) goto err_out; - } - /* is it using AUI or 10BaseT ? */ - if ( dev->if_port == 0 ) { - SMC_SELECT_BANK(1); - configuration_register = inw( ioaddr + CONFIG ); - if ( configuration_register & CFG_AUI_SELECT ) - dev->if_port = 2; - else - dev->if_port = 1; + /* Initialize the private structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL); + if (dev->priv == NULL) { + retval = -ENOMEM; + goto err_out; + } } - if_string = interfaces[ dev->if_port - 1 ]; - /* now, reset the chip, and put it into a known state */ - smc_reset( ioaddr ); + smc = dev->priv; + + /* set the private data to zero by default */ + memset(smc, 0, sizeof(struct smc_local)); /* - . If dev->irq is 0, then the device has to be banged on to see - . what the IRQ is. - . - . This banging doesn't always detect the IRQ, for unknown reasons. - . a workaround is to reset the chip and try again. - . - . Interestingly, the DOS packet driver *SETS* the IRQ on the card to - . be what is requested on the command line. I don't do that, mostly - . because the card that I have uses a non-standard method of accessing - . the IRQs, and because this _should_ work in most configurations. - . - . Specifying an IRQ is done with the assumption that the user knows - . what (s)he is doing. No checking is done!!!! - . - */ - if ( dev->irq < 2 ) { - int trials; + * Get the interface characteristics. + * is it using AUI or 10BaseT ? + */ + switch (dev->if_port) { + case IF_PORT_10BASET: + smc->port = PORT_TP; + break; - trials = 3; - while ( trials-- ) { - dev->irq = smc_findirq( ioaddr ); - if ( dev->irq ) - break; - /* kick the card and try again */ - smc_reset( ioaddr ); + case IF_PORT_AUI: + smc->port = PORT_AUI; + break; + + default: + SMC_SELECT_BANK(1); + configuration_register = smc_inw(ioaddr, CONFIG); + if (configuration_register & CFG_AUI_SELECT) { + dev->if_port = IF_PORT_AUI; + smc->port = PORT_AUI; + } else { + dev->if_port = IF_PORT_10BASET; + smc->port = PORT_TP; } - } - if (dev->irq == 0 ) { - printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n"); - retval = -ENODEV; - goto err_out; + break; } - /* now, print out the card info, in a short format.. */ + /* all interfaces are half-duplex by default */ + smc->duplex = DUPLEX_HALF; - printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name, - version_string, revision_register & 0xF, ioaddr, dev->irq, - if_string, memory ); + /* now, print out the card info, in a short format.. */ + printk("%s: %s (rev %d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name, + version_string, revision_register & 15, ioaddr, dev->irq, + interfaces[smc->port], memory); /* . Print the Ethernet address */ printk("ADDR: "); for (i = 0; i < 5; i++) - printk("%2.2x:", dev->dev_addr[i] ); - printk("%2.2x \n", dev->dev_addr[5] ); - - - /* Initialize the private structure. */ - if (dev->priv == NULL) { - dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL); - if (dev->priv == NULL) { - retval = -ENOMEM; - goto err_out; - } - } - /* set the private data to zero by default */ - memset(dev->priv, 0, sizeof(struct smc_local)); + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x\n", dev->dev_addr[5]); /* Fill in the fields of the device structure with ethernet values. */ ether_setup(dev); /* Grab the IRQ */ - retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); - if (retval) { + retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); + if (retval) { printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, retval); kfree(dev->priv); dev->priv = NULL; - goto err_out; - } + goto err_out; + } - dev->open = smc_open; - dev->stop = smc_close; - dev->hard_start_xmit = smc_wait_to_send_packet; - dev->tx_timeout = smc_timeout; - dev->watchdog_timeo = HZ/20; - dev->get_stats = smc_query_statistics; - dev->set_multicast_list = smc_set_multicast_list; + dev->open = smc_open; + dev->stop = smc_close; + dev->hard_start_xmit = smc_wait_to_send_packet; + dev->tx_timeout = smc_timeout; + dev->watchdog_timeo = HZ/20; + dev->get_stats = smc_query_statistics; + dev->set_multicast_list = smc_set_multicast_list; + dev->do_ioctl = smc_ioctl; return 0; @@ -1022,42 +1384,43 @@ return retval; } -#if SMC_DEBUG > 2 -static void print_packet( byte * buf, int length ) +/* + * This is responsible for setting the chip appropriately + * for the interface type. This should only be called while + * the interface is up and running. + */ +static void smc_set_port(struct net_device *dev) { -#if 0 - int i; - int remainder; - int lines; - - printk("Packet of length %d \n", length ); - lines = length / 16; - remainder = length % 16; - - 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; + SMC_SELECT_BANK(1); + val = smc_inw(ioaddr, CONFIG); + switch (smc->port) { + case PORT_TP: + val &= ~CFG_AUI_SELECT; + break; - a = *(buf ++ ); - b = *(buf ++ ); - printk("%02x%02x ", a, b ); - } - printk("\n"); + case PORT_AUI: + val |= CFG_AUI_SELECT; + break; } - for ( i = 0; i < remainder/2 ; i++ ) { - byte a, b; + smc_outw(val, ioaddr, CONFIG); - a = *(buf ++ ); - b = *(buf ++ ); - printk("%02x%02x ", a, b ); + SMC_SELECT_BANK(0); + val = smc_inw(ioaddr, TCR); + switch (smc->duplex) { + case DUPLEX_HALF: + val &= ~TCR_FDSE; + break; + + case DUPLEX_FULL: + val |= TCR_FDSE; + break; } - printk("\n"); -#endif + smc_outw(val, ioaddr, TCR); } -#endif - /* * Open and Initialize the board @@ -1067,48 +1430,141 @@ */ static int smc_open(struct net_device *dev) { - int ioaddr = dev->base_addr; + struct smc_local *smc = dev->priv; + u_int ioaddr = dev->base_addr; + int i; - int i; /* used to set hw ethernet address */ + /* + * Check that the address is valid. If its not, refuse + * to bring the device up. The user must specify an + * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx + */ + if (!is_valid_ether_addr(dev->dev_addr)) + return -EINVAL; /* clear out all the junk that was put here before... */ - memset(dev->priv, 0, sizeof(struct smc_local)); + smc->saved_skb = NULL; + smc->packets_waiting = 0; /* reset the hardware */ - - smc_reset( ioaddr ); - smc_enable( ioaddr ); + smc_reset(dev); + smc_enable(dev); /* Select which interface to use */ - - SMC_SELECT_BANK( 1 ); - if ( dev->if_port == 1 ) { - outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT, - ioaddr + CONFIG ); - } - else if ( dev->if_port == 2 ) { - outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT, - ioaddr + CONFIG ); - } + smc_set_port(dev); /* - According to Becker, I have to set the hardware address + According to Becker, I have to set the hardware address at this point, because the (l)user can set it with an ioctl. Easily done... */ - SMC_SELECT_BANK( 1 ); - for ( i = 0; i < 6; i += 2 ) { + SMC_SELECT_BANK(1); + for (i = 0; i < 6; i += 2) { word address; - address = dev->dev_addr[ i + 1 ] << 8 ; - address |= dev->dev_addr[ i ]; - outw( address, ioaddr + ADDR0 + i ); + address = dev->dev_addr[i + 1] << 8 ; + address |= dev->dev_addr[i]; + smc_outw(address, ioaddr, ADDR0 + i); } netif_start_queue(dev); return 0; } +/* + * This is our template. Fill the rest in at run-time + */ +static const struct ethtool_cmd ecmd_template = { + supported: SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_TP | + SUPPORTED_AUI, + speed: SPEED_10, + autoneg: AUTONEG_DISABLE, + maxtxpkt: 1, + maxrxpkt: 1, + transceiver: XCVR_INTERNAL, +}; + +static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct smc_local *smc = dev->priv; + u32 etcmd; + int ret = -EINVAL; + + if (cmd != SIOCETHTOOL) + return -EOPNOTSUPP; + + if (get_user(etcmd, (u32 *)rq->ifr_data)) + return -EFAULT; + + switch (etcmd) { + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = ecmd_template; + + ecmd.cmd = etcmd; + ecmd.port = smc->port; + ecmd.duplex = smc->duplex; + + ret = copy_to_user(rq->ifr_data, &ecmd, sizeof(ecmd)) + ? -EFAULT : 0; + break; + } + + case ETHTOOL_SSET: { + struct ethtool_cmd ecmd; + + ret = -EPERM; + if (!capable(CAP_NET_ADMIN)) + break; + + ret = -EFAULT; + if (copy_from_user(&ecmd, rq->ifr_data, sizeof(ecmd))) + break; + + /* + * Sanity-check the arguments. + */ + ret = -EINVAL; + if (ecmd.autoneg != AUTONEG_DISABLE) + break; + if (ecmd.speed != SPEED_10) + break; + if (ecmd.duplex != DUPLEX_HALF && ecmd.duplex != DUPLEX_FULL) + break; + if (ecmd.port != PORT_TP && ecmd.port != PORT_AUI) + break; + + smc->port = ecmd.port; + smc->duplex = ecmd.duplex; + + if (netif_running(dev)) + smc_set_port(dev); + + ret = 0; + break; + } + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo edrv; + + memset(&edrv, 0, sizeof(edrv)); + + edrv.cmd = etcmd; + strcpy(edrv.driver, DRV_NAME); + strcpy(edrv.version, DRV_VERSION); + sprintf(edrv.bus_info, "ISA:%8.8lx:%d", + dev->base_addr, dev->irq); + + ret = copy_to_user(rq->ifr_data, &edrv, sizeof(edrv)) + ? -EFAULT : 0; + break; + } + } + + return ret; +} + /*-------------------------------------------------------- . Called by the kernel to send a packet out into the void . of the net. This routine is largely based on @@ -1120,12 +1576,10 @@ { /* If we get here, some higher level has decided we are broken. There should really be a "kick me" function call instead. */ - printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n", - tx_done(dev) ? "IRQ conflict" : - "network cable problem"); + printk(KERN_WARNING "%s: transmit timed out\n", dev->name); /* "kick" the adaptor */ - smc_reset( dev->base_addr ); - smc_enable( dev->base_addr ); + smc_reset(dev); + smc_enable(dev); dev->trans_start = jiffies; /* clear anything saved */ ((struct smc_local *)dev->priv)->saved_skb = NULL; @@ -1145,10 +1599,10 @@ . ---------------------------------------------------------------------*/ -static void smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) +static void smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) { struct net_device *dev = dev_id; - int ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; struct smc_local *lp = (struct smc_local *)dev->priv; byte status; @@ -1161,45 +1615,45 @@ - PRINTK3((CARDNAME": SMC interrupt started \n")); + PRINTK3(("%s: SMC interrupt started\n", dev->name)); - saved_bank = inw( ioaddr + BANK_SELECT ); + saved_bank = smc_inw(ioaddr, BANK_SELECT); SMC_SELECT_BANK(2); - saved_pointer = inw( ioaddr + POINTER ); + saved_pointer = smc_inw(ioaddr, POINTER); - mask = inb( ioaddr + INT_MASK ); + mask = smc_inb(ioaddr, INT_MASK); /* clear all interrupts */ - outb( 0, ioaddr + INT_MASK ); + SMC_SET_INT(0); /* set a timeout value, so I don't stay here forever */ timeout = 4; - PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x \n", mask )); + PRINTK2((KERN_WARNING "%s: MASK IS %x\n", dev->name, mask)); do { /* read the status flag, and mask it */ - status = inb( ioaddr + INTERRUPT ) & mask; - if (!status ) + status = smc_inb(ioaddr, INTERRUPT) & mask; + if (!status) break; - PRINTK3((KERN_WARNING CARDNAME - ": Handling interrupt status %x \n", status )); + PRINTK3((KERN_WARNING "%s: handling interrupt status %x\n", + dev->name, status)); if (status & IM_RCV_INT) { /* Got a packet(s). */ - PRINTK2((KERN_WARNING CARDNAME - ": Receive Interrupt\n")); + PRINTK2((KERN_WARNING "%s: receive interrupt\n", + dev->name)); smc_rcv(dev); - } else if (status & IM_TX_INT ) { - PRINTK2((KERN_WARNING CARDNAME - ": TX ERROR handled\n")); + } else if (status & IM_TX_INT) { + PRINTK2((KERN_WARNING "%s: TX ERROR handled\n", + dev->name)); smc_tx(dev); - outb(IM_TX_INT, ioaddr + INTERRUPT ); - } else if (status & IM_TX_EMPTY_INT ) { + smc_outb(IM_TX_INT, ioaddr, INTERRUPT); + } else if (status & IM_TX_EMPTY_INT) { /* update stats */ - SMC_SELECT_BANK( 0 ); - card_stats = inw( ioaddr + COUNTER ); + SMC_SELECT_BANK(0); + card_stats = smc_inw(ioaddr, COUNTER); /* single collisions */ lp->stats.collisions += card_stats & 0xF; card_stats >>= 4; @@ -1208,60 +1662,63 @@ /* these are for when linux supports these statistics */ - SMC_SELECT_BANK( 2 ); - PRINTK2((KERN_WARNING CARDNAME - ": TX_BUFFER_EMPTY handled\n")); - outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); + SMC_SELECT_BANK(2); + PRINTK2((KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n", + dev->name)); + smc_outb(IM_TX_EMPTY_INT, ioaddr, INTERRUPT); mask &= ~IM_TX_EMPTY_INT; lp->stats.tx_packets += lp->packets_waiting; lp->packets_waiting = 0; - } else if (status & IM_ALLOC_INT ) { - PRINTK2((KERN_DEBUG CARDNAME - ": Allocation interrupt \n")); + } else if (status & IM_ALLOC_INT) { + PRINTK2((KERN_DEBUG "%s: Allocation interrupt\n", + dev->name)); /* clear this interrupt so it doesn't happen again */ mask &= ~IM_ALLOC_INT; - smc_hardware_send_packet( dev ); + smc_hardware_send_packet(dev); /* enable xmit interrupts based on this */ - mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); + mask |= (IM_TX_EMPTY_INT | IM_TX_INT); /* and let the card send more packets to me */ netif_wake_queue(dev); - PRINTK2((CARDNAME": Handoff done successfully.\n")); - } else if (status & IM_RX_OVRN_INT ) { + PRINTK2(("%s: Handoff done successfully.\n", + dev->name)); + } else if (status & IM_RX_OVRN_INT) { lp->stats.rx_errors++; lp->stats.rx_fifo_errors++; - outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); - } else if (status & IM_EPH_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT \n")); - } else if (status & IM_ERCV_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT \n")); - outb( IM_ERCV_INT, ioaddr + INTERRUPT ); + smc_outb(IM_RX_OVRN_INT, ioaddr, INTERRUPT); + } else if (status & IM_EPH_INT) { + PRINTK(("%s: UNSUPPORTED: EPH INTERRUPT\n", + dev->name)); + } else if (status & IM_ERCV_INT) { + PRINTK(("%s: UNSUPPORTED: ERCV INTERRUPT\n", + dev->name)); + smc_outb(IM_ERCV_INT, ioaddr, INTERRUPT); } - } while ( timeout -- ); + } while (timeout --); /* restore state register */ - SMC_SELECT_BANK( 2 ); - outb( mask, ioaddr + INT_MASK ); + SMC_SELECT_BANK(2); + SMC_SET_INT(mask); - PRINTK3(( KERN_WARNING CARDNAME ": MASK is now %x \n", mask )); - outw( saved_pointer, ioaddr + POINTER ); + PRINTK3((KERN_WARNING "%s: MASK is now %x\n", dev->name, mask)); + smc_outw(saved_pointer, ioaddr, POINTER); - SMC_SELECT_BANK( saved_bank ); + SMC_SELECT_BANK(saved_bank); - PRINTK3((CARDNAME ": Interrupt done\n")); + PRINTK3(("%s: Interrupt done\n", dev->name)); return; } /*------------------------------------------------------------- . - . smc_rcv - receive a packet from the card + . smc_rcv - receive a packet from the card . - . There is ( at least ) a packet waiting to be read from + . There is (at least) a packet waiting to be read from . chip-memory. . . o Read the status @@ -1272,55 +1729,57 @@ static void smc_rcv(struct net_device *dev) { struct smc_local *lp = (struct smc_local *)dev->priv; - int ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; int packet_number; word status; word packet_length; /* assume bank 2 */ - packet_number = inw( ioaddr + FIFO_PORTS ); + packet_number = smc_inw(ioaddr, FIFO_PORTS); - if ( packet_number & FP_RXEMPTY ) { + if (packet_number & FP_RXEMPTY) { /* we got called , but nothing was on the FIFO */ - PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO. \n")); + PRINTK(("%s: WARNING: smc_rcv with nothing on FIFO.\n", + dev->name)); /* don't need to restore anything */ return; } /* start reading from the start of the packet */ - outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); + smc_outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr, POINTER); /* First two words are status and packet_length */ - status = inw( ioaddr + DATA_1 ); - packet_length = inw( ioaddr + DATA_1 ); + status = smc_inw(ioaddr, DATA_1); + packet_length = smc_inw(ioaddr, DATA_1); packet_length &= 0x07ff; /* mask off top bits */ - PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length )); + PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length)); /* . the packet length contains 3 extra words : . status, length, and an extra word with an odd byte . */ packet_length -= 6; - if ( !(status & RS_ERRORS ) ){ + if (!(status & RS_ERRORS)){ /* do stuff to make a new packet */ struct sk_buff * skb; byte * data; /* read one extra byte */ - if ( status & RS_ODDFRAME ) + if (status & RS_ODDFRAME) packet_length++; /* set multicast stats */ - if ( status & RS_MULTICAST ) + if (status & RS_MULTICAST) lp->stats.multicast++; - skb = dev_alloc_skb( packet_length + 5); + skb = dev_alloc_skb(packet_length + 5); - if ( skb == NULL ) { - printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n"); + if (skb == NULL) { + printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", + dev->name); lp->stats.rx_dropped++; goto done; } @@ -1330,36 +1789,15 @@ ! in the worse case */ - skb_reserve( skb, 2 ); /* 16 bit alignment */ + skb_reserve(skb, 2); /* 16 bit alignment */ skb->dev = dev; - data = skb_put( skb, packet_length); + data = skb_put(skb, packet_length); -#ifdef USE_32_BIT - /* QUESTION: Like in the TX routine, do I want - to send the DWORDs or the bytes first, or some - mixture. A mixture might improve already slow PIO - performance */ - PRINTK3((" Reading %d dwords (and %d bytes) \n", - packet_length >> 2, packet_length & 3 )); - insl(ioaddr + DATA_1 , data, packet_length >> 2 ); - /* read the left over bytes */ - insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC), - packet_length & 0x3 ); -#else - PRINTK3((" Reading %d words and %d byte(s) \n", - (packet_length >> 1 ), packet_length & 1 )); - insw(ioaddr + DATA_1 , data, packet_length >> 1); - if ( packet_length & 1 ) { - data += packet_length & ~1; - *(data++) = inb( ioaddr + DATA_1 ); - } -#endif -#if SMC_DEBUG > 2 - print_packet( data, packet_length ); -#endif + smc_ins(ioaddr, DATA_1, data, packet_length); + print_packet(data, packet_length); - skb->protocol = eth_type_trans(skb, dev ); + skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; @@ -1368,15 +1806,17 @@ /* error ... */ lp->stats.rx_errors++; - if ( status & RS_ALGNERR ) lp->stats.rx_frame_errors++; - if ( status & (RS_TOOSHORT | RS_TOOLONG ) ) + if (status & RS_ALGNERR) + lp->stats.rx_frame_errors++; + if (status & (RS_TOOSHORT | RS_TOOLONG)) lp->stats.rx_length_errors++; - if ( status & RS_BADCRC) lp->stats.rx_crc_errors++; + if (status & RS_BADCRC) + lp->stats.rx_crc_errors++; } done: /* error or good, tell the card to get rid of this packet */ - outw( MC_RELEASE, ioaddr + MMU_CMD ); + smc_outw(MC_RELEASE, ioaddr, MMU_CMD); } @@ -1389,62 +1829,64 @@ . Algorithm: . Save pointer and packet no . Get the packet no from the top of the queue - . check if it's valid ( if not, is this an error??? ) + . check if it's valid (if not, is this an error???) . read the status word . record the error - . ( resend? Not really, since we don't want old packets around ) + . (resend? Not really, since we don't want old packets around) . Restore saved values ************************************************************************/ -static void smc_tx( struct net_device * dev ) +static void smc_tx(struct net_device * dev) { - int ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; struct smc_local *lp = (struct smc_local *)dev->priv; byte saved_packet; byte packet_no; word tx_status; - /* assume bank 2 */ + /* assume bank 2 */ - saved_packet = inb( ioaddr + PNR_ARR ); - packet_no = inw( ioaddr + FIFO_PORTS ); + saved_packet = smc_inb(ioaddr, PNR_ARR); + packet_no = smc_inw(ioaddr, FIFO_PORTS); packet_no &= 0x7F; /* select this as the packet to read from */ - outb( packet_no, ioaddr + PNR_ARR ); + smc_outb(packet_no, ioaddr, PNR_ARR); /* read the first word from this packet */ - outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER ); + smc_outw(PTR_AUTOINC | PTR_READ, ioaddr, POINTER); - tx_status = inw( ioaddr + DATA_1 ); - PRINTK3((CARDNAME": TX DONE STATUS: %4x \n", tx_status )); + tx_status = smc_inw(ioaddr, DATA_1); + PRINTK3(("%s: TX DONE STATUS: %4x\n", dev->name, tx_status)); lp->stats.tx_errors++; - if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++; - if ( tx_status & TS_LATCOL ) { - printk(KERN_DEBUG CARDNAME - ": Late collision occurred on last xmit.\n"); + if (tx_status & TS_LOSTCAR) + lp->stats.tx_carrier_errors++; + if (tx_status & TS_LATCOL) { + printk(KERN_DEBUG "%s: Late collision occurred on " + "last xmit.\n", dev->name); lp->stats.tx_window_errors++; } #if 0 - if ( tx_status & TS_16COL ) { ... } + if (tx_status & TS_16COL) { ... } #endif - if ( tx_status & TS_SUCCESS ) { - printk(CARDNAME": Successful packet caused interrupt \n"); + if (tx_status & TS_SUCCESS) { + printk("%s: Successful packet caused interrupt\n", + dev->name); } /* re-enable transmit */ - SMC_SELECT_BANK( 0 ); - outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR ); + SMC_SELECT_BANK(0); + smc_outw(smc_inw(ioaddr, TCR) | TCR_ENABLE, ioaddr, TCR); /* kill the packet */ - SMC_SELECT_BANK( 2 ); - outw( MC_FREEPKT, ioaddr + MMU_CMD ); + SMC_SELECT_BANK(2); + smc_outw(MC_FREEPKT, ioaddr, MMU_CMD); /* one less packet waiting for me */ lp->packets_waiting--; - outb( saved_packet, ioaddr + PNR_ARR ); + smc_outb(saved_packet, ioaddr, PNR_ARR); return; } @@ -1460,7 +1902,7 @@ { netif_stop_queue(dev); /* clear everything */ - smc_shutdown( dev->base_addr ); + smc_shutdown(dev); /* Update the statistics here. */ return 0; @@ -1481,16 +1923,16 @@ . . This routine will, depending on the values passed to it, . either make it accept multicast packets, go into - . promiscuous mode ( for TCPDUMP and cousins ) or accept + . promiscuous mode (for TCPDUMP and cousins) or accept . a select set of multicast packets */ static void smc_set_multicast_list(struct net_device *dev) { - short ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; SMC_SELECT_BANK(0); - if ( dev->flags & IFF_PROMISC ) - outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR ); + if (dev->flags & IFF_PROMISC) + smc_outw(smc_inw(ioaddr, RCR) | RCR_PROMISC, ioaddr, RCR); /* BUG? I never disable promiscuous mode if multicasting was turned on. Now, I turn off promiscuous mode, but I don't do anything to multicasting @@ -1502,34 +1944,34 @@ checked before the table is */ else if (dev->flags & IFF_ALLMULTI) - outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR ); + smc_outw(smc_inw(ioaddr, RCR) | RCR_ALMUL, ioaddr, RCR); /* We just get all multicast packets even if we only want them . from one source. This will be changed at some future . point. */ - else if (dev->mc_count ) { + else if (dev->mc_count) { /* support hardware multicasting */ /* be sure I get rid of flags I might have set */ - outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), - ioaddr + RCR ); + smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL), + ioaddr, RCR); /* NOTE: this has to set the bank, so make sure it is the last thing called. The bank is set to zero at the top */ - smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list ); + smc_setmulticast(dev, dev->mc_count, dev->mc_list); } - else { - outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), - ioaddr + RCR ); + else { + smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL), + ioaddr, RCR); /* since I'm disabling all multicast entirely, I need to clear the multicast list */ - SMC_SELECT_BANK( 3 ); - outw( 0, ioaddr + MULTICAST1 ); - outw( 0, ioaddr + MULTICAST2 ); - outw( 0, ioaddr + MULTICAST3 ); - outw( 0, ioaddr + MULTICAST4 ); + SMC_SELECT_BANK(3); + smc_outw(0, ioaddr, MULTICAST1); + smc_outw(0, ioaddr, MULTICAST2); + smc_outw(0, ioaddr, MULTICAST3); + smc_outw(0, ioaddr, MULTICAST4); } } @@ -1550,21 +1992,26 @@ int init_module(void) { - int result; - if (io == 0) - printk(KERN_WARNING - CARDNAME": You shouldn't use auto-probing with insmod!\n" ); + printk(KERN_WARNING CARDNAME + ": You shouldn't use auto-probing with insmod!\n"); + + /* + * Note: dev->if_port has changed to be 2.4 compliant. + * We keep the ifport insmod parameter the same though. + */ + switch (ifport) { + case 1: devSMC9194.if_port = IF_PORT_10BASET; break; + case 2: devSMC9194.if_port = IF_PORT_AUI; break; + default: devSMC9194.if_port = 0; break; + } /* copy the parameters from insmod into the device structure */ devSMC9194.base_addr = io; devSMC9194.irq = irq; - devSMC9194.if_port = ifport; - devSMC9194.init = smc_init; - if ((result = register_netdev(&devSMC9194)) != 0) - return result; + devSMC9194.init = smc_init; - return 0; + return register_netdev(&devSMC9194); } void cleanup_module(void) --- linux-2.4.27/drivers/net/smc9194.h~2.4.27-vrs1 +++ linux-2.4.27/drivers/net/smc9194.h @@ -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 */ +/* + * 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 SMC_DISABLE_INT(x) {\ - unsigned char mask;\ - SMC_SELECT_BANK(2);\ - mask = inb( ioaddr + INT_MASK );\ - mask &= ~(x);\ - outb( mask, ioaddr + INT_MASK ); \ -} +#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_ */ --- linux-2.4.27/drivers/parport/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/parport/Config.in @@ -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 --- linux-2.4.27/drivers/parport/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/parport/Makefile @@ -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 --- linux-2.4.27/drivers/parport/init.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/parport/init.c @@ -164,6 +164,9 @@ #ifdef CONFIG_PARPORT_SUNBPP parport_sunbpp_init(); #endif +#ifdef CONFIG_PARPORT_IDP + parport_idp_init(); +#endif return 0; } --- /dev/null +++ linux-2.4.27/drivers/parport/parport_idp.c @@ -0,0 +1,247 @@ +/* Low-level polled-mode parallel port routines for the Accelent IDP + * + * Author: Rich Dulabahn + * + * Inspiration taken from parport_amiga.c and parport_atari.c. + * + * To use, under menuconfig: + * 1) Turn on <*> Accelent IDP under Parallel port setup + * 2) Turn on <*> Parallel printer support under Character devices + * + * This will give you parport0 configured as /dev/lp0 + * + * To make the correct /dev/lp* entries, enter /dev and type this: + * + * mknod lp0 c 6 0 + * mknod lp1 c 6 1 + * mknod lp2 c 6 2 + * + */ + +#include +#include +#include +#include + +/* + * Parallel data port is port H, data + * Parallel data direction is port H, direction + * Control port is port I, data, lowest 4 bits + * Status port is port G, data, upper 5 bits + */ + +#define INPUTPOWERHANDLER 0 +/* masks */ +#define CONTROL_MASK 0x0f +#define STATUS_MASK 0xf8 + +#undef DEBUG + +#ifdef DEBUG +#define DPRINTK printk +#else +#define DPRINTK(stuff...) +#endif + +static struct parport *this_port = NULL; + +static unsigned char +parport_idp_read_data(struct parport *p) +{ + unsigned char c; + + c = IDP_FPGA_PORTH_DATA; + DPRINTK("read_data:0x%x\n",c); + return c; +} + +static void +parport_idp_write_data(struct parport *p, unsigned char data) +{ + IDP_FPGA_PORTH_DATA = data; + DPRINTK("write_data:0x%x\n",data); +} + +static unsigned char +parport_idp_read_control(struct parport *p) +{ + unsigned char c; + + c = IDP_FPGA_PORTI_DATA & CONTROL_MASK; + DPRINTK("read_control:0x%x\n",c); + return c; +} + +static void +parport_idp_write_control(struct parport *p, unsigned char control) +{ + unsigned int temp; + + temp = IDP_FPGA_PORTH_DATA; + temp &= ~CONTROL_MASK; + IDP_FPGA_PORTI_DATA = (temp | (control & CONTROL_MASK)); +DPRINTK("write_control:0x%x\n",control); +} + +static unsigned char +parport_idp_frob_control(struct parport *p, unsigned char mask, + unsigned char val) +{ + unsigned char c; + +/* From the parport-lowlevel.txt file...*/ +/* This is equivalent to reading from the control register, masking out +the bits in mask, exclusive-or'ing with the bits in val, and writing +the result to the control register. */ + +/* Easy enough, right? */ + + c = parport_idp_read_control(p); + parport_idp_write_control(p, (c & ~mask) ^ val); + DPRINTK("frob_control:0x%x\n",c); + return c; +} + +static unsigned char +parport_idp_read_status(struct parport *p) +{ + unsigned char c; + + c = IDP_FPGA_PORTG_DATA & STATUS_MASK; + c ^= 0x80; /* toggle S7 bit, active low */ + DPRINTK("read_status:0x%x\n",c); + return c; +} + +static void +parport_idp_init_state(struct pardevice *d, struct parport_state *s) +{ +} + +static void +parport_idp_save_state(struct parport *p, struct parport_state *s) +{ +} + +static void +parport_idp_restore_state(struct parport *p, struct parport_state *s) +{ +} + +static void +parport_idp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static void +parport_idp_enable_irq(struct parport *p) +{ +} + +static void +parport_idp_disable_irq(struct parport *p) +{ +} + +static void +parport_idp_data_forward(struct parport *p) +{ + IDP_FPGA_PORTH_DIR = 0x00; /* 0 sets to output */ + DPRINTK("data_forward:0x%x\n",0); +} + +static void +parport_idp_data_reverse(struct parport *p) +{ + IDP_FPGA_PORTH_DIR = 0xff; /* and 1 sets to input */ + DPRINTK("data_reverse:0x%x\n",0xff); +} + +static void +parport_idp_inc_use_count(void) +{ + MOD_INC_USE_COUNT; +} + +static void +parport_idp_dec_use_count(void) +{ + MOD_DEC_USE_COUNT; +} + +static struct parport_operations parport_idp_ops = { + parport_idp_write_data, + parport_idp_read_data, + + parport_idp_write_control, + parport_idp_read_control, + parport_idp_frob_control, + + parport_idp_read_status, + + parport_idp_enable_irq, + parport_idp_disable_irq, + + parport_idp_data_forward, + parport_idp_data_reverse, + + parport_idp_init_state, + parport_idp_save_state, + parport_idp_restore_state, + + parport_idp_inc_use_count, + parport_idp_dec_use_count, + + parport_ieee1284_epp_write_data, + parport_ieee1284_epp_read_data, + parport_ieee1284_epp_write_addr, + parport_ieee1284_epp_read_addr, + + parport_ieee1284_ecp_write_data, + parport_ieee1284_ecp_read_data, + parport_ieee1284_ecp_write_addr, + + parport_ieee1284_write_compat, + parport_ieee1284_read_nibble, + parport_ieee1284_read_byte, +}; + + +int __init +parport_idp_init(void) +{ + struct parport *p; + + p = parport_register_port((unsigned long)0,PARPORT_IRQ_NONE,PARPORT_DMA_NONE,&parport_idp_ops); + + if (!p) return 0; /* return 0 on failure */ + + this_port=p; + printk("%s: Accelent IDP parallel port registered.\n", p->name); + parport_proc_register(p); + parport_announce_port(p); + + return 1; +} + +#ifdef MODULE + +MODULE_AUTHOR("Rich Dulabahn"); +MODULE_DESCRIPTION("Parport Driver for Accelent IDP"); +MODULE_SUPPORTED_DEVICE("Accelent IDP builtin Parallel Port"); +MODULE_LICENSE("GPL"); + +int +init_module(void) +{ + return parport_idp_init() ? 0 : -ENODEV; +} + +void +cleanup_module(void) +{ + parport_proc_unregister(this_port); + parport_unregister_port(this_port); +} +#endif + --- linux-2.4.27/drivers/pci/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/pci/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/pci/bridge.c @@ -0,0 +1,149 @@ + +/* + * Copyright (c) 2001 Red Hat, Inc. All rights reserved. + * + * This software may be freely redistributed under the terms + * of the GNU public license. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Arjan van de Ven + * + */ + + +/* + * Generic PCI driver for PCI bridges for powermanagement purposes + * + */ + +#include +#include +#include +#include +#include + +static struct pci_device_id bridge_pci_table[] __devinitdata = { + {/* handle all PCI bridges */ + class: ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), + class_mask: ~0, + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + }, + {0,}, +}; + +static int bridge_probe(struct pci_dev *pdev, const struct pci_device_id *id); +static int pci_bridge_save_state_bus(struct pci_bus *bus, int force); +int pci_generic_resume_compare(struct pci_dev *pdev); + +int pci_bridge_force_restore = 0; + + + + +static int __init bridge_setup(char *str) +{ + if (!strcmp(str,"force")) + pci_bridge_force_restore = 1; + else if (!strcmp(str,"noforce")) + pci_bridge_force_restore = 0; + return 0; +} + +__setup("resume=",bridge_setup); + + +static int pci_bridge_save_state_bus(struct pci_bus *bus, int force) +{ + struct list_head *list; + int error = 0; + + list_for_each(list, &bus->children) { + error = pci_bridge_save_state_bus(pci_bus_b(list),force); + if (error) return error; + } + list_for_each(list, &bus->devices) { + pci_generic_suspend_save(pci_dev_b(list),0); + } + return 0; +} + + +static int pci_bridge_restore_state_bus(struct pci_bus *bus, int force) +{ + struct list_head *list; + int error = 0; + static int printed_warning=0; + + list_for_each(list, &bus->children) { + error = pci_bridge_restore_state_bus(pci_bus_b(list),force); + if (error) return error; + } + list_for_each(list, &bus->devices) { + if (force) + pci_generic_resume_restore(pci_dev_b(list)); + else { + error = pci_generic_resume_compare(pci_dev_b(list)); + if (error && !printed_warning++) { + printk(KERN_WARNING "resume warning: bios doesn't restore PCI state properly\n"); + printk(KERN_WARNING "resume warning: if resume failed, try booting with resume=force\n"); + } + if (error) + return error; + } + } + return 0; +} + +static int bridge_suspend(struct pci_dev *dev, u32 force) +{ + pci_generic_suspend_save(dev,force); + if (dev->subordinate) + pci_bridge_save_state_bus(dev->subordinate,force); + return 0; +} + +static int bridge_resume(struct pci_dev *dev) +{ + + pci_generic_resume_restore(dev); + if (dev->subordinate) + pci_bridge_restore_state_bus(dev->subordinate,pci_bridge_force_restore); + return 0; +} + + +MODULE_DEVICE_TABLE(pci, bridge_pci_table); +static struct pci_driver bridge_ops = { + name: "PCI Bridge", + id_table: bridge_pci_table, + probe: bridge_probe, + suspend: bridge_suspend, + resume: bridge_resume +}; + +static int __devinit bridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + return 0; +} + +static int __init bridge_init(void) +{ + pci_register_driver(&bridge_ops); + return 0; +} + +static void __exit bridge_exit(void) +{ + pci_unregister_driver(&bridge_ops); +} + + +module_init(bridge_init) +module_exit(bridge_exit) + --- linux-2.4.27/drivers/pci/pci.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pci/pci.c @@ -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 --- linux-2.4.27/drivers/pci/setup-bus.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pci/setup-bus.c @@ -12,6 +12,8 @@ /* * Nov 2000, Ivan Kokshaysky * PCI-PCI bridges cleanup, sorted resource allocation. + * May 2001, Russell King + * Allocate prefetchable memory regions where available. * Feb 2002, Ivan Kokshaysky * Converted to allocation in 3 passes, which gives * tighter packing. Prefetchable range support. @@ -160,8 +162,10 @@ pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); /* Check if we have VGA behind the bridge. - Enable ISA in either case (FIXME!). */ - l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? 0x0c : 0x04; + Enable ISA in either case. */ + l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? + PCI_BRIDGE_CTL_VGA | PCI_BRIDGE_CTL_NO_ISA : + PCI_BRIDGE_CTL_NO_ISA; pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, l); } --- linux-2.4.27/drivers/pcmcia/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/Config.in @@ -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 --- linux-2.4.27/drivers/pcmcia/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/Makefile @@ -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 --- linux-2.4.27/drivers/pcmcia/cistpl.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/cistpl.c @@ -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; --- /dev/null +++ linux-2.4.27/drivers/pcmcia/clps6700.c @@ -0,0 +1,498 @@ +/* + * linux/drivers/pcmcia/clps6700.c + * + * Copyright (C) 2000 Deep Blue Solutions Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "clps6700.h" + +#define DEBUG + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("CL-PS6700 PCMCIA socket driver"); + +#define NR_CLPS6700 2 + +struct clps6700_skt { + u_int nr; + u_int physbase; + u_int regbase; + u_int pmr; + u_int cpcr; + u_int cpcr_3v3; + u_int cpcr_5v0; + u_int cur_pmr; + u_int cur_cicr; + u_int cur_pcimr; + u_int cur_cpcr; + void (*handler)(void *, u_int); + void *handler_info; + + u_int ev_pending; + spinlock_t ev_lock; +}; + +static struct clps6700_skt *skts[NR_CLPS6700]; + +static int clps6700_sock_init(u_int sock) +{ + struct clps6700_skt *skt = skts[sock]; + + skt->cur_cicr = 0; + skt->cur_pmr = skt->pmr; + skt->cur_pcimr = 0; + skt->cur_cpcr = skt->cpcr; + +#ifdef DEBUG + printk("skt%d: sock_init()\n", sock); +#endif + + __raw_writel(skt->cur_pmr, skt->regbase + PMR); + __raw_writel(skt->cur_cpcr, skt->regbase + CPCR); + __raw_writel(0x01f8, skt->regbase + SICR); + __raw_writel(0x0000, skt->regbase + DMACR); + __raw_writel(skt->cur_cicr, skt->regbase + CICR); + __raw_writel(0x1f00, skt->regbase + CITR0A); + __raw_writel(0x0000, skt->regbase + CITR0B); + __raw_writel(0x1f00, skt->regbase + CITR1A); + __raw_writel(0x0000, skt->regbase + CITR1B); + __raw_writel(skt->cur_pcimr, skt->regbase + PCIMR); + + /* + * Enable Auto Idle Mode in PM register + */ + __raw_writel(-1, skt->regbase + PCIRR1); + __raw_writel(-1, skt->regbase + PCIRR2); + __raw_writel(-1, skt->regbase + PCIRR3); + + return 0; +} + +static int clps6700_suspend(u_int sock) +{ + return 0; +} + +static int clps6700_register_callback(u_int sock, void (*handler)(void *, u_int), void *info) +{ + struct clps6700_skt *skt = skts[sock]; + +#ifdef DEBUG + printk("skt%d: register_callback: %p (%p)\n", sock, handler, info); +#endif + + skt->handler_info = info; + skt->handler = handler; + + return 0; +} + +static int clps6700_inquire_socket(u_int sock, socket_cap_t *cap) +{ + cap->features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP | SS_CAP_MEM_ALIGN; + cap->irq_mask = 0; /* available IRQs for this socket */ + cap->map_size = PAGE_SIZE; /* minimum mapping size */ + cap->pci_irq = 0; /* PCI interrupt number */ + cap->cb_dev = NULL; + cap->bus = NULL; + return 0; +} + +static int __clps6700_get_status(struct clps6700_skt *skt) +{ + unsigned int v, val; + + v = __raw_readl(skt->regbase + PCIILR); + val = 0; + if ((v & (PCM_CD1 | PCM_CD2)) == 0) + val |= SS_DETECT; + if ((v & (PCM_BVD2 | PCM_BVD1)) == PCM_BVD1) + val |= SS_BATWARN; + if ((v & PCM_BVD2) == 0) + val |= SS_BATDEAD; + + if (v & PCM_RDYL) + val |= SS_READY; + if (v & PCM_VS1) + val |= SS_3VCARD; + if (v & PCM_VS2) + val |= SS_XVCARD; + +#ifdef DEBUG + printk("skt%d: PCIILR: %08x -> (%s %s %s %s %s %s)\n", + skt->nr, v, + val & SS_READY ? "rdy" : "---", + val & SS_DETECT ? "det" : "---", + val & SS_BATWARN ? "bw" : "--", + val & SS_BATDEAD ? "bd" : "--", + val & SS_3VCARD ? "3v" : "--", + val & SS_XVCARD ? "xv" : "--"); +#endif + return val; +} + +static int clps6700_get_status(u_int sock, u_int *valp) +{ + struct clps6700_skt *skt = skts[sock]; + + *valp = __clps6700_get_status(skt); + + return 0; /* not used! */ +} + +static int clps6700_get_socket(u_int sock, socket_state_t *state) +{ + return -EINVAL; +} + +static int clps6700_set_socket(u_int sock, socket_state_t *state) +{ + struct clps6700_skt *skt = skts[sock]; + unsigned long flags; + u_int cpcr = skt->cur_cpcr, pmr = skt->cur_pmr, cicr = skt->cur_cicr; + u_int pcimr = 0; + + cicr &= ~(CICR_ENABLE | CICR_RESET | CICR_IOMODE); + + if (state->flags & SS_PWR_AUTO) + pmr |= PMR_DCAR | PMR_PDCR; + + /* + * Note! We must NOT assert the Card Enable bit until reset has + * been de-asserted. Some cards indicate not ready, which then + * hangs our next access. (Bug in CLPS6700?) + */ + if (state->flags & SS_RESET) + cicr |= CICR_RESET | CICR_RESETOE; + else if (state->flags & SS_OUTPUT_ENA) + cicr |= CICR_ENABLE; + + if (state->flags & SS_IOCARD) { + cicr |= CICR_IOMODE; + +/* if (state->csc_mask & SS_STSCHG)*/ + } else { + if (state->csc_mask & SS_BATDEAD) + pcimr |= PCM_BVD2; + if (state->csc_mask & SS_BATWARN) + pcimr |= PCM_BVD1; + if (state->csc_mask & SS_READY) + pcimr |= PCM_RDYL; + } + + if (state->csc_mask & SS_DETECT) + pcimr |= PCM_CD1 | PCM_CD2; + + switch (state->Vcc) { + case 0: break; + case 33: cpcr |= skt->cpcr_3v3; pmr |= PMR_CPE; break; + case 50: cpcr |= skt->cpcr_5v0; pmr |= PMR_CPE; break; + default: return -EINVAL; + } + +#ifdef DEBUG + printk("skt%d: PMR: %04x, CPCR: %04x, CICR: %04x PCIMR: %04x " + "(Vcc = %d, flags = %c%c%c%c, csc = %c%c%c%c%c)\n", + sock, pmr, cpcr, cicr, pcimr, state->Vcc, + state->flags & SS_RESET ? 'r' : '-', + state->flags & SS_PWR_AUTO ? 'p' : '-', + state->flags & SS_IOCARD ? 'i' : '-', + state->flags & SS_OUTPUT_ENA ? 'o' : '-', + state->csc_mask & SS_STSCHG ? 's' : '-', + state->csc_mask & SS_BATDEAD ? 'd' : '-', + state->csc_mask & SS_BATWARN ? 'w' : '-', + state->csc_mask & SS_READY ? 'r' : '-', + state->csc_mask & SS_DETECT ? 'c' : '-'); +#endif + + save_flags_cli(flags); + + if (skt->cur_cpcr != cpcr) { + skt->cur_cpcr = cpcr; + __raw_writel(skt->cur_cpcr, skt->regbase + CPCR); + } + + if (skt->cur_pmr != pmr) { + skt->cur_pmr = pmr; + __raw_writel(skt->cur_pmr, skt->regbase + PMR); + } + if (skt->cur_pcimr != pcimr) { + skt->cur_pcimr = pcimr; + __raw_writel(skt->cur_pcimr, skt->regbase + PCIMR); + } + if (skt->cur_cicr != cicr) { + skt->cur_cicr = cicr; + __raw_writel(skt->cur_cicr, skt->regbase + CICR); + } + + restore_flags(flags); + + return 0; +} + +static int clps6700_get_io_map(u_int sock, struct pccard_io_map *io) +{ + return -EINVAL; +} + +static int clps6700_set_io_map(u_int sock, struct pccard_io_map *io) +{ + printk("skt%d: iomap: %d: speed %d, flags %X start %X stop %X\n", + sock, io->map, io->speed, io->flags, io->start, io->stop); + return 0; +} + +static int clps6700_get_mem_map(u_int sock, struct pccard_mem_map *mem) +{ + return -EINVAL; +} + +/* + * Set the memory map attributes for this socket. (ie, mem->speed) + * Note that since we have SS_CAP_STATIC_MAP set, we don't need to do + * any mapping here at all; we just need to return the address (suitable + * for ioremap) to map the requested space in mem->sys_start. + * + * flags & MAP_ATTRIB indicates whether we want attribute space. + */ +static int clps6700_set_mem_map(u_int sock, struct pccard_mem_map *mem) +{ + struct clps6700_skt *skt = skts[sock]; + u_int off; + + printk("skt%d: memmap: %d: speed %d, flags %X start %lX stop %lX card %X\n", + sock, mem->map, mem->speed, mem->flags, mem->sys_start, + mem->sys_stop, mem->card_start); + + if (mem->flags & MAP_ATTRIB) + off = CLPS6700_ATTRIB_BASE; + else + off = CLPS6700_MEM_BASE; + + mem->sys_start = skt->physbase + off; + mem->sys_start += mem->card_start; + + return 0; +} + +static void clps6700_proc_setup(u_int sock, struct proc_dir_entry *base) +{ +} + +static struct pccard_operations clps6700_operations = { + init: clps6700_sock_init, + suspend: clps6700_suspend, + register_callback: clps6700_register_callback, + inquire_socket: clps6700_inquire_socket, + get_status: clps6700_get_status, + get_socket: clps6700_get_socket, + set_socket: clps6700_set_socket, + get_io_map: clps6700_get_io_map, + set_io_map: clps6700_set_io_map, + get_mem_map: clps6700_get_mem_map, + set_mem_map: clps6700_set_mem_map, + proc_setup: clps6700_proc_setup +}; + +static void clps6700_bh(void *dummy) +{ + int i; + + for (i = 0; i < NR_CLPS6700; i++) { + struct clps6700_skt *skt = skts[i]; + unsigned long flags; + u_int events; + + if (!skt) + continue; + + /* + * Note! We must read the pending event state + * with interrupts disabled, otherwise we race + * with our own interrupt routine! + */ + spin_lock_irqsave(&skt->ev_lock, flags); + events = skt->ev_pending; + skt->ev_pending = 0; + spin_unlock_irqrestore(&skt->ev_lock, flags); + + if (skt->handler && events) + skt->handler(skt->handler_info, events); + } +} + +static struct tq_struct clps6700_task = { + routine: clps6700_bh +}; + +static void clps6700_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct clps6700_skt *skt = dev_id; + u_int val, events; + + val = __raw_readl(skt->regbase + PCISR); + if (!val) + return; + + __raw_writel(val, skt->regbase + PCICR); + + events = 0; + if (val & (PCM_CD1 | PCM_CD2)) + events |= SS_DETECT; + if (val & PCM_BVD1) + events |= SS_BATWARN; + if (val & PCM_BVD2) + events |= SS_BATDEAD; + if (val & PCM_RDYL) + events |= SS_READY; + + spin_lock(&skt->ev_lock); + skt->ev_pending |= events; + spin_unlock(&skt->ev_lock); + schedule_task(&clps6700_task); +} + +static int __init clps6700_init_skt(int nr) +{ + struct clps6700_skt *skt; + int ret; + + skt = kmalloc(sizeof(struct clps6700_skt), GFP_KERNEL); + if (!skt) + return -ENOMEM; + + memset(skt, 0, sizeof(struct clps6700_skt)); + + spin_lock_init(&skt->ev_lock); + + skt->nr = nr; + skt->physbase = nr ? CS5_PHYS_BASE : CS4_PHYS_BASE; + skt->pmr = PMR_AUTOIDLE | PMR_MCPE | PMR_CDWEAK; + skt->cpcr = CPCR_PDIR(PCTL1|PCTL0); + skt->cpcr_3v3 = CPCR_PON(PCTL0); + skt->cpcr_5v0 = CPCR_PON(PCTL0); /* we only do 3v3 */ + + skt->cur_pmr = skt->pmr; + + skt->regbase = (u_int)ioremap(skt->physbase + CLPS6700_REG_BASE, + CLPS6700_REG_SIZE); + ret = -ENOMEM; + if (!skt->regbase) + goto err_free; + + skts[nr] = skt; + + ret = request_irq(IRQ_EINT3, clps6700_interrupt, + SA_SHIRQ, "pcmcia", skt); + + if (ret) { + printk(KERN_ERR "clps6700: unable to grab irq%d (%d)\n", + IRQ_EINT3, ret); + goto err_unmap; + } + return 0; + +err_unmap: + iounmap((void *)skt->regbase); +err_free: + kfree(skt); + skts[nr] = NULL; + return ret; +} + +static void clps6700_free_resources(void) +{ + int i; + + for (i = NR_CLPS6700; i >= 0; i--) { + struct clps6700_skt *skt = skts[i]; + + skts[i] = NULL; + if (skt == NULL) + continue; + + free_irq(IRQ_EINT3, skt); + if (skt->regbase) { + __raw_writel(skt->pmr, skt->regbase + PMR); + __raw_writel(skt->cpcr, skt->regbase + CPCR); + __raw_writel(0, skt->regbase + CICR); + __raw_writel(0, skt->regbase + PCIMR); + } + iounmap((void *)skt->regbase); + kfree(skt); + } +} + +static int __init clps6700_init(void) +{ + unsigned int v; + int err, nr; + + PLD_CF = 0; + v = clps_readl(SYSCON2) | SYSCON2_PCCARD1 | SYSCON2_PCCARD2; + clps_writel(v, SYSCON2); + v = clps_readl(SYSCON1) | SYSCON1_EXCKEN; + clps_writel(v, SYSCON1); + + for (nr = 0; nr < NR_CLPS6700; nr++) { + err = clps6700_init_skt(nr); + if (err) + goto free; + } + + err = register_ss_entry(nr, &clps6700_operations); + if (err) + goto free; + + return 0; + +free: + clps6700_free_resources(); + /* + * An error occurred. Unmap and free all CLPS6700 + */ + return err; +} + +static void __exit clps6700_exit(void) +{ + unregister_ss_entry(&clps6700_operations); + clps6700_free_resources(); +} + +module_init(clps6700_init); +module_exit(clps6700_exit); --- /dev/null +++ linux-2.4.27/drivers/pcmcia/clps6700.h @@ -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) --- linux-2.4.27/drivers/pcmcia/cs.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/cs.c @@ -3,7 +3,7 @@ Kernel Card Services -- core services cs.c 1.271 2000/10/02 20:27:49 - + The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of @@ -28,7 +28,7 @@ and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL. - + ======================================================================*/ #include @@ -92,7 +92,7 @@ MODULE_AUTHOR("David Hinds "); MODULE_DESCRIPTION("Linux Kernel Card Services " CS_RELEASE "\n options:" OPTIONS); -MODULE_LICENSE("Dual MPL/GPL"); +MODULE_LICENSE("Dual MPL/GPL"); #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") @@ -123,7 +123,7 @@ static const char *version = "cs.c 1.279 2001/10/13 00:08:28 (David Hinds)"; #endif - + /*====================================================================*/ socket_state_t dead_socket = { @@ -299,7 +299,7 @@ Low-level PC Card interface drivers need to register with Card Services using these calls. - + ======================================================================*/ static int setup_socket(socket_info_t *); @@ -331,7 +331,7 @@ s->use_bus_pm = use_bus_pm; s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; spin_lock_init(&s->lock); - + for (i = 0; i < sockets; i++) if (socket_table[i] == NULL) break; socket_table[i] = s; @@ -365,7 +365,7 @@ for (ns = 0; ns < nsock; ns++) { pcmcia_register_socket (ns, ss_entry, 0); } - + return 0; } /* register_ss_entry */ @@ -457,7 +457,7 @@ static void shutdown_socket(socket_info_t *s) { client_t **c; - + DEBUG(1, "cs: shutdown_socket(%p)\n", s); /* Blank out the socket state */ @@ -561,7 +561,7 @@ have several causes: card insertion, a call to reset_socket, or recovery from a suspend/resume cycle. Unreset_socket() sends a CS event that matches the cause of the reset. - + ======================================================================*/ static void reset_socket(socket_info_t *s) @@ -616,7 +616,7 @@ s->state &= ~SOCKET_SETUP_PENDING; } else { send_event(s, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW); - if (s->reset_handle) { + if (s->reset_handle) { s->reset_handle->event_callback_args.info = NULL; EVENT(s->reset_handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW); @@ -631,7 +631,7 @@ valid clients. Parse_events() interprets the event bits from a card status change report. Do_shutdown() handles the high priority stuff associated with a card removal. - + ======================================================================*/ static int send_event(socket_info_t *s, event_t event, int priority) @@ -641,7 +641,7 @@ DEBUG(1, "cs: send_event(sock %d, event %d, pri %d)\n", s->sock, event, priority); ret = 0; - for (; client; client = client->next) { + for (; client; client = client->next) { if (client->state & (CLIENT_UNBOUND|CLIENT_STALE)) continue; if (client->EventMask & event) { @@ -675,10 +675,17 @@ static void parse_events(void *info, u_int events) { socket_info_t *s = info; + if (events & SS_DETECT) { int status; get_socket_status(s, &status); + + /* + * If our socket state indicates that a card is present and + * either the socket has not been suspended (for some reason) + * or the card has been removed, shut down the socket first. + */ if ((s->state & SOCKET_PRESENT) && (!(s->state & SOCKET_SUSPEND) || !(status & SS_DETECT))) @@ -716,7 +723,7 @@ This does not comply with the latest PC Card spec for handling power management events. - + ======================================================================*/ void pcmcia_suspend_socket (socket_info_t *s) @@ -773,7 +780,7 @@ /*====================================================================== Special stuff for managing IO windows, because they are scarce. - + ======================================================================*/ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base, @@ -862,7 +869,7 @@ Access_configuration_register() reads and writes configuration registers in attribute memory. Memory window 0 is reserved for this and the tuple reading services. - + ======================================================================*/ int pcmcia_access_configuration_register(client_handle_t handle, @@ -872,7 +879,7 @@ config_t *c; int addr; u_char val; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); @@ -890,7 +897,7 @@ return CS_CONFIGURATION_LOCKED; addr = (c->ConfigBase + reg->Offset) >> 1; - + switch (reg->Action) { case CS_READ: read_cis_mem(s, 1, addr, 1, &val); @@ -913,7 +920,7 @@ It is normally called by Driver Services after it has identified a newly inserted card. An instance of that driver will then be eligible to register as a client of this socket. - + ======================================================================*/ int pcmcia_bind_device(bind_req_t *req) @@ -949,23 +956,23 @@ region. It is normally called by Driver Services after it has identified a memory device type. An instance of the corresponding driver will then be able to register to control this region. - + ======================================================================*/ int pcmcia_bind_mtd(mtd_bind_t *req) { socket_info_t *s; memory_handle_t region; - + if (CHECK_SOCKET(req->Socket)) return CS_BAD_SOCKET; s = SOCKET(req); - + if (req->Attributes & REGION_TYPE_AM) region = s->a_region; else region = s->c_region; - + while (region) { if (region->info.CardOffset == req->CardOffset) break; region = region->info.next; @@ -973,7 +980,7 @@ if (!region || (region->mtd != NULL)) return CS_BAD_OFFSET; strncpy(region->dev_info, (char *)req->dev_info, DEV_NAME_LEN); - + DEBUG(1, "cs: bind_mtd(): attr 0x%x, offset 0x%x, dev %s\n", req->Attributes, req->CardOffset, (char *)req->dev_info); return CS_SUCCESS; @@ -988,7 +995,7 @@ memory_handle_t region; u_long flags; int i, sn; - + DEBUG(1, "cs: deregister_client(%p)\n", handle); if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; @@ -1007,7 +1014,7 @@ for (region = s->c_region; region; region = region->info.next) if (region->mtd == handle) region->mtd = NULL; } - + sn = handle->Socket; s = socket_table[sn]; if ((handle->state & CLIENT_STALE) || @@ -1032,7 +1039,7 @@ if (--s->real_clients == 0) register_callback(s, NULL, NULL); - + return CS_SUCCESS; } /* deregister_client */ @@ -1043,7 +1050,7 @@ { socket_info_t *s; config_t *c; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); @@ -1055,7 +1062,7 @@ return CS_BAD_ARGS; } else config->Function = handle->Function; - + #ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) { u_char fn = config->Function; @@ -1076,16 +1083,16 @@ return CS_SUCCESS; } #endif - + c = (s->config != NULL) ? &s->config[config->Function] : NULL; - + if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { config->Attributes = 0; config->Vcc = s->socket.Vcc; config->Vpp1 = config->Vpp2 = s->socket.Vpp; return CS_SUCCESS; } - + /* !!! This is a hack !!! */ memcpy(&config->Attributes, &c->Attributes, sizeof(config_t)); config->Attributes |= CONF_VALID_CLIENT; @@ -1099,14 +1106,14 @@ config->NumPorts2 = c->io.NumPorts2; config->Attributes2 = c->io.Attributes2; config->IOAddrLines = c->io.IOAddrLines; - + return CS_SUCCESS; } /* get_configuration_info */ /*====================================================================== Return information about this version of Card Services. - + ======================================================================*/ int pcmcia_get_card_services_info(servinfo_t *info) @@ -1124,7 +1131,7 @@ Note that get_first_client() *does* recognize the Socket field in the request structure. - + ======================================================================*/ int pcmcia_get_first_client(client_handle_t *handle, client_req_t *req) @@ -1239,7 +1246,7 @@ Get the current socket state bits. We don't support the latched SocketState yet: I haven't seen any point for it. - + ======================================================================*/ int pcmcia_get_status(client_handle_t handle, cs_status_t *status) @@ -1247,7 +1254,7 @@ socket_info_t *s; config_t *c; int val; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); @@ -1263,7 +1270,7 @@ return CS_NO_CARD; if (s->state & SOCKET_SETUP_PENDING) status->CardState |= CS_EVENT_CARD_INSERTION; - + /* Get info from the PRR, if necessary */ if (handle->Function == BIND_FN_ALL) { if (status->Function && (status->Function >= s->functions)) @@ -1309,7 +1316,7 @@ /*====================================================================== Change the card address of an already open memory window. - + ======================================================================*/ int pcmcia_get_mem_page(window_handle_t win, memreq_t *req) @@ -1338,7 +1345,7 @@ /*====================================================================== Modify a locked socket configuration - + ======================================================================*/ int pcmcia_modify_configuration(client_handle_t handle, @@ -1346,7 +1353,7 @@ { socket_info_t *s; config_t *c; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); c = CONFIG(handle); @@ -1354,7 +1361,7 @@ return CS_NO_CARD; if (!(c->state & CONFIG_LOCKED)) return CS_CONFIGURATION_LOCKED; - + if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { if (mod->Attributes & CONF_ENABLE_IRQ) { c->Attributes |= CONF_ENABLE_IRQ; @@ -1406,7 +1413,7 @@ win->ctl.flags |= MAP_USE_WAIT; win->ctl.speed = req->AccessSpeed; set_mem_map(win->sock, &win->ctl); - + return CS_SUCCESS; } /* modify_window */ @@ -1416,7 +1423,7 @@ caller with a socket. The driver must have already been bound to a socket with bind_device() -- in fact, bind_device() allocates the client structure that will be used. - + ======================================================================*/ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) @@ -1424,7 +1431,7 @@ client_t *client; socket_info_t *s; socket_t ns; - + /* Look for unbound client with matching dev_info */ client = NULL; for (ns = 0; ns < sockets; ns++) { @@ -1464,7 +1471,7 @@ if (s->state & SOCKET_CARDBUS) client->state |= CLIENT_CARDBUS; - + if ((!(s->state & SOCKET_CARDBUS)) && (s->functions == 0) && (client->Function != BIND_FN_ALL)) { cistpl_longlink_mfc_t mfc; @@ -1479,7 +1486,7 @@ return CS_OUT_OF_RESOURCE; memset(s->config, 0, sizeof(config_t) * s->functions); } - + DEBUG(1, "cs: register_client(): client 0x%p, sock %d, dev %s\n", client, client->Socket, client->dev_info); if (client->EventMask & CS_EVENT_REGISTRATION_COMPLETE) @@ -1501,13 +1508,13 @@ pccard_io_map io = { 0, 0, 0, 0, 1 }; socket_info_t *s; int i; - + if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_CONFIG_LOCKED)) return CS_BAD_HANDLE; handle->state &= ~CLIENT_CONFIG_LOCKED; s = SOCKET(handle); - + #ifdef CONFIG_CARDBUS if (handle->state & CLIENT_CARDBUS) { cb_disable(s); @@ -1515,7 +1522,7 @@ return CS_SUCCESS; } #endif - + if (!(handle->state & CLIENT_STALE)) { config_t *c = CONFIG(handle); if (--(s->lock_count) == 0) { @@ -1536,7 +1543,7 @@ } c->state &= ~CONFIG_LOCKED; } - + return CS_SUCCESS; } /* release_configuration */ @@ -1547,25 +1554,25 @@ the actual socket configuration, so if the client is "stale", we don't bother checking the port ranges against the current socket values. - + ======================================================================*/ int pcmcia_release_io(client_handle_t handle, io_req_t *req) { socket_info_t *s; - + if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ)) return CS_BAD_HANDLE; handle->state &= ~CLIENT_IO_REQ; s = SOCKET(handle); - + #ifdef CONFIG_CARDBUS if (handle->state & CLIENT_CARDBUS) { cb_release(s); return CS_SUCCESS; } #endif - + if (!(handle->state & CLIENT_STALE)) { config_t *c = CONFIG(handle); if (c->state & CONFIG_LOCKED) @@ -1581,7 +1588,7 @@ release_io_space(s, req->BasePort1, req->NumPorts1); if (req->NumPorts2) release_io_space(s, req->BasePort2, req->NumPorts2); - + return CS_SUCCESS; } /* release_io */ @@ -1594,7 +1601,7 @@ return CS_BAD_HANDLE; handle->state &= ~CLIENT_IRQ_REQ; s = SOCKET(handle); - + if (!(handle->state & CLIENT_STALE)) { config_t *c = CONFIG(handle); if (c->state & CONFIG_LOCKED) @@ -1608,16 +1615,16 @@ s->irq.AssignedIRQ = 0; } } - + if (req->Attributes & IRQ_HANDLE_PRESENT) { bus_free_irq(s->cap.bus, req->AssignedIRQ, req->Instance); } -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE if (req->AssignedIRQ != s->cap.pci_irq) undo_irq(req->Attributes, req->AssignedIRQ); #endif - + return CS_SUCCESS; } /* cs_release_irq */ @@ -1626,7 +1633,7 @@ int pcmcia_release_window(window_handle_t win) { socket_info_t *s; - + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) return CS_BAD_HANDLE; s = win->sock; @@ -1640,11 +1647,11 @@ /* Release system memory */ if(!(s->cap.features & SS_CAP_STATIC_MAP)) - release_mem_region(win->base, win->size); + release_mem_resource(win->base, win->size); win->handle->state &= ~CLIENT_WIN_REQ(win->index); win->magic = 0; - + return CS_SUCCESS; } /* release_window */ @@ -1658,13 +1665,13 @@ socket_info_t *s; config_t *c; pccard_io_map iomap; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; i = handle->Socket; s = socket_table[i]; if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; - + #ifdef CONFIG_CARDBUS if (handle->state & CLIENT_CARDBUS) { if (!(req->IntType & INT_CARDBUS)) @@ -1677,7 +1684,7 @@ return CS_SUCCESS; } #endif - + if (req->IntType & INT_CARDBUS) return CS_UNSUPPORTED_MODE; c = CONFIG(handle); @@ -1692,9 +1699,9 @@ s->socket.Vpp = req->Vpp1; if (set_socket(s, &s->socket)) return CS_BAD_VPP; - + c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1; - + /* Pick memory or I/O card, DMA mode, interrupt */ c->IntType = req->IntType; c->Attributes = req->Attributes; @@ -1712,7 +1719,7 @@ s->socket.io_irq = 0; set_socket(s, &s->socket); s->lock_count++; - + /* Set up CIS configuration registers */ base = c->ConfigBase = req->ConfigBase; c->Present = c->CardValues = req->Present; @@ -1757,7 +1764,7 @@ u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1; write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b); } - + /* Configure I/O windows */ if (c->state & CONFIG_IO_REQ) { iomap.speed = io_speed; @@ -1779,24 +1786,24 @@ s->io[i].Config++; } } - + c->state |= CONFIG_LOCKED; handle->state |= CLIENT_CONFIG_LOCKED; return CS_SUCCESS; } /* request_configuration */ /*====================================================================== - + Request_io() reserves ranges of port addresses for a socket. I have not implemented range sharing or alias addressing. - + ======================================================================*/ int pcmcia_request_io(client_handle_t handle, io_req_t *req) { socket_info_t *s; config_t *c; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); @@ -1855,7 +1862,7 @@ hooked, we don't guarantee that an irq will still be available when the configuration is locked. Now that I think about it, there might be a way to fix this using a dummy handler. - + ======================================================================*/ int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) @@ -1863,7 +1870,7 @@ socket_info_t *s; config_t *c; int ret = CS_IN_USE, irq = 0; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); @@ -1875,7 +1882,7 @@ if (c->state & CONFIG_IRQ_REQ) return CS_IN_USE; -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE if (s->irq.AssignedIRQ != 0) { /* If the interrupt is already assigned, it must match */ irq = s->irq.AssignedIRQ; @@ -1909,7 +1916,7 @@ if (req->Attributes & IRQ_HANDLE_PRESENT) { if (bus_request_irq(s->cap.bus, irq, req->Handler, - ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || (s->functions > 1) || (irq == s->cap.pci_irq)) ? SA_SHIRQ : 0, handle->dev_info, req->Instance)) @@ -1919,7 +1926,7 @@ c->irq.Attributes = req->Attributes; s->irq.AssignedIRQ = req->AssignedIRQ = irq; s->irq.Config++; - + c->state |= CONFIG_IRQ_REQ; handle->state |= CLIENT_IRQ_REQ; return CS_SUCCESS; @@ -1938,7 +1945,7 @@ window_t *win; u_long align; int w; - + if (CHECK_HANDLE(*handle)) return CS_BAD_HANDLE; s = SOCKET(*handle); @@ -2005,7 +2012,7 @@ /* Return window handle */ req->Base = win->ctl.sys_start; *wh = win; - + return CS_SUCCESS; } /* request_window */ @@ -2014,14 +2021,14 @@ I'm not sure which "reset" function this is supposed to use, but for now, it uses the low-level interface's reset, not the CIS register. - + ======================================================================*/ int pcmcia_reset_card(client_handle_t handle, client_req_t *req) { int i, ret; socket_info_t *s; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; i = handle->Socket; s = socket_table[i]; @@ -2049,14 +2056,14 @@ These shut down or wake up a socket. They are sort of user initiated versions of the APM suspend and resume actions. - + ======================================================================*/ int pcmcia_suspend_card(client_handle_t handle, client_req_t *req) { int i; socket_info_t *s; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; i = handle->Socket; s = socket_table[i]; @@ -2077,7 +2084,7 @@ { int i; socket_info_t *s; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; i = handle->Socket; s = socket_table[i]; @@ -2095,7 +2102,7 @@ /*====================================================================== These handle user requests to eject or insert a card. - + ======================================================================*/ int pcmcia_eject_card(client_handle_t handle, client_req_t *req) @@ -2103,7 +2110,7 @@ int i, ret; socket_info_t *s; u_long flags; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; i = handle->Socket; s = socket_table[i]; @@ -2119,9 +2126,9 @@ spin_lock_irqsave(&s->lock, flags); do_shutdown(s); spin_unlock_irqrestore(&s->lock, flags); - + return CS_SUCCESS; - + } /* eject_card */ int pcmcia_insert_card(client_handle_t handle, client_req_t *req) @@ -2129,7 +2136,7 @@ int i, status; socket_info_t *s; u_long flags; - + if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; i = handle->Socket; s = socket_table[i]; @@ -2157,7 +2164,7 @@ Maybe this should send a CS_EVENT_CARD_INSERTION event if we haven't sent one to this client yet? - + ======================================================================*/ int pcmcia_set_event_mask(client_handle_t handle, eventmask_t *mask) @@ -2189,7 +2196,7 @@ printk(KERN_NOTICE); else printk(KERN_NOTICE "%s: ", handle->dev_info); - + for (i = 0; i < SERVICE_COUNT; i++) if (service_table[i].key == err->func) break; if (i < SERVICE_COUNT) @@ -2347,13 +2354,13 @@ default: return CS_UNSUPPORTED_FUNCTION; break; } - + } /* CardServices */ /*====================================================================== OS-specific module glue goes here - + ======================================================================*/ /* in alpha order */ EXPORT_SYMBOL(pcmcia_access_configuration_register); @@ -2450,4 +2457,3 @@ module_exit(exit_pcmcia_cs); /*====================================================================*/ - --- linux-2.4.27/drivers/pcmcia/ds.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/ds.c @@ -55,6 +55,7 @@ #include #include #include +#include /*====================================================================*/ @@ -880,6 +881,8 @@ EXPORT_SYMBOL(register_pccard_driver); EXPORT_SYMBOL(unregister_pccard_driver); +static devfs_handle_t devfs_handle; + /*====================================================================*/ int __init init_pcmcia_ds(void) @@ -957,8 +960,13 @@ if (i == -EBUSY) printk(KERN_NOTICE "unable to find a free device # for " "Driver Services\n"); - else + else { major_dev = i; + devfs_handle = devfs_register(NULL, "pcmcia", DEVFS_FL_DEFAULT, + major_dev, 0, + S_IFCHR | S_IRUSR | S_IWUSR, + &ds_fops, NULL); + } #ifdef CONFIG_PROC_FS if (proc_pccard) @@ -983,7 +991,9 @@ remove_proc_entry("drivers", proc_pccard); #endif if (major_dev != -1) - unregister_chrdev(major_dev, "pcmcia"); + devfs_unregister_chrdev(major_dev, "pcmcia"); + devfs_unregister(devfs_handle); + for (i = 0; i < sockets; i++) pcmcia_deregister_client(socket_table[i].handle); sockets = 0; --- linux-2.4.27/drivers/pcmcia/i82365.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/i82365.c @@ -28,7 +28,7 @@ and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL. - + ======================================================================*/ #include @@ -65,6 +65,15 @@ #include "ricoh.h" #include "o2micro.h" +#ifdef CONFIG_ARCH_EBSA110 +#define I365_MASK (1 << 6) +#define SOCKIRQ2REG(sock,irq) ((irq) ? ((sock) ? 3 : 4) : 0) +#define REG2SOCKIRQ(sock,reg) (6) +#else +#define SOCKIRQ2REG(sock,irq) (irq) +#define REG2SOCKIRQ(sock,reg) (reg) +#endif + #ifdef PCMCIA_DEBUG static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); @@ -173,13 +182,15 @@ } socket_info_t; /* Where we keep track of our sockets... */ -static int sockets = 0; -static socket_info_t socket[8] = { - { 0, }, /* ... */ -}; +static int sockets /* = 0 */; +static socket_info_t socket[8] /* = { + { 0, }, +} */; /* Default ISA interrupt mask */ +#ifndef I365_MASK #define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */ +#endif static int grab_irq; static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED; @@ -303,7 +314,7 @@ The VIA controllers also use these routines, as they are mostly Cirrus lookalikes, without the timing registers. - + ======================================================================*/ #define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b)))) @@ -389,7 +400,7 @@ Code to save and restore global state information for Vadem VG468 and VG469 controllers, and to set and report global configuration options. - + ======================================================================*/ static void vg46x_get_state(u_short s) @@ -411,7 +422,7 @@ static u_int __init vg46x_set_opts(u_short s, char *buf) { vg46x_state_t *p = &socket[s].state.vg46x; - + flip(p->ctl, VG468_CTL_ASYNC, async_clock); flip(p->ema, VG469_MODE_CABLE, cable_mode); if (p->ctl & VG468_CTL_ASYNC) @@ -436,7 +447,7 @@ /*====================================================================== Generic routines to get and set controller options - + ======================================================================*/ static void get_bridge_state(u_short s) @@ -489,7 +500,7 @@ /*====================================================================== Interrupt testing code, for ISA and PCI interrupts - + ======================================================================*/ static volatile u_int irq_hits; @@ -517,7 +528,7 @@ } /* Generate one interrupt */ - i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4)); + i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (SOCKIRQ2REG(sock, irq) << 4)); i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ); udelay(1000); @@ -526,7 +537,7 @@ /* mask all interrupts */ i365_set(sock, I365_CSCINT, 0); DEBUG(2, " hits = %d\n", irq_hits); - + return (irq_hits != 1); } @@ -540,7 +551,7 @@ /* Don't probe level-triggered interrupts -- reserved for PCI */ mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8)); #endif - + if (do_scan) { set_bridge_state(sock); i365_set(sock, I365_CSCINT, 0); @@ -551,7 +562,7 @@ if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0)) mask1 ^= (1 << i); } - + printk(KERN_INFO " ISA irqs ("); if (mask1) { printk("scanned"); @@ -565,12 +576,12 @@ if (!cs_irq && (poll_interval == 0)) poll_interval = HZ; } printk(") = "); - + for (i = 0; i < 16; i++) if (mask1 & (1<ioaddr > 0) request_region(t->ioaddr, 2, "i82365"); - + if (base == 0) printk("\n"); printk(KERN_INFO " %s", pcic[type].name); printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x", @@ -713,7 +724,7 @@ mask &= I365_MASK & set_bridge_opts(base, ns); /* Scan for ISA interrupts */ mask = isa_scan(base, mask); - + /* Poll if only two interrupts available */ if (!poll_interval) { u_int tmp = (mask & 0xff20); @@ -735,15 +746,15 @@ printk(" status change on irq %d\n", cs_irq); } } - + if (!isa_irq) { if (poll_interval == 0) poll_interval = HZ; printk(" polling interval = %d ms\n", poll_interval * 1000 / HZ); - + } - + /* Update socket interrupt information, capabilities */ for (i = 0; i < ns; i++) { t[i].cap.features |= SS_CAP_PCCARD; @@ -866,12 +877,12 @@ events = pending_events[i]; pending_events[i] = 0; spin_unlock_irq(&pending_event_lock); - /* - SS_DETECT events need a small delay here. The reason for this is that + /* + SS_DETECT events need a small delay here. The reason for this is that the "is there a card" electronics need time to see the card after the - "we have a card coming in" electronics have seen it. + "we have a card coming in" electronics have seen it. */ - if (events & SS_DETECT) + if (events & SS_DETECT) mdelay(4); if (socket[i].handler) socket[i].handler(socket[i].info, events); @@ -890,7 +901,7 @@ int i, j, csc; u_int events, active; u_long flags = 0; - + DEBUG(4, "i82365: pcic_interrupt(%d)\n", irq); for (j = 0; j < 20; j++) { @@ -906,20 +917,20 @@ continue; } events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0; - - + + /* Several sockets will send multiple "new card detected" - events in rapid succession. However, the rest of the pcmcia expects + events in rapid succession. However, the rest of the pcmcia expects only one such event. We just ignore these events by having a timeout */ if (events) { - if ((jiffies - last_detect_jiffies)<(HZ/20)) + if ((jiffies - last_detect_jiffies)<(HZ/20)) events = 0; last_detect_jiffies = jiffies; - + } - + if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD) events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0; else { @@ -980,11 +991,11 @@ static int i365_get_status(u_short sock, u_int *value) { u_int status; - + status = i365_get(sock, I365_STATUS); *value = ((status & I365_CS_DETECT) == I365_CS_DETECT) ? SS_DETECT : 0; - + if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG; else { @@ -1005,7 +1016,7 @@ *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD; } } - + DEBUG(1, "i82365: GetStatus(%d) = %#4.4x\n", sock, *value); return 0; } /* i365_get_status */ @@ -1016,7 +1027,7 @@ { socket_info_t *t = &socket[sock]; u_char reg, vcc, vpp; - + reg = i365_get(sock, I365_POWER); state->flags = (reg & I365_PWR_AUTO) ? SS_PWR_AUTO : 0; state->flags |= (reg & I365_PWR_OUT) ? SS_OUTPUT_ENA : 0; @@ -1057,14 +1068,14 @@ reg = i365_get(sock, I365_INTCTL); state->flags |= (reg & I365_PC_RESET) ? 0 : SS_RESET; if (reg & I365_PC_IOCARD) state->flags |= SS_IOCARD; - state->io_irq = reg & I365_IRQ_MASK; - + state->io_irq = REG2SOCKIRQ(sock, reg & I365_IRQ_MASK); + /* speaker control */ if (t->flags & IS_CIRRUS) { if (i365_get(sock, PD67_MISC_CTL_1) & PD67_MC1_SPKR_ENA) state->flags |= SS_SPKR_ENA; } - + /* Card status change mask */ reg = i365_get(sock, I365_CSCINT); state->csc_mask = (reg & I365_CSC_DETECT) ? SS_DETECT : 0; @@ -1075,7 +1086,7 @@ state->csc_mask |= (reg & I365_CSC_BVD2) ? SS_BATWARN : 0; state->csc_mask |= (reg & I365_CSC_READY) ? SS_READY : 0; } - + DEBUG(1, "i82365: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " "io_irq %d, csc_mask %#2.2x\n", sock, state->flags, state->Vcc, state->Vpp, state->io_irq, state->csc_mask); @@ -1088,21 +1099,21 @@ { socket_info_t *t = &socket[sock]; u_char reg; - + DEBUG(1, "i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags, state->Vcc, state->Vpp, state->io_irq, state->csc_mask); - + /* First set global controller options */ set_bridge_state(sock); - + /* IO card, RESET flag, IO interrupt */ reg = t->intr; - reg |= state->io_irq; + reg |= SOCKIRQ2REG(sock, state->io_irq); reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; i365_set(sock, I365_INTCTL, reg); - + reg = I365_PWR_NORESET; if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO; if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT; @@ -1165,7 +1176,7 @@ default: return -EINVAL; } } - + if (reg != i365_get(sock, I365_POWER)) i365_set(sock, I365_POWER, reg); @@ -1175,9 +1186,9 @@ i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA, state->flags & SS_SPKR_ENA); } - + /* Card status change interrupt mask */ - reg = t->cs_irq << 4; + reg = SOCKIRQ2REG(sock, t->cs_irq) << 4; if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT; if (state->flags & SS_IOCARD) { if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG; @@ -1188,7 +1199,7 @@ } i365_set(sock, I365_CSCINT, reg); i365_get(sock, I365_CSC); - + return 0; } /* i365_set_socket */ @@ -1197,7 +1208,7 @@ static int i365_get_io_map(u_short sock, struct pccard_io_map *io) { u_char map, ioctl, addr; - + map = io->map; if (map > 1) return -EINVAL; io->start = i365_get_pair(sock, I365_IO(map)+I365_W_START); @@ -1220,7 +1231,7 @@ static int i365_set_io_map(u_short sock, struct pccard_io_map *io) { u_char map, ioctl; - + DEBUG(1, "i82365: SetIOMap(%d, %d, %#2.2x, %d ns, " "%#4.4x-%#4.4x)\n", sock, io->map, io->flags, io->speed, io->start, io->stop); @@ -1250,30 +1261,30 @@ { u_short base, i; u_char map, addr; - + map = mem->map; if (map > 4) return -EINVAL; addr = i365_get(sock, I365_ADDRWIN); mem->flags = (addr & I365_ENA_MEM(map)) ? MAP_ACTIVE : 0; base = I365_MEM(map); - + i = i365_get_pair(sock, base+I365_W_START); mem->flags |= (i & I365_MEM_16BIT) ? MAP_16BIT : 0; mem->flags |= (i & I365_MEM_0WS) ? MAP_0WS : 0; mem->sys_start = ((u_long)(i & 0x0fff) << 12); - + i = i365_get_pair(sock, base+I365_W_STOP); mem->speed = (i & I365_MEM_WS0) ? 1 : 0; mem->speed += (i & I365_MEM_WS1) ? 2 : 0; mem->speed = to_ns(mem->speed); mem->sys_stop = ((u_long)(i & 0x0fff) << 12) + 0x0fff; - + i = i365_get_pair(sock, base+I365_W_OFF); mem->flags |= (i & I365_MEM_WRPROT) ? MAP_WRPROT : 0; mem->flags |= (i & I365_MEM_REG) ? MAP_ATTRIB : 0; mem->card_start = ((u_int)(i & 0x3fff) << 12) + mem->sys_start; mem->card_start &= 0x3ffffff; - + DEBUG(1, "i82365: GetMemMap(%d, %d) = %#2.2x, %d ns, %#5.5lx-%#5." "5lx, %#5.5x\n", sock, mem->map, mem->flags, mem->speed, mem->sys_start, mem->sys_stop, mem->card_start); @@ -1281,12 +1292,12 @@ } /* i365_get_mem_map */ /*====================================================================*/ - + static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem) { u_short base, i; u_char map; - + DEBUG(1, "i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5" "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed, mem->sys_start, mem->sys_stop, mem->card_start); @@ -1297,17 +1308,17 @@ return -EINVAL; if ((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff)) return -EINVAL; - + /* Turn off the window before changing anything */ if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map)); - + base = I365_MEM(map); i = (mem->sys_start >> 12) & 0x0fff; if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; if (mem->flags & MAP_0WS) i |= I365_MEM_0WS; i365_set_pair(sock, base+I365_W_START, i); - + i = (mem->sys_stop >> 12) & 0x0fff; switch (to_cycles(mem->speed)) { case 0: break; @@ -1316,12 +1327,12 @@ default: i |= I365_MEM_WS1 | I365_MEM_WS0; break; } i365_set_pair(sock, base+I365_W_STOP, i); - + i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG; i365_set_pair(sock, base+I365_W_OFF, i); - + /* Turn on the window if necessary */ if (mem->flags & MAP_ACTIVE) i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map)); @@ -1332,7 +1343,7 @@ Routines for accessing socket information and register dumps via /proc/bus/pccard/... - + ======================================================================*/ #ifdef CONFIG_PROC_FS @@ -1353,7 +1364,7 @@ u_short sock = (socket_info_t *)data - socket; char *p = buf; int i, top; - + u_long flags = 0; ISA_LOCK(sock, flags); top = 0x40; @@ -1399,7 +1410,7 @@ /*====================================================================*/ -/* this is horribly ugly... proper locking needs to be done here at +/* this is horribly ugly... proper locking needs to be done here at * some time... */ #define LOCKED(x) do { \ int retval; \ @@ -1532,7 +1543,7 @@ /* Set up interrupt handler(s) */ if (grab_irq != 0) request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt); - + if (register_ss_entry(sockets, &pcic_operations) != 0) printk(KERN_NOTICE "i82365: register_ss_entry() failed\n"); @@ -1544,9 +1555,9 @@ poll_timer.expires = jiffies + poll_interval; add_timer(&poll_timer); } - + return 0; - + } /* init_i82365 */ static void __exit exit_i82365(void) --- linux-2.4.27/drivers/pcmcia/rsrc_mgr.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/rsrc_mgr.c @@ -28,7 +28,7 @@ and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL. - + ======================================================================*/ #define __NO_VERSION__ @@ -55,6 +55,10 @@ #include #include "cs_internal.h" +#ifndef ISAMEM_PHYS +#define ISAMEM_PHYS 0 +#endif + /*====================================================================*/ /* Parameters that can be set with 'insmod' */ @@ -62,7 +66,7 @@ #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") INT_MODULE_PARM(probe_mem, 1); /* memory probe? */ -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE INT_MODULE_PARM(probe_io, 1); /* IO port probe? */ INT_MODULE_PARM(mem_limit, 0x10000); #endif @@ -85,7 +89,7 @@ /* IO port resource database */ static resource_map_t io_db = { 0, 0, &io_db }; -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE typedef struct irq_info_t { u_int Attributes; @@ -133,6 +137,7 @@ static inline int check_mem_resource(unsigned long b, unsigned long n, struct pci_dev *dev) { + b += ISAMEM_PHYS; return check_resource(resource_parent(b, n, IORESOURCE_MEM, dev), b, n); } @@ -169,10 +174,15 @@ static int request_mem_resource(unsigned long b, unsigned long n, char *name, struct pci_dev *dev) { - struct resource *res = make_resource(b, n, IORESOURCE_MEM, name); - struct resource *pr = resource_parent(b, n, IORESOURCE_MEM, dev); + struct resource *res; + struct resource *pr; int err = -ENOMEM; + b += ISAMEM_PHYS; + + res = make_resource(b, n, IORESOURCE_MEM, name); + pr = resource_parent(b, n, IORESOURCE_MEM, dev); + if (res) { err = request_resource(pr, res); if (err) @@ -181,10 +191,16 @@ return err; } +void release_mem_resource(unsigned long b, unsigned long n) +{ + b += ISAMEM_PHYS; + release_mem_region(b, n); +} + /*====================================================================== These manage the internal databases of available resources. - + ======================================================================*/ static int add_interval(resource_map_t *map, u_long base, u_long num) @@ -248,25 +264,25 @@ These routines examine a region of IO or memory addresses to determine what ranges might be genuinely available. - + ======================================================================*/ -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE static void do_io_probe(ioaddr_t base, ioaddr_t num) { - + ioaddr_t i, j, bad, any; u_char *b, hole, most; - + printk(KERN_INFO "cs: IO port probe 0x%04x-0x%04x:", base, base+num-1); - + /* First, what does a floating port look like? */ b = kmalloc(256, GFP_KERNEL); if (!b) { printk(KERN_ERR "do_io_probe: unable to kmalloc 256 bytes"); return; - } + } memset(b, 0, 256); for (i = base, most = 0; i < base+num; i += 8) { if (check_io_resource(i, 8, NULL)) @@ -308,7 +324,7 @@ printk(" %#04x-%#04x", bad, i-1); } } - + printk(any ? "\n" : " clean.\n"); } #endif @@ -318,7 +334,7 @@ The memory probe. If the memory list includes a 64K-aligned block below 1MB, we probe in 64K chunks, and as soon as we accumulate at least mem_limit free space, we quit. - + ======================================================================*/ static int do_mem_probe(u_long base, u_long num, @@ -332,7 +348,7 @@ bad = fail = 0; step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); for (i = j = base; i < base+num; i = j + step) { - if (!fail) { + if (!fail) { for (j = i; j < base+num; j += step) if ((check_mem_resource(j, step, s->cap.cb_dev) == 0) && is_valid(j)) @@ -356,7 +372,7 @@ return (num - bad); } -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE static u_long inv_probe(int (*is_valid)(u_long), int (*do_cksum)(u_long), @@ -383,7 +399,7 @@ static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 }; static int hi = 0, lo = 0; u_long b, i, ok = 0; - + if (!probe_mem) return; /* We do up to four passes through the list */ if (!force_low) { @@ -414,14 +430,14 @@ } } -#else /* CONFIG_ISA */ +#else /* CONFIG_PCMCIA_PROBE */ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), int force_low, socket_info_t *s) { resource_map_t *m, *n; static int done = 0; - + if (!probe_mem || done++) return; @@ -432,7 +448,7 @@ } } -#endif /* CONFIG_ISA */ +#endif /* CONFIG_PCMCIA_PROBE */ /*====================================================================== @@ -444,7 +460,7 @@ should be a power of two, greater than or equal to 'num'. A value of 0 means that all bits of *base are significant. *base should also be strictly less than 'align'. - + ======================================================================*/ int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, @@ -452,7 +468,7 @@ { ioaddr_t try; resource_map_t *m; - + for (m = io_db.next; m != &io_db; m = m->next) { try = (m->base & ~(align-1)) + *base; for (try = (try >= m->base) ? try : try+align; @@ -500,10 +516,10 @@ This checks to see if an interrupt is available, with support for interrupt sharing. We don't support reserving interrupts yet. If the interrupt is available, we allocate it. - + ======================================================================*/ -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE static void fake_irq(int i, void *d, struct pt_regs *r) { } static inline int check_irq(int irq) @@ -570,7 +586,7 @@ /*====================================================================*/ -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE void undo_irq(u_int Attributes, int irq) { @@ -600,7 +616,7 @@ The various adjust_* calls form the external interface to the resource database. - + ======================================================================*/ static int adjust_memory(adjust_t *adj) @@ -632,7 +648,7 @@ default: ret = CS_UNSUPPORTED_FUNCTION; } - + return ret; } @@ -641,7 +657,7 @@ static int adjust_io(adjust_t *adj) { int base, num; - + base = adj->resource.io.BasePort; num = adj->resource.io.NumPorts; if ((base < 0) || (base > 0xffff)) @@ -653,7 +669,7 @@ case ADD_MANAGED_RESOURCE: if (add_interval(&io_db, base, num) != 0) return CS_IN_USE; -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE if (probe_io) do_io_probe(base, num); #endif @@ -673,15 +689,15 @@ static int adjust_irq(adjust_t *adj) { -#ifdef CONFIG_ISA +#ifdef CONFIG_PCMCIA_PROBE int irq; irq_info_t *info; - + irq = adj->resource.irq.IRQ; if ((irq < 0) || (irq > 15)) return CS_BAD_IRQ; info = &irq_table[irq]; - + switch (adj->Action) { case ADD_MANAGED_RESOURCE: if (info->Attributes & RES_REMOVED) @@ -716,7 +732,7 @@ { if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; - + switch (adj->Resource) { case RES_MEMORY_RANGE: return adjust_memory(adj); @@ -736,7 +752,7 @@ void release_resource_db(void) { resource_map_t *p, *q; - + for (p = mem_db.next; p != &mem_db; p = q) { q = p->next; kfree(p); --- linux-2.4.27/drivers/pcmcia/sa1100.h~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100.h @@ -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; --- linux-2.4.27/drivers/pcmcia/sa1100_adsbitsy.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_adsbitsy.c @@ -11,28 +11,156 @@ */ #include #include +#include #include +#include +#include #include "sa1100_generic.h" #include "sa1111_generic.h" +int adsbitsy_smc91111_present(void); + +#ifndef CONFIG_SMC91111 +#define adsbitsy_smc91111_present() 0 +#endif + +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { S0_CD_VALID, "SA1111 PCMCIA card detect" }, + { S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" }, + { S1_CD_VALID, "SA1111 CF card detect" }, + { S1_BVD1_STSCHG, "SA1111 CF BVD1" }, +}; + static int adsbitsy_pcmcia_init(struct pcmcia_init *init) { - /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ - PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + int ret=0; + int nirq = 0; + int slots = 0; + int i; - /* Disable Power 3.3V/5V for PCMCIA/CF */ - PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; + /* Set GPIO_A<1:0> to be outputs for PCMCIA power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1); - /* Why? */ - MECR = 0x09430943; + /* Disable Power 3.3V/5V for PCMCIA */ + PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1; - return sa1111_pcmcia_init(init); + 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 -adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *conf) +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_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, --- /dev/null +++ linux-2.4.27/drivers/pcmcia/sa1100_adsbitsyplus.c @@ -0,0 +1,236 @@ +/* + * drivers/pcmcia/sa1100_adsbitsyplus.c + * + * PCMCIA implementation routines for ADS Bitsy Plus + * + * Created Feb 7, 2003 by Robert Whaley + * + * This file comes from sa1100_adsbitsy.c of Woojung Huh + * + */ +#include +#include +#include + +#include +#include +#include + +#include "sa1100_generic.h" +#include "sa1111_generic.h" + +int adsbitsy_smc91111_present(void); + +#ifndef CONFIG_SMC91111 +#define adsbitsy_smc91111_present() 0 +#endif + +static struct irqs { + int irq; + const char *str; +} irqs[] = { + { S0_CD_VALID, "SA1111 PCMCIA card detect" }, + { S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" }, + { S1_CD_VALID, "SA1111 CF card detect" }, + { S1_BVD1_STSCHG, "SA1111 CF BVD1" }, +}; + +#define sock0_3_3_reverse_logic() ((ADS_CPLD_IO2 & ADS_IO2_CPLD_REV_MASK) >= ADS_IO2_CPLD_REV_5_MAGIC) + +static int adsbitsyplus_pcmcia_init(struct pcmcia_init *init) +{ + int ret=0; + int nirq = 0; + int slots = 0; + int i; + + /* Set GPIO_A<1:0> to be outputs for PCMCIA power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1); + + /* Disable Power 3.3V/5V for PCMCIA */ + if (sock0_3_3_reverse_logic()) + PA_DWR = (PA_DWR & ~GPIO_GPIO0) | GPIO_GPIO1; + else + PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1; + + if (!request_mem_region(_PCCR, 512, "PCMCIA")) + return -1; + + + INTPOL1 |= SA1111_IRQMASK_HI(S0_READY_NINT) | + SA1111_IRQMASK_HI(S0_CD_VALID) | + SA1111_IRQMASK_HI(S0_BVD1_STSCHG); + + nirq = 2; + slots = 1; + + if (!adsbitsy_smc91111_present()) { + /* If the SMC91111 is used CF cannot be used */ + /* Set GPIO_A<3:2> to be outputs for CF power controller: */ + PA_DDR &= ~(GPIO_GPIO2 | GPIO_GPIO3); + + /* Disable Power 3.3V/5V for CF */ + PA_DWR |= GPIO_GPIO2 | GPIO_GPIO3; + + INTPOL1 |= SA1111_IRQMASK_HI(S1_READY_NINT) | + SA1111_IRQMASK_HI(S1_CD_VALID) | + SA1111_IRQMASK_HI(S1_BVD1_STSCHG); + + nirq = 4; + slots = 2; + } + + for (i = ret = 0; i < nirq; i++) { + ret = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, + irqs[i].str, NULL); + if (ret) + break; + } + + if (i < nirq) { + printk(KERN_ERR "sa1111_pcmcia: unable to grab IRQ%d (%d)\n", + irqs[i].irq, ret); + while (i--) + free_irq(irqs[i].irq, NULL); + + release_mem_region(_PCCR, 16); + } + + return ret ? -1 : slots; +} + +static int adsbitsyplus_pcmcia_shutdown(void) +{ + + free_irq(S0_CD_VALID, NULL); + free_irq(S0_BVD1_STSCHG, NULL); + INTPOL1 &= ~(SA1111_IRQMASK_HI(S0_CD_VALID) | SA1111_IRQMASK_HI(S0_BVD1_STSCHG)); + + if (!adsbitsy_smc91111_present()) { + free_irq(S1_CD_VALID, NULL); + free_irq(S1_BVD1_STSCHG, NULL); + INTPOL1 &= ~(SA1111_IRQMASK_HI(S1_CD_VALID) | SA1111_IRQMASK_HI(S1_BVD1_STSCHG)); + } + + return 0; +} + + +static int adsbitsyplus_pcmcia_socket_state(struct pcmcia_state_array *state) +{ + unsigned long status; + + if (adsbitsy_smc91111_present()) { + if(state->size<1) return -1; + } + else + if(state->size<2) return -1; + + memset(state->state, 0, + (state->size)*sizeof(struct pcmcia_state)); + + status = PCSR; + + state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1; + state->state[0].ready = status & PCSR_S0_READY ? 1 : 0; + state->state[0].bvd1 = status & PCSR_S0_BVD1 ? 1 : 0; + state->state[0].bvd2 = status & PCSR_S0_BVD2 ? 1 : 0; + state->state[0].wrprot = status & PCSR_S0_WP ? 1 : 0; + state->state[0].vs_3v = status & PCSR_S0_VS1 ? 0 : 1; + state->state[0].vs_Xv = status & PCSR_S0_VS2 ? 0 : 1; + + if (state->size > 1) { + if (adsbitsy_smc91111_present()) { + // If there is SMC91111 on ADS Bitsy connector board + // it returns not detect/ready/... + state->state[1].detect = 0; + state->state[1].ready = 0; + state->state[1].bvd1 = 0; + state->state[1].bvd2 = 0; + state->state[1].wrprot = 0; + state->state[1].vs_3v = 0; + state->state[1].vs_Xv = 0; + } + else { + state->state[1].detect = status & PCSR_S1_DETECT ? 0 : 1; + state->state[1].ready = status & PCSR_S1_READY ? 1 : 0; + state->state[1].bvd1 = status & PCSR_S1_BVD1 ? 1 : 0; + state->state[1].bvd2 = status & PCSR_S1_BVD2 ? 1 : 0; + state->state[1].wrprot = status & PCSR_S1_WP ? 1 : 0; + state->state[1].vs_3v = status & PCSR_S1_VS1 ? 0 : 1; + state->state[1].vs_Xv = status & PCSR_S1_VS2 ? 0 : 1; + } + } + return 1; +} + +static int adsbitsyplus_pcmcia_configure_socket(const struct pcmcia_configure *conf) +{ + unsigned int pa_dwr_mask, pa_dwr_set; + int ret; + + switch (conf->sock) { + case 0: + pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1; + + if (sock0_3_3_reverse_logic()) { + switch (conf->vcc) { + default: + case 0: pa_dwr_set = GPIO_GPIO1; break; + case 33: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break; + case 50: pa_dwr_set = 0; break; + } + } else { + switch (conf->vcc) { + default: + case 0: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break; + case 33: pa_dwr_set = GPIO_GPIO1; break; + case 50: pa_dwr_set = GPIO_GPIO0; break; + } + } + break; + + case 1: + pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3; + + switch (conf->vcc) { + default: + case 0: pa_dwr_set = GPIO_GPIO2 | GPIO_GPIO3; break; + case 33: pa_dwr_set = GPIO_GPIO2; break; + case 50: pa_dwr_set = GPIO_GPIO3; break; + } + break; + + default: + return -1; + } + + if (conf->vpp != conf->vcc && conf->vpp != 0) { + printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n", + __FUNCTION__, conf->vpp); + return -1; + } + + ret = sa1111_pcmcia_configure_socket(conf); + if (ret == 0) { + unsigned long flags; + + local_irq_save(flags); + PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; + local_irq_restore(flags); + } + + return ret; +} + +struct pcmcia_low_level adsbitsyplus_pcmcia_ops = { + init: adsbitsyplus_pcmcia_init, + shutdown: adsbitsyplus_pcmcia_shutdown, + socket_state: adsbitsyplus_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: adsbitsyplus_pcmcia_configure_socket, + + socket_init: sa1111_pcmcia_socket_init, + socket_suspend: sa1111_pcmcia_socket_suspend, +}; + --- linux-2.4.27/drivers/pcmcia/sa1100_freebird.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_freebird.c @@ -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); --- linux-2.4.27/drivers/pcmcia/sa1100_generic.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_generic.c @@ -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); --- linux-2.4.27/drivers/pcmcia/sa1100_graphicsclient.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_graphicsclient.c @@ -3,6 +3,8 @@ * * PCMCIA implementation routines for Graphics Client Plus * + * Nov/14/01 Woojung + * Set MECR at initializing time * 9/12/01 Woojung * Turn power OFF at startup * 1/31/2001 Woojung Huh @@ -19,11 +21,6 @@ #include #include "sa1100_generic.h" -#error This is broken! - -#define S0_CD_IRQ 60 // Socket 0 Card Detect IRQ -#define S0_STS_IRQ 55 // Socket 0 PCMCIA IRQ - static volatile unsigned long *PCMCIA_Status = ((volatile unsigned long *) ADS_p2v(_ADS_CS_STATUS)); @@ -46,20 +43,23 @@ *PCMCIA_Power &= ~0x03; /* Register interrupts */ - irq = S0_CD_IRQ; + irq = IRQ_GRAPHICSCLIENT_S0_CD; res = request_irq(irq, init->handler, SA_INTERRUPT, "PCMCIA 0 CD", NULL); if (res < 0) { - printk(KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq); + printk(KERN_ERR "%s: Request for IRQ %u failed\n", __FUNCTION__, irq); return -1; } + // Nov/14/01 WH + MECR = 0x00000943; + return 1; // 1 PCMCIA Slot } static int gcplus_pcmcia_shutdown(void) { /* disable IRQs */ - free_irq( S0_CD_IRQ, NULL); + free_irq( IRQ_GRAPHICSCLIENT_S0_CD, NULL); /* Shutdown PCMCIA power */ mdelay(2); // 2msec @@ -74,9 +74,6 @@ if(state_array->size<1) return -1; - memset(state_array->state, 0, - (state_array->size)*sizeof(struct pcmcia_state)); - levels=*PCMCIA_Status; state_array->state[0].detect=(levels & ADS_CS_ST_A_CD)?1:0; @@ -96,7 +93,7 @@ return -1; if (info->sock == 0) - info->irq = S0_STS_IRQ; + info->irq = IRQ_GRAPHICSCLIENT_S0_STS; return 0; } @@ -143,6 +140,11 @@ restore_flags(flags); + if (configure->irq) + enable_irq(IRQ_GRAPHICSCLIENT_S0_STS); + else + disable_irq(IRQ_GRAPHICSCLIENT_S0_STS); + return 0; } --- linux-2.4.27/drivers/pcmcia/sa1100_graphicsmaster.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_graphicsmaster.c @@ -12,13 +12,13 @@ #include #include +#include #include "sa1100_generic.h" #include "sa1111_generic.h" static int graphicsmaster_pcmcia_init(struct pcmcia_init *init) { - int return_val=0; /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); @@ -26,9 +26,6 @@ /* Disable Power 3.3V/5V for PCMCIA/CF */ PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; - /* why? */ - MECR = 0x09430943; - return sa1111_pcmcia_init(init); } @@ -59,6 +56,10 @@ case 33: pa_dwr_set = GPIO_GPIO3; break; case 50: pa_dwr_set = GPIO_GPIO2; break; } + break; + + default: + return -1; } if (conf->vpp != conf->vcc && conf->vpp != 0) { --- linux-2.4.27/drivers/pcmcia/sa1100_h3600.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_h3600.c @@ -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 */ --- linux-2.4.27/drivers/pcmcia/sa1100_jornada720.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_jornada720.c @@ -8,6 +8,7 @@ #include #include +#include #include "sa1100_generic.h" #include "sa1111_generic.h" @@ -88,7 +89,7 @@ local_irq_save(flags); PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; - locla_irq_restore(flags); + local_irq_restore(flags); } return ret; --- linux-2.4.27/drivers/pcmcia/sa1100_pangolin.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_pangolin.c @@ -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; --- linux-2.4.27/drivers/pcmcia/sa1100_shannon.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_shannon.c @@ -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; --- linux-2.4.27/drivers/pcmcia/sa1100_simpad.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_simpad.c @@ -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; --- linux-2.4.27/drivers/pcmcia/sa1100_stork.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_stork.c @@ -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) --- linux-2.4.27/drivers/pcmcia/sa1100_yopy.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/pcmcia/sa1100_yopy.c @@ -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; --- /dev/null +++ linux-2.4.27/drivers/pld/Makefile @@ -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: + --- /dev/null +++ linux-2.4.27/drivers/pld/pld_epxa.c @@ -0,0 +1,375 @@ + +/* + * drivers/char/epxapld.c + * + * Copyright (C) 2001 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define PLD_CONF00_TYPE (volatile unsigned int *) +#define MODE_CTRL00_TYPE (volatile unsigned int *) +//#define DEBUG(x) x +#define DEBUG(x) + +#include +#include +#ifdef CONFIG_PLD_HOTSWAP +#include +#endif +#include + +/* + * Macros + */ + + +#define PLD_BASE (IO_ADDRESS(EXC_PLD_CONFIG00_BASE)) +#define CLOCK_DIV_RATIO ((1 + EXC_AHB2_CLK_FREQUENCY/32000000) & CONFIG_CONTROL_CLOCK_RATIO_MSK) +/* + * STRUCTURES + */ + + +struct pld_sbihdr{ + unsigned int fpos; + unsigned int temp; +}; + +static DECLARE_MUTEX(pld_sem); + + +static void lock_pld (void) +{ + /* Lock the pld i/f */ + unsigned int tmp; + + tmp = readl(CONFIG_CONTROL(PLD_BASE)); + tmp |= CONFIG_CONTROL_LK_MSK; + + writel(tmp,CONFIG_CONTROL(PLD_BASE)); +} + +static void unlock_excalibur_pld (void) +{ + /* Unlock the pld i/f */ + + if (readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_LK_MSK ){ + writel(CONFIG_UNLOCK_MAGIC, CONFIG_UNLOCK(PLD_BASE)); + while (readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_LK_MSK); + } +} + + +static int place_pld_into_configure_mode (void) +{ + unsigned int tmp; + + + if( readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_CO_MSK ){ + /* + * Already being configured!!! + */ + printk(KERN_WARNING "pld0: Device already being configured!\n"); + return -EBUSY; + } + + /* Set up the config clock */ + + writel(CLOCK_DIV_RATIO,CONFIG_CONTROL_CLOCK(PLD_BASE)); + while(readl(CONFIG_CONTROL_CLOCK(PLD_BASE)) + !=CLOCK_DIV_RATIO); + /* Tell the device we wish to configure it */ + tmp = readl(CONFIG_CONTROL(PLD_BASE)); + tmp |= CONFIG_CONTROL_CO_MSK; + writel(tmp,CONFIG_CONTROL(PLD_BASE)); + + + /* + * Wait for the busy bit to clear then check for errors. + */ + + while((tmp=readl(CONFIG_CONTROL(PLD_BASE))&CONFIG_CONTROL_B_MSK )); + + if ( tmp & CONFIG_CONTROL_E_MSK ){ + if ( tmp & CONFIG_CONTROL_ES_0_MSK ){ + /* Already being programmed via JTAG */ + printk(KERN_WARNING "pld0:JTAG configuration alreay in progress\n"); + return -EBUSY; + + } + if ( tmp & CONFIG_CONTROL_ES_1_MSK ){ + /* No config clock configured */ + printk(KERN_ERR "pld0:No config clock!\n"); + BUG(); + return -EBUSY; + } + if ( tmp & CONFIG_CONTROL_ES_2_MSK ){ + /* Already being programmed via External device */ + printk(KERN_WARNING "pld0:JTAG configuration alreay in progress\n"); + return -EBUSY; + } + } + + return 0; +} + + +static int write_pld_data_word(unsigned int config_data) +{ + unsigned int tmp; + + do{ + tmp = readl(CONFIG_CONTROL(PLD_BASE)); + } + while ( ( tmp & CONFIG_CONTROL_B_MSK ) && + !( tmp & CONFIG_CONTROL_E_MSK )); + + if ( tmp & CONFIG_CONTROL_E_MSK ){ + printk("pld0: Error writing pld data, CONFIG_CONTROL=%#x\n",tmp); + + return -EILSEQ; + } + + writel(config_data,CONFIG_CONTROL_DATA(PLD_BASE)); + return 0; + +} + + +static int wait_for_device_to_configure (void) +{ + int i=0x10000; + + while(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_B_MSK); + + /* + * Wait for the config bit (CO) to go low, indicating that everything + * is Ok. If it doesn't, assume that is screwed up somehow and + * clear the CO bit to abort the configuration. + */ + + while(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_B_MSK); + + while (i&&(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_CO_MSK)){ + i--; + } + + if (!i){ + writel(0,CONFIG_CONTROL(PLD_BASE)); + printk(KERN_WARNING "pld0: Invalid PLD config data\n"); + return -EILSEQ; + } + + return 0; +} + + + +static int pld_open(struct inode* inode, struct file *filep) +{ + + struct pld_sbihdr* sbihdr; + + /* Check the device minor number */ + if(minor(inode->i_rdev)){ + DEBUG(printk("pld0: minor=%d",minor(inode->i_rdev));) + return -ENODEV; + } + + /* Create our private data and link it to the file structure */ + sbihdr=kmalloc(sizeof(struct pld_sbihdr),GFP_KERNEL); + + if(!sbihdr) + return -ENOMEM; + + filep->private_data=sbihdr; + + sbihdr->fpos=0; + sbihdr->temp=0; + return 0; +} + +static int pld_release(struct inode* inode, struct file *filep){ + int ret_code; + + kfree(filep->private_data); + ret_code=wait_for_device_to_configure(); + lock_pld(); + return ret_code; +} + +static ssize_t pld_write(struct file* filep, const char* data, size_t count, loff_t* ppos){ + + struct pld_sbihdr* sbihdr=filep->private_data; + int bytes_left=count; + int result; + DEBUG(int i=0); + + + /* Can't seek (pwrite) on pld. */ + if (ppos != &filep->f_pos) + return -ESPIPE; + + + /* Check access to the whole are in one go */ + if(!access_ok(VERIFY_READ,(const void*)data, count)){ + return -EFAULT; + } + + /* + * We now lock against writes. + */ + if (down_interruptible(&pld_sem)) + return -ERESTARTSYS; + + if(!sbihdr->fpos){ + /* + * unlock the pld and place in configure mode + */ + unlock_excalibur_pld(); + result=place_pld_into_configure_mode(); + if(result) + return result; + } + DEBUG(printk("data= %#x count=%#x 0ffset=%#x\n",*data, count, *ppos)); + + while(bytes_left){ + char tmp; + __get_user(tmp,data); + /* read our header ! */ + sbihdr->temp|=tmp << (8*(sbihdr->fpos&3)); + if((sbihdr->fpos&3)==3){ + if(write_pld_data_word(sbihdr->temp)) + { + DEBUG(printk("pos=%d\n",sbihdr->fpos);) + return -EILSEQ; + } + DEBUG(if(i<10){) + DEBUG(printk("fpos2 :%#x data=%#x\n",sbihdr->fpos,sbihdr->temp)); + DEBUG(i++); + DEBUG(}); + sbihdr->temp=0; + DEBUG(words_written++); + } + sbihdr->fpos++; + data++; + bytes_left--; + } + + up(&pld_sem); + return (count); +} + +int pld_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) +{ + + switch(cmd){ + +#ifdef CONFIG_PLD_HOTSWAP + case PLD_IOC_ADD_PLD_DEV: + { + struct pldhs_dev_desc desc; + struct pldhs_dev_info info; + char *name; + void *data; + int result=0; + + result=copy_from_user(&desc,(const void*)arg,sizeof(struct pldhs_dev_desc)); + if(result) + return -EFAULT; + result=copy_from_user(&info,(const void*)desc.info,sizeof(struct pldhs_dev_info)); + if(result) + return -EFAULT; + name=kmalloc(info.nsize,GFP_KERNEL); + if(!name) + return -ENOMEM; + + result=copy_from_user(name,(const void*)desc.name,info.nsize); + if(result){ + result=-EFAULT; + goto ioctl_out; + } + + data=kmalloc(info.pssize,GFP_KERNEL); + if(!data){ + result=-ENOMEM; + goto ioctl_out; + } + + result=copy_from_user(data,(const void*)desc.data,info.pssize); + if(result){ + result=-EFAULT; + goto ioctl_out1; + } + result=pldhs_add_device(&info,name,data); + + ioctl_out1: + kfree(data); + ioctl_out: + kfree(name); + return result; + + } + + case PLD_IOC_REMOVE_PLD_DEVS: + pldhs_remove_devices(); + return 0; + + case PLD_IOC_SET_INT_MODE: + if(cmd==3){ + printk(KERN_INFO "Interrupt mode set to 3 (Six individual interrupts)\n"); + return 0; + }else{ + printk(KERN_INFO "There is no interrupt handler available for this mode (%d). You will need to add one\n to implement whatever scheme you require\n"); + return -EACCES; + } +#endif + default: + return -ENOTTY; + } +} + + +static struct file_operations pld_fops={ + write: pld_write, + ioctl: pld_ioctl, + open: pld_open, + release: pld_release +}; + +static int __init pld_init(void){ + int major; + major=register_chrdev(0,"pld",&pld_fops); + printk(KERN_INFO "Using PLD major num=%d\n",major); + if (major<0){ + return major; + } + return 0; +} + +__initcall(pld_init); --- /dev/null +++ linux-2.4.27/drivers/pld/pld_hotswap.c @@ -0,0 +1,188 @@ +/* + * linux/drivers/pld/pld_hotswap.c + * + * Pld driver for Altera EPXA Excalibur devices + * + * + * Copyright 2001 Altera Corporation (cdavies@altera.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: $ + * + */ + +/* + * pld_hotswap ops contains the basic operation required for adding + * and removing devices from the system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct pld_hotswap_ops pldhs_driver_list={ + list: LIST_HEAD_INIT(pldhs_driver_list.list), + name: "", +}; + +static spinlock_t list_lock=SPIN_LOCK_UNLOCKED; + + + +static struct pld_hotswap_ops * pldhs_find_driver(char* name) +{ + struct pld_hotswap_ops * ptr; + struct list_head *list_pos; + + spin_lock(&list_lock); + list_for_each(list_pos,&pldhs_driver_list.list){ + ptr=(struct pld_hotswap_ops *)list_pos; + if(!strcmp(name, ptr->name)){ + spin_unlock(&list_lock); + return ptr; + + } + } + spin_unlock(&list_lock); + return 0; +} + + + +int pldhs_register_driver(struct pld_hotswap_ops *drv) +{ + + /* Check that the device is not already on the list + * if so, do nothing */ + if(pldhs_find_driver(drv->name)){ + return 0; + } + + printk(KERN_INFO "PLD: Registering hotswap driver %s\n",drv->name); + /* Add this at the start of the list */ + spin_lock(&list_lock); + list_add((struct list_head*)drv,&pldhs_driver_list.list); + spin_unlock(&list_lock); + + return 0; +} + +int pldhs_unregister_driver(char *name) +{ + struct pld_hotswap_ops *ptr; + + ptr=pldhs_find_driver(name); + if(!ptr){ + return -ENODEV; + } + + printk(KERN_INFO "PLD: Unregistering hotswap driver%s\n",name); + spin_lock(&list_lock); + list_del((struct list_head*)ptr); + spin_unlock(&list_lock); + + return 0; +} + +int pldhs_add_device(struct pldhs_dev_info* dev_info,char *drv_name, void* dev_ps_data) +{ + struct pld_hotswap_ops * ptr; + + ptr=pldhs_find_driver(drv_name); + + if(!ptr){ + /* try requesting this module*/ + request_module(drv_name); + /* is the driver there now? */ + ptr=pldhs_find_driver(drv_name); + if(!ptr){ + printk("pld hotswap: Failed to load a driver for %s\n",drv_name); + return -ENODEV; + } + } + + if(!ptr->add_device){ + printk(KERN_WARNING "pldhs: no add_device() function for driver %s\n",drv_name); + return 0; + } + + return ptr->add_device(dev_info,dev_ps_data); +} + +int pldhs_remove_devices(void) +{ + struct list_head *list_pos; + struct pld_hotswap_ops * ptr; + + + spin_lock(&list_lock); + list_for_each(list_pos,&pldhs_driver_list.list){ + ptr=(struct pld_hotswap_ops *)list_pos; + if(ptr->remove_devices) + ptr->remove_devices(); + + } + spin_unlock(&list_lock); + + return 0; +} + +#ifdef CONFIG_PROC_FS +int pldhs_read_proc(char* buf,char** start,off_t offset,int count,int *eof,void *data){ + + + struct list_head *list_pos; + struct pld_hotswap_ops * ptr; + int i,len=0; + + *start=buf; + spin_lock(&list_lock); + list_for_each(list_pos,&pldhs_driver_list.list){ + ptr=(struct pld_hotswap_ops *)list_pos; + if(ptr->proc_read){ + i=ptr->proc_read(buf,start,offset,count,eof,data); + count-=i; + len+=i; + *start+=i; + } + } + spin_unlock(&list_lock); + + *start=NULL; + *eof=1; + return len; +} + +void __init pldhs_init(void){ + create_proc_read_entry("pld",0,NULL,pldhs_read_proc,NULL); +} + +__initcall(pldhs_init); +#endif + +EXPORT_SYMBOL(pldhs_register_driver); +EXPORT_SYMBOL(pldhs_unregister_driver); --- linux-2.4.27/drivers/scsi/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/Makefile @@ -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 --- linux-2.4.27/drivers/scsi/scsi.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/scsi.c @@ -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 --- linux-2.4.27/drivers/scsi/scsi.h~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/scsi.h @@ -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 */ --- linux-2.4.27/drivers/scsi/scsi_dma.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/scsi_dma.c @@ -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 --- linux-2.4.27/drivers/scsi/scsi_error.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/scsi_error.c @@ -35,6 +35,8 @@ #include "hosts.h" #include "constants.h" +#include /* grr */ + /* * We must always allow SHUTDOWN_SIGS. Even if we are not a module, * the host drivers that we are using may be loaded as modules, and @@ -49,6 +51,13 @@ */ #define SHUTDOWN_SIGS (sigmask(SIGHUP)) +/* + * The number of times we retry a REQUEST SENSE and TEST UNIT READY + * respectively. This is arbitary. + */ +#define SENSE_RETRIES 5 +#define TUR_RETRIES 5 + #ifdef DEBUG #define SENSE_TIMEOUT SCSI_TIMEOUT #define ABORT_TIMEOUT SCSI_TIMEOUT @@ -373,16 +382,12 @@ */ STATIC int scsi_eh_retry_command(Scsi_Cmnd * SCpnt) { - memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, - sizeof(SCpnt->data_cmnd)); - SCpnt->request_buffer = SCpnt->buffer; - SCpnt->request_bufflen = SCpnt->bufflen; - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->cmd_len = SCpnt->old_cmd_len; - SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; - SCpnt->underflow = SCpnt->old_underflow; + int tries = 0; - scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); + do { + scsi_setup_cmd_retry(SCpnt); + scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); + } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < SCpnt->allowed); /* * Hey, we are done. Let's look to see what happened. @@ -409,16 +414,10 @@ static unsigned char generic_sense[6] = {REQUEST_SENSE, 0, 0, 0, 255, 0}; unsigned char scsi_result0[256], *scsi_result = NULL; - int saved_result; + int saved_result, tries; ASSERT_LOCK(&io_request_lock, 0); - memcpy((void *) SCpnt->cmnd, (void *) generic_sense, - sizeof(generic_sense)); - - if (SCpnt->device->scsi_level <= SCSI_2) - SCpnt->cmnd[1] = SCpnt->lun << 5; - scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma) ? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA); @@ -426,24 +425,41 @@ printk("cannot allocate scsi_result in scsi_request_sense.\n"); return FAILED; } - /* - * Zero the sense buffer. Some host adapters automatically always request - * sense, so it is not a good idea that SCpnt->request_buffer and - * SCpnt->sense_buffer point to the same address (DB). - * 0 is not a valid sense code. - */ - memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); - memset((void *) scsi_result, 0, 256); saved_result = SCpnt->result; - SCpnt->request_buffer = scsi_result; - SCpnt->request_bufflen = 256; - SCpnt->use_sg = 0; - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - SCpnt->sc_data_direction = SCSI_DATA_READ; - SCpnt->underflow = 0; - scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + tries = 0; + do { + memcpy((void *) SCpnt->cmnd, (void *) generic_sense, + sizeof(generic_sense)); + + if (SCpnt->device->scsi_level <= SCSI_2) + SCpnt->cmnd[1] = SCpnt->lun << 5; + + /* + * Zero the sense buffer. Some host adapters automatically + * always request sense, so it is not a good idea that + * SCpnt->request_buffer and SCpnt->sense_buffer point to + * the same address (DB). 0 is not a valid sense code. + */ + memset((void *) SCpnt->sense_buffer, 0, + sizeof(SCpnt->sense_buffer)); + memset((void *) scsi_result, 0, 256); + + SCpnt->request_buffer = scsi_result; + SCpnt->request_bufflen = 256; + SCpnt->use_sg = 0; + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->sc_data_direction = SCSI_DATA_READ; + SCpnt->underflow = 0; + + scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + /* + * If the SCSI device responded with "logical unit + * is in process of becoming ready", we need to + * retry this command. + */ + } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < SENSE_RETRIES); /* Last chance to have valid sense data */ if (!scsi_sense_valid(SCpnt)) @@ -458,15 +474,8 @@ * When we eventually call scsi_finish, we really wish to complete * the original request, so let's restore the original data. (DB) */ - memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, - sizeof(SCpnt->data_cmnd)); SCpnt->result = saved_result; - SCpnt->request_buffer = SCpnt->buffer; - SCpnt->request_bufflen = SCpnt->bufflen; - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->cmd_len = SCpnt->old_cmd_len; - SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; - SCpnt->underflow = SCpnt->old_underflow; + scsi_setup_cmd_retry(SCpnt); /* * Hey, we are done. Let's look to see what happened. @@ -484,40 +493,42 @@ { static unsigned char tur_command[6] = {TEST_UNIT_READY, 0, 0, 0, 0, 0}; + int tries = 0; - memcpy((void *) SCpnt->cmnd, (void *) tur_command, - sizeof(tur_command)); + do { + memcpy((void *) SCpnt->cmnd, (void *) tur_command, + sizeof(tur_command)); - if (SCpnt->device->scsi_level <= SCSI_2) - SCpnt->cmnd[1] = SCpnt->lun << 5; + if (SCpnt->device->scsi_level <= SCSI_2) + SCpnt->cmnd[1] = SCpnt->lun << 5; - /* - * Zero the sense buffer. The SCSI spec mandates that any - * untransferred sense data should be interpreted as being zero. - */ - memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); + /* + * Zero the sense buffer. The SCSI spec mandates that any + * untransferred sense data should be interpreted as being zero. + */ + memset((void *) SCpnt->sense_buffer, 0, + sizeof(SCpnt->sense_buffer)); - SCpnt->request_buffer = NULL; - SCpnt->request_bufflen = 0; - SCpnt->use_sg = 0; - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - SCpnt->underflow = 0; - SCpnt->sc_data_direction = SCSI_DATA_NONE; + SCpnt->request_buffer = NULL; + SCpnt->request_bufflen = 0; + SCpnt->use_sg = 0; + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->underflow = 0; + SCpnt->sc_data_direction = SCSI_DATA_NONE; - scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + /* + * If the SCSI device responded with "logical unit + * is in process of becoming ready", we need to + * retry this command. + */ + } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < TUR_RETRIES); /* * When we eventually call scsi_finish, we really wish to complete * the original request, so let's restore the original data. (DB) */ - memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, - sizeof(SCpnt->data_cmnd)); - SCpnt->request_buffer = SCpnt->buffer; - SCpnt->request_bufflen = SCpnt->bufflen; - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->cmd_len = SCpnt->old_cmd_len; - SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; - SCpnt->underflow = SCpnt->old_underflow; + scsi_setup_cmd_retry(SCpnt); /* * Hey, we are done. Let's look to see what happened. @@ -577,7 +588,6 @@ host = SCpnt->host; - retry: /* * We will use a queued command if possible, otherwise we will emulate the * queuing and calling of completion function ourselves. @@ -660,14 +670,13 @@ SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_send_eh_cmnd: scsi_eh_completed_normally %x\n", ret)); switch (ret) { - case SUCCESS: - SCpnt->eh_state = SUCCESS; - break; - case NEEDS_RETRY: - goto retry; - case FAILED: default: - SCpnt->eh_state = FAILED; + ret = FAILED; + /*FALLTHROUGH*/ + case FAILED: + case NEEDS_RETRY: + case SUCCESS: + SCpnt->eh_state = ret; break; } } else { @@ -1208,6 +1217,82 @@ /* + * Function: scsi_eh_lock_done + * + * Purpose: free the command and request structures associated + * with the error handlers door lock request + * + * Arguments: SCpnt - the SCSI command block for the door lock request. + * + * Returns: Nothing + * + * Notes: We completed the asynchronous door lock request, and + * it has either locked the door or failed. We must free + * the command structures associated with this request. + */ +static void scsi_eh_lock_done(struct scsi_cmnd *SCpnt) +{ + struct scsi_request *SRpnt = SCpnt->sc_request; + + SCpnt->sc_request = NULL; + SRpnt->sr_command = NULL; + + scsi_release_command(SCpnt); + scsi_release_request(SRpnt); +} + + +/* + * Function: scsi_eh_lock_door + * + * Purpose: Prevent medium removal for the specified device + * + * Arguments: dev - SCSI device to prevent medium removal + * + * Locking: We must be called from process context; + * scsi_allocate_request() may sleep. + * + * Returns: Nothing + * + * Notes: We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request + * on the head of the devices request queue, and continue. + * + * Bugs: scsi_allocate_request() may sleep waiting for existing + * requests to be processed. However, since we haven't + * kicked off any request processing for this host, this + * may deadlock. + * + * If scsi_allocate_request() fails for what ever reason, + * we completely forget to lock the door. + */ +STATIC void scsi_eh_lock_door(struct scsi_device *dev) +{ + struct scsi_request *SRpnt = scsi_allocate_request(dev); + + if (SRpnt == NULL) { + /* what now? */ + return; + } + + SRpnt->sr_cmnd[0] = ALLOW_MEDIUM_REMOVAL; + SRpnt->sr_cmnd[1] = (dev->scsi_level <= SCSI_2) ? (dev->lun << 5) : 0; + SRpnt->sr_cmnd[2] = 0; + SRpnt->sr_cmnd[3] = 0; + SRpnt->sr_cmnd[4] = SCSI_REMOVAL_PREVENT; + SRpnt->sr_cmnd[5] = 0; + SRpnt->sr_data_direction = SCSI_DATA_NONE; + SRpnt->sr_bufflen = 0; + SRpnt->sr_buffer = NULL; + SRpnt->sr_allowed = 5; + SRpnt->sr_done = scsi_eh_lock_done; + SRpnt->sr_timeout_per_command = 10 * HZ; + SRpnt->sr_cmd_len = COMMAND_SIZE(SRpnt->sr_cmnd[0]); + + scsi_insert_special_req(SRpnt, 1); +} + + +/* * Function: scsi_restart_operations * * Purpose: Restart IO operations to the specified host. @@ -1229,6 +1314,15 @@ ASSERT_LOCK(&io_request_lock, 0); /* + * If the door was locked, we need to insert a door lock request + * onto the head of the SCSI request queue for the device. There + * is no point trying to lock the door of an off-line device. + */ + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) + if (SDpnt->online && SDpnt->locked) + scsi_eh_lock_door(SDpnt); + + /* * Next free up anything directly waiting upon the host. This will be * requests for character device operations, and also for ioctls to queued * block devices. @@ -1248,8 +1342,7 @@ request_queue_t *q; if ((host->can_queue > 0 && (host->host_busy >= host->can_queue)) || (host->host_blocked) - || (host->host_self_blocked) - || (SDpnt->device_blocked)) { + || (host->host_self_blocked)) { break; } q = &SDpnt->request_queue; @@ -1259,136 +1352,202 @@ } /* - * Function: scsi_unjam_host + * Function: scsi_eh_find_failed_command * - * Purpose: Attempt to fix a host which has a command that failed for - * some reason. + * Purpose: Find a failed Scsi_Cmnd structure on a device. * - * Arguments: host - host that needs unjamming. - * - * Returns: Nothing + * Arguments: SDpnt - Scsi_Device structure * - * Notes: When we come in here, we *know* that all commands on the - * bus have either completed, failed or timed out. We also - * know that no further commands are being sent to the host, - * so things are relatively quiet and we have freedom to - * fiddle with things as we wish. + * Returns: Pointer to Scsi_Cmnd structure, or NULL on failure + */ +STATIC Scsi_Cmnd *scsi_eh_find_failed_command(Scsi_Device *SDpnt) +{ + Scsi_Cmnd *SCpnt; + + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) + if (SCpnt->state == SCSI_STATE_FAILED || + SCpnt->state == SCSI_STATE_TIMEOUT) + return SCpnt; + + return NULL; +} + + +/* + * Function: scsi_eh_test_and_retry * - * Additional note: This is only the *default* implementation. It is possible - * for individual drivers to supply their own version of this - * function, and if the maintainer wishes to do this, it is - * strongly suggested that this function be taken as a template - * and modified. This function was designed to correctly handle - * problems for about 95% of the different cases out there, and - * it should always provide at least a reasonable amount of error - * recovery. + * Purpose: Try to retry a failed command. * - * Note3: Any command marked 'FAILED' or 'TIMEOUT' must eventually - * have scsi_finish_command() called for it. We do all of - * the retry stuff here, so when we restart the host after we - * return it should have an empty queue. + * Arguments: SCpnt - scsi command structure + * done - list of commands that have been successfully + * completed. + * + * Returns: SUCCESS or FAILED + * + * Note: If the TEST UNIT READY command successfully executes, + * but returns some form of "device not ready", we wait + * a while, and retry 3 times. The device could be still + * re-initialising. */ -STATIC int scsi_unjam_host(struct Scsi_Host *host) +STATIC int scsi_eh_test_and_retry(Scsi_Cmnd *SCpnt, Scsi_Cmnd **done) { - int devices_failed; - int numfailed; - int ourrtn; - int rtn = FALSE; - int result; - Scsi_Cmnd *SCloop; - Scsi_Cmnd *SCpnt; - Scsi_Device *SDpnt; - Scsi_Device *SDloop; - Scsi_Cmnd *SCdone; - int timed_out; + int rtn, tries = 3; - ASSERT_LOCK(&io_request_lock, 0); + do { + rtn = scsi_test_unit_ready(SCpnt); + if (rtn != SUCCESS) + return rtn; - SCdone = NULL; + if (scsi_unit_is_ready(SCpnt)) + break; + + if (tries-- == 0) + return FAILED; + + scsi_sleep(5 * HZ); + } while (1); + + rtn = scsi_eh_retry_command(SCpnt); + if (rtn == SUCCESS) { + SCpnt->host->host_failed--; + scsi_eh_finish_command(done, SCpnt); + } + + return rtn; +} - /* - * First, protect against any sort of race condition. If any of the outstanding - * commands are in states that indicate that we are not yet blocked (i.e. we are - * not in a quiet state) then we got woken up in error. If we ever end up here, - * we need to re-examine some of the assumptions. - */ - for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { - for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { - if (SCpnt->state == SCSI_STATE_FAILED - || SCpnt->state == SCSI_STATE_TIMEOUT - || SCpnt->state == SCSI_STATE_INITIALIZING - || SCpnt->state == SCSI_STATE_UNUSED) { - continue; - } - /* - * Rats. Something is still floating around out there. This could - * be the result of the fact that the upper level drivers are still frobbing - * commands that might have succeeded. There are two outcomes. One is that - * the command block will eventually be freed, and the other one is that - * the command will be queued and will be finished along the way. - */ - SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target)); /* - * panic("SCSI Error handler woken too early\n"); + * Function: scsi_eh_restart_device * - * This is no longer a problem, since now the code cares only about - * SCSI_STATE_TIMEOUT and SCSI_STATE_FAILED. - * Other states are useful only to release active commands when devices are - * set offline. If (host->host_active == host->host_busy) we can safely assume - * that there are no commands in state other then TIMEOUT od FAILED. (DB) + * Purpose: Retry all failed or timed out commands for a device * - * FIXME: - * It is not easy to release correctly commands according to their state when - * devices are set offline, when the state is neither TIMEOUT nor FAILED. - * When a device is set offline, we can have some command with - * rq_status=RQ_SCSY_BUSY, owner=SCSI_STATE_HIGHLEVEL, - * state=SCSI_STATE_INITIALIZING and the driver module cannot be released. - * (DB, 17 May 1998) + * Arguments: SDpnt - SCSI device to retry + * done - list of commands that have been successfully + * completed. + * + * Returns: SUCCESS or failure code */ +STATIC int scsi_eh_restart_device(Scsi_Device *SDpnt, Scsi_Cmnd **done) +{ + Scsi_Cmnd *SCpnt, *SCnext; + int rtn = SUCCESS; + + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) { + SCnext = SCpnt->next; + + if (SCpnt->state == SCSI_STATE_FAILED || + SCpnt->state == SCSI_STATE_TIMEOUT) { + rtn = scsi_eh_test_and_retry(SCpnt, done); + if (rtn != SUCCESS) + break; + } + } + + return rtn; +} + +/* + * Function: scsi_eh_set_device_offline + * + * Purpose: set a device off line + * + * Arguments: SDpnt - SCSI device to take off line + * done - list of commands that have been successfully + * completed. + * reason - text string describing why the device is off-line + * + * Returns: Nothing + * + * Notes: In addition, we complete each failed or timed out command + * attached to this device. + */ +STATIC void scsi_eh_set_device_offline(Scsi_Device *SDpnt, Scsi_Cmnd **done, + const char *reason) +{ + Scsi_Cmnd *SCpnt, *SCnext; + + printk(KERN_ERR "scsi: device set offline - %s: " + "host %d channel %d id %d lun %d\n", + reason, SDpnt->host->host_no, SDpnt->channel, + SDpnt->id, SDpnt->lun); + + SDpnt->online = FALSE; + + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) { + SCnext = SCpnt->next; + + switch (SCpnt->state) { + case SCSI_STATE_TIMEOUT: + SCpnt->result |= DRIVER_TIMEOUT; + /*FALLTHROUGH*/ + + case SCSI_STATE_FAILED: + SCSI_LOG_ERROR_RECOVERY(3, + printk("Finishing command for device %d %x\n", + SDpnt->id, SCpnt->result)); + + SDpnt->host->host_failed--; + scsi_eh_finish_command(done, SCpnt); + break; + + default: + break; } } +} + +static void scsi_unjam_request_sense(struct Scsi_Host *host, Scsi_Cmnd **done) +{ + int rtn; + int result; + Scsi_Cmnd *SCpnt; + Scsi_Device *SDpnt; /* * Next, see if we need to request sense information. if so, * then get it now, so we have a better idea of what to do. - * FIXME(eric) this has the unfortunate side effect that if a host - * adapter does not automatically request sense information, that we end - * up shutting it down before we request it. All hosts should be doing this - * anyways, so for now all I have to say is tough noogies if you end up in here. - * On second thought, this is probably a good idea. We *really* want to give - * authors an incentive to automatically request this. + * FIXME(eric) this has the unfortunate side effect that if a + * host adapter does not automatically request sense information, + * that we end up shutting it down before we request it. All + * hosts should be doing this anyways, so for now all I have + * to say is tough noogies if you end up in here. On second + * thought, this is probably a good idea. We *really* want + * to give authors an incentive to automatically request this. */ - SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we need to request sense\n")); + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_unjam_host: Checking to see if we need to request sense\n")); for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { - if (SCpnt->state != SCSI_STATE_FAILED || scsi_sense_valid(SCpnt)) { + if (SCpnt->state != SCSI_STATE_FAILED || scsi_sense_valid(SCpnt)) continue; - } - SCSI_LOG_ERROR_RECOVERY(2, printk("scsi_unjam_host: Requesting sense for %d\n", - SCpnt->target)); + + SCSI_LOG_ERROR_RECOVERY(2, + printk("scsi_unjam_host: Requesting sense for %d\n", + SCpnt->target)); rtn = scsi_request_sense(SCpnt); - if (rtn != SUCCESS) { + if (rtn != SUCCESS) continue; - } - SCSI_LOG_ERROR_RECOVERY(3, printk("Sense requested for %p - result %x\n", - SCpnt, SCpnt->result)); + + SCSI_LOG_ERROR_RECOVERY(3, + printk("Sense requested for %p - result %x\n", + SCpnt, SCpnt->result)); SCSI_LOG_ERROR_RECOVERY(3, print_sense("bh", SCpnt)); result = scsi_decide_disposition(SCpnt); /* - * If the result was normal, then just pass it along to the - * upper level. + * If the result was normal, then just pass + * it along to the upper level. */ if (result == SUCCESS) { SCpnt->host->host_failed--; - scsi_eh_finish_command(&SCdone, SCpnt); + scsi_eh_finish_command(done, SCpnt); } - if (result != NEEDS_RETRY) { + if (result != NEEDS_RETRY) continue; - } + /* * We only come in here if we want to retry a * command. The test to see whether the command @@ -1398,20 +1557,29 @@ */ SCpnt->state = NEEDS_RETRY; rtn = scsi_eh_retry_command(SCpnt); - if (rtn != SUCCESS) { + if (rtn != SUCCESS) continue; - } + /* * We eventually hand this one back to the top level. */ SCpnt->host->host_failed--; - scsi_eh_finish_command(&SCdone, SCpnt); + scsi_eh_finish_command(done, SCpnt); } } +} + +static void scsi_unjam_count(struct Scsi_Host *host, Scsi_Cmnd **done) +{ + Scsi_Device *SDpnt; + Scsi_Cmnd *SCpnt; + int devices_failed; + int numfailed; + int timed_out; /* - * Go through the list of commands and figure out where we stand and how bad things - * really are. + * Go through the list of commands and figure out where we + * stand and how bad things really are. */ numfailed = 0; timed_out = 0; @@ -1421,359 +1589,478 @@ for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { if (SCpnt->state == SCSI_STATE_FAILED) { - SCSI_LOG_ERROR_RECOVERY(5, printk("Command to ID %d failed\n", - SCpnt->target)); + SCSI_LOG_ERROR_RECOVERY(5, + printk("Command to ID %d failed\n", + SCpnt->target)); numfailed++; device_error++; } if (SCpnt->state == SCSI_STATE_TIMEOUT) { - SCSI_LOG_ERROR_RECOVERY(5, printk("Command to ID %d timedout\n", - SCpnt->target)); + SCSI_LOG_ERROR_RECOVERY(5, + printk("Command to ID %d timedout\n", + SCpnt->target)); timed_out++; device_error++; } } - if (device_error > 0) { + if (device_error > 0) devices_failed++; - } } - SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d+%d commands on %d devices require eh work\n", - numfailed, timed_out, devices_failed)); + SCSI_LOG_ERROR_RECOVERY(2, + printk("Total of %d+%d commands on %d devices require eh work\n", + numfailed, timed_out, devices_failed)); +} + +static void scsi_unjam_abort(struct Scsi_Host *host, Scsi_Cmnd **done) +{ + Scsi_Device *SDpnt; + Scsi_Cmnd *SCpnt; + int rtn; - if (host->host_failed == 0) { - ourrtn = TRUE; - goto leave; - } /* - * Next, try and see whether or not it makes sense to try and abort - * the running command. This only works out to be the case if we have - * one command that has timed out. If the command simply failed, it - * makes no sense to try and abort the command, since as far as the - * host adapter is concerned, it isn't running. + * Next, try and see whether or not it makes sense to try and + * abort the running command. This only works out to be the + * case if we have one command that has timed out. If the + * command simply failed, it makes no sense to try and abort + * the command, since as far as the host adapter is concerned, + * it isn't running. */ - SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we want to try abort\n")); + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_unjam_host: Checking to see if we want to try abort\n")); for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { - for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) { - if (SCloop->state != SCSI_STATE_TIMEOUT) { + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { + if (SCpnt->state != SCSI_STATE_TIMEOUT) continue; - } - rtn = scsi_try_to_abort_command(SCloop, ABORT_TIMEOUT); - if (rtn == SUCCESS) { - rtn = scsi_test_unit_ready(SCloop); - if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) { - rtn = scsi_eh_retry_command(SCloop); - - if (rtn == SUCCESS) { - SCloop->host->host_failed--; - scsi_eh_finish_command(&SCdone, SCloop); - } - } - } + rtn = scsi_try_to_abort_command(SCpnt, ABORT_TIMEOUT); + if (rtn == SUCCESS) + scsi_eh_test_and_retry(SCpnt, done); } } +} + +static void scsi_unjam_device_reset(struct Scsi_Host *host, Scsi_Cmnd **done) +{ + Scsi_Device *SDpnt; + Scsi_Cmnd *SCpnt; + int rtn; - /* - * If we have corrected all of the problems, then we are done. - */ - if (host->host_failed == 0) { - ourrtn = TRUE; - goto leave; - } /* * Either the abort wasn't appropriate, or it didn't succeed. - * Now try a bus device reset. Still, look to see whether we have - * multiple devices that are jammed or not - if we have multiple devices, - * it makes no sense to try BUS_DEVICE_RESET - we really would need - * to try a BUS_RESET instead. + * Now try a bus device reset. Still, look to see whether we + * have multiple devices that are jammed or not - if we have + * multiple devices, it makes no sense to try BUS_DEVICE_RESET + * - we really would need to try a BUS_RESET instead. * - * Does this make sense - should we try BDR on each device individually? - * Yes, definitely. + * Does this make sense - should we try BDR on each device + * individually? Yes, definitely. */ - SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we want to try BDR\n")); + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_unjam_host: Checking to see if we want to try BDR\n")); for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { - for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) { - if (SCloop->state == SCSI_STATE_FAILED - || SCloop->state == SCSI_STATE_TIMEOUT) { - break; - } - } - - if (SCloop == NULL) { + SCpnt = scsi_eh_find_failed_command(SDpnt); + if (SCpnt == NULL) continue; - } + /* - * OK, we have a device that is having problems. Try and send - * a bus device reset to it. - * - * FIXME(eric) - make sure we handle the case where multiple - * commands to the same device have failed. They all must - * get properly restarted. + * OK, we have a device that is having problems. + * Try and send a bus device reset to it. */ - rtn = scsi_try_bus_device_reset(SCloop, RESET_TIMEOUT); + rtn = scsi_try_bus_device_reset(SCpnt, RESET_TIMEOUT); - if (rtn == SUCCESS) { - rtn = scsi_test_unit_ready(SCloop); + /* + * 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. + * + * If any command fails, bail out. We will try a + * bus reset instead. + */ + if (rtn == SUCCESS) + scsi_eh_restart_device(SDpnt, done); + } +} - if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) { - rtn = scsi_eh_retry_command(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) { - SCloop->host->host_failed--; - scsi_eh_finish_command(&SCdone, 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 (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. + * Find the maximum channel number for this host. */ - SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard bus reset\n")); + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) + if (SDpnt->channel > max_channel) + max_channel = SDpnt->channel; - /* - * 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 channel, and see if it any device on + * each channel has failed. */ - for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { - next_device: - for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { - if (SCpnt->state != SCSI_STATE_FAILED - && SCpnt->state != SCSI_STATE_TIMEOUT) { + for (channel = 0; channel <= max_channel; channel++) { + Scsi_Cmnd *failed_command; + int soft_reset; + + try_again: + failed_command = NULL; + soft_reset = 0; + + /* + * Loop over each device on this channel locating any + * failed command. We need a Scsi_Cmnd structure to + * call the bus reset function. + * + * We also need to check if any of the failed commands + * are on soft_reset devices, and if so, skip the reset. + */ + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { + if (SDpnt->channel != channel) continue; - } - /* - * We have a failed command. Make sure there are no other failed - * commands on the same channel that are timed out and implement a - * soft reset. - */ - for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) { - for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) { - if (SCloop->channel != SCpnt->channel) { - continue; - } - if (SCloop->state != SCSI_STATE_FAILED - && SCloop->state != SCSI_STATE_TIMEOUT) { - continue; - } - if (SDloop->soft_reset && SCloop->state == SCSI_STATE_TIMEOUT) { - /* - * If this device uses the soft reset option, and this - * is one of the devices acting up, then our only - * option is to wait a bit, since the command is - * supposedly still running. - * - * FIXME(eric) - right now we will just end up falling - * through to the 'take device offline' case. - * - * FIXME(eric) - It is possible that the command completed - * *after* the error recovery procedure started, and if this - * is the case, we are worrying about nothing here. - */ - scsi_sleep(1 * HZ); - goto next_device; - } - } - } + SCpnt = scsi_eh_find_failed_command(SDpnt); + if (SCpnt) + failed_command = SCpnt; /* - * We now know that we are able to perform a reset for the - * bus that SCpnt points to. There are no soft-reset devices - * with outstanding timed out commands. + * If this device has timed out or failed commands, + * and uses the soft_reset option. */ - rtn = scsi_try_bus_reset(SCpnt); - if (rtn == SUCCESS) { - for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) { - for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) { - if (SCloop->channel != SCpnt->channel) { - continue; - } - if (SCloop->state != SCSI_STATE_FAILED - && SCloop->state != SCSI_STATE_TIMEOUT) { - continue; - } - rtn = scsi_test_unit_ready(SCloop); + if (SCpnt && SDpnt->soft_reset) + soft_reset = 1; + } - if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) { - rtn = scsi_eh_retry_command(SCloop); + /* + * If this channel hasn't failed, we + * don't need to reset it. + */ + if (!failed_command) + continue; - if (rtn == SUCCESS) { - SCpnt->host->host_failed--; - scsi_eh_finish_command(&SCdone, SCloop); - } - } - /* - * If the bus reset worked, but we are still unable to - * talk to the device, take it offline. - * FIXME(eric) - is this really the correct thing to do? - */ - if (rtn != SUCCESS) { - printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after bus reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun); + /* + * If this device uses the soft reset option, and this + * is one of the devices acting up, then our only + * option is to wait a bit, since the command is + * supposedly still running. + * + * FIXME(eric) - right now we will just end up falling + * through to the 'take device offline' case. + * + * FIXME(eric) - It is possible that the command completed + * *after* the error recovery procedure started, and if + * this is the case, we are worrying about nothing here. + * + * FIXME(rmk) - This should be bounded; we shouldn't wait + * for an infinite amount of time for any device. + */ + if (soft_reset) { + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_unjam_host: unable to try bus " + "reset for host %d channel %d\n", + host->host_no, channel)); + scsi_sleep(1 * HZ); + goto try_again; + } - SDloop->online = FALSE; - SDloop->host->host_failed--; - scsi_eh_finish_command(&SCdone, SCloop); - } - } - } + /* + * We now know that we are able to perform a reset for the + * bus that SCpnt points to. There are no soft-reset devices + * with outstanding timed out commands. + */ + rtn = scsi_try_bus_reset(failed_command); + + /* + * If we failed to reset the bus, move on to the next bus. + */ + if (rtn != SUCCESS) + continue; + + /* + * We succeeded. Retry each failed command. + */ + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { + if (SDpnt->channel != channel) + continue; + + rtn = scsi_eh_restart_device(SDpnt, done); + + if (rtn != SUCCESS) { + SCpnt = scsi_eh_find_failed_command(SDpnt); + + /* + * This device failed again. Since a bus + * reset freed it up, chances are we've + * hit the same problem, so try the same + * solution. We also need to ensure that + * the SCSI bus is in the BUS FREE state + * so we can try to talk to other devices. + */ + scsi_try_bus_reset(SCpnt); + scsi_eh_set_device_offline(SDpnt, done, + "not ready or command retry " + "failed after bus reset"); } } } +} + +static void scsi_unjam_host_reset(struct Scsi_Host *host, Scsi_Cmnd **done) +{ + Scsi_Device *SDpnt; + Scsi_Cmnd *SCpnt; + Scsi_Cmnd *failed_command = NULL; + int rtn, soft_reset; - if (host->host_failed == 0) { - ourrtn = TRUE; - goto leave; - } /* - * If we ended up here, we have serious problems. The only thing left - * to try is a full host reset - perhaps the firmware on the device - * crashed, or something like that. + * If we ended up here, we have serious problems. The only thing + * left to try is a full host reset - perhaps the firmware on the + * device crashed, or something like that. * - * It is assumed that a succesful host reset will cause *all* information - * about the command to be flushed from both the host adapter *and* the - * device. + * It is assumed that a succesful host reset will cause *all* + * information about the command to be flushed from both the host + * adapter *and* the device. * - * FIXME(eric) - it isn't clear that devices that implement the soft reset - * option can ever be cleared except via cycling the power. The problem is - * that sending the host reset command will cause the host to forget - * about the pending command, but the device won't forget. For now, we - * skip the host reset option if any of the failed devices are configured - * to use the soft reset option. + * FIXME(eric) - it isn't clear that devices that implement the + * soft reset option can ever be cleared except via cycling the + * power. The problem is that sending the host reset command will + * cause the host to forget about the pending command, but the + * device won't forget. For now, we skip the host reset option + * if any of the failed devices are configured to use the soft + * reset option. */ + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_unjam_host: Try host reset\n")); + + try_again: + failed_command = NULL; + soft_reset = 0; + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { - next_device2: - for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { - if (SCpnt->state != SCSI_STATE_FAILED - && SCpnt->state != SCSI_STATE_TIMEOUT) { - continue; - } - if (SDpnt->soft_reset && SCpnt->state == SCSI_STATE_TIMEOUT) { - /* - * If this device uses the soft reset option, and this - * is one of the devices acting up, then our only - * option is to wait a bit, since the command is - * supposedly still running. - * - * FIXME(eric) - right now we will just end up falling - * through to the 'take device offline' case. - */ - SCSI_LOG_ERROR_RECOVERY(3, - printk("scsi_unjam_host: Unable to try hard host reset\n")); + /* + * Locate any failed commands for this device. + */ + SCpnt = scsi_eh_find_failed_command(SDpnt); + if (SCpnt) + failed_command = SCpnt; - /* - * Due to the spinlock, we will never get out of this - * loop without a proper wait. (DB) - */ - scsi_sleep(1 * HZ); + /* + * If this device has timed out or failed commands, + * and uses the soft_reset option. + */ + if (SCpnt && SDpnt->soft_reset) + soft_reset = 1; + } - goto next_device2; - } - SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard host reset\n")); + /* + * If this device uses the soft reset option, and this + * is one of the devices acting up, then our only + * option is to wait a bit, since the command is + * supposedly still running. + * + * FIXME(eric) - right now we will just end up falling + * through to the 'take device offline' case. + * + * FIXME(rmk) - This should be bounded; we shouldn't wait + * for an infinite amount of time for any device. + */ + if (soft_reset) { + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_unjam_host: unable to try " + "hard host reset\n")); /* - * FIXME(eric) - we need to obtain a valid SCpnt to perform this call. + * Due to the spinlock, we will never get out of this + * loop without a proper wait. (DB) */ - rtn = scsi_try_host_reset(SCpnt); - if (rtn == SUCCESS) { - /* - * FIXME(eric) we assume that all commands are flushed from the - * controller. We should get a DID_RESET for all of the commands - * that were pending. We should ignore these so that we can - * guarantee that we are in a consistent state. - * - * I believe this to be the case right now, but this needs to be - * tested. - */ - for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) { - for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) { - if (SCloop->state != SCSI_STATE_FAILED - && SCloop->state != SCSI_STATE_TIMEOUT) { - continue; - } - rtn = scsi_test_unit_ready(SCloop); - - if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) { - rtn = scsi_eh_retry_command(SCloop); + scsi_sleep(1 * HZ); - if (rtn == SUCCESS) { - SCpnt->host->host_failed--; - scsi_eh_finish_command(&SCdone, SCloop); - } - } - if (rtn != SUCCESS) { - printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after host reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun); - SDloop->online = FALSE; - SDloop->host->host_failed--; - scsi_eh_finish_command(&SCdone, SCloop); - } - } - } - } - } + goto try_again; } + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_unjam_host: Try hard host reset\n")); + /* - * If we solved all of the problems, then let's rev up the engines again. - */ - if (host->host_failed == 0) { - ourrtn = TRUE; - goto leave; - } - /* - * If the HOST RESET failed, then for now we assume that the entire host - * adapter is too hosed to be of any use. For our purposes, however, it is - * easier to simply take the devices offline that correspond to commands - * that failed. + * FIXME(eric) - we need to obtain a valid SCpnt to perform this call. */ - SCSI_LOG_ERROR_RECOVERY(1, printk("scsi_unjam_host: Take device offline\n")); + rtn = scsi_try_host_reset(failed_command); + if (rtn == SUCCESS) { + /* + * FIXME(eric) we assume that all commands are flushed from + * the controller. We should get a DID_RESET for all of the + * commands that were pending. We should ignore these so + * that we can guarantee that we are in a consistent state. + * + * I believe this to be the case right now, but this needs + * to be tested. + */ + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { + rtn = scsi_eh_restart_device(SDpnt, done); - for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { - for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) { - if (SCloop->state == SCSI_STATE_FAILED || SCloop->state == SCSI_STATE_TIMEOUT) { - SDloop = SCloop->device; - if (SDloop->online == TRUE) { - printk(KERN_INFO "scsi: device set offline - command error recover failed: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun); - SDloop->online = FALSE; - } + if (rtn != SUCCESS) { + SCpnt = scsi_eh_find_failed_command(SDpnt); /* - * This should pass the failure up to the top level driver, and - * it will have to try and do something intelligent with it. + * This device failed again. Since a host + * reset freed it up, chances are we've + * hit the same problem, so try the same + * solution. We also need to ensure that + * the SCSI bus is in the BUS FREE state + * so we can try to talk to other devices. */ - SCloop->host->host_failed--; - - if (SCloop->state == SCSI_STATE_TIMEOUT) { - SCloop->result |= (DRIVER_TIMEOUT << 24); - } - SCSI_LOG_ERROR_RECOVERY(3, printk("Finishing command for device %d %x\n", - SDloop->id, SCloop->result)); - - scsi_eh_finish_command(&SCdone, SCloop); + scsi_try_host_reset(SCpnt); + scsi_eh_set_device_offline(SDpnt, done, + "not ready or command retry " + "failed after host reset"); } } } +} - if (host->host_failed != 0) { +static void scsi_unjam_failure(struct Scsi_Host *host, Scsi_Cmnd **done) +{ + Scsi_Device *SDpnt; + + /* + * 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")); +} - ourrtn = FALSE; +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, +}; - leave: +/* + * Function: scsi_unjam_host + * + * Purpose: Attempt to fix a host which has a command that failed for + * some reason. + * + * Arguments: host - host that needs unjamming. + * + * Returns: Nothing + * + * Notes: When we come in here, we *know* that all commands on the + * bus have either completed, failed or timed out. We also + * know that no further commands are being sent to the host, + * so things are relatively quiet and we have freedom to + * fiddle with things as we wish. + * + * Additional note: This is only the *default* implementation. It is possible + * for individual drivers to supply their own version of this + * function, and if the maintainer wishes to do this, it is + * strongly suggested that this function be taken as a template + * and modified. This function was designed to correctly handle + * problems for about 95% of the different cases out there, and + * it should always provide at least a reasonable amount of error + * recovery. + * + * Note3: Any command marked 'FAILED' or 'TIMEOUT' must eventually + * have scsi_finish_command() called for it. We do all of + * the retry stuff here, so when we restart the host after we + * return it should have an empty queue. + */ +STATIC int scsi_unjam_host(struct Scsi_Host *host) +{ + Scsi_Cmnd *SCdone = NULL; + Scsi_Cmnd *SCpnt; + Scsi_Device *SDpnt; + int ourrtn = FALSE; + int i; + + ASSERT_LOCK(&io_request_lock, 0); + + /* + * First, protect against any sort of race condition. If any of the outstanding + * commands are in states that indicate that we are not yet blocked (i.e. we are + * not in a quiet state) then we got woken up in error. If we ever end up here, + * we need to re-examine some of the assumptions. + */ + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { + if (SCpnt->state == SCSI_STATE_FAILED + || SCpnt->state == SCSI_STATE_TIMEOUT + || SCpnt->state == SCSI_STATE_INITIALIZING + || SCpnt->state == SCSI_STATE_UNUSED) { + continue; + } + /* + * Rats. Something is still floating around out there. This could + * be the result of the fact that the upper level drivers are still frobbing + * commands that might have succeeded. There are two outcomes. One is that + * the command block will eventually be freed, and the other one is that + * the command will be queued and will be finished along the way. + */ + SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target)); + +/* + * panic("SCSI Error handler woken too early\n"); + * + * This is no longer a problem, since now the code cares only about + * SCSI_STATE_TIMEOUT and SCSI_STATE_FAILED. + * Other states are useful only to release active commands when devices are + * set offline. If (host->host_active == host->host_busy) we can safely assume + * that there are no commands in state other then TIMEOUT od FAILED. (DB) + * + * FIXME: + * It is not easy to release correctly commands according to their state when + * devices are set offline, when the state is neither TIMEOUT nor FAILED. + * When a device is set offline, we can have some command with + * rq_status=RQ_SCSY_BUSY, owner=SCSI_STATE_HIGHLEVEL, + * state=SCSI_STATE_INITIALIZING and the driver module cannot be released. + * (DB, 17 May 1998) + */ + } + } + + for (i = 0; i < ARRAY_SIZE(unjam_method); i++) { + unjam_method[i](host, &SCdone); + + /* + * If we solved all of the problems, then + * let's rev up the engines again. + */ + if (host->host_failed == 0) { + ourrtn = TRUE; + break; + } + } /* * We should have a list of commands that we 'finished' during the course of @@ -2013,3 +2300,17 @@ * tab-width: 8 * End: */ + +EXPORT_SYMBOL(scsi_eh_times_out); +EXPORT_SYMBOL(scsi_eh_retry_command); +EXPORT_SYMBOL(scsi_request_sense); +EXPORT_SYMBOL(scsi_test_unit_ready); +EXPORT_SYMBOL(scsi_unit_is_ready); +EXPORT_SYMBOL(scsi_eh_finish_command); +EXPORT_SYMBOL(scsi_try_to_abort_command); +EXPORT_SYMBOL(scsi_try_bus_device_reset); +EXPORT_SYMBOL(scsi_try_bus_reset); +EXPORT_SYMBOL(scsi_try_host_reset); +EXPORT_SYMBOL(scsi_sense_valid); +EXPORT_SYMBOL(scsi_done); +EXPORT_SYMBOL(scsi_decide_disposition); --- linux-2.4.27/drivers/scsi/scsi_ioctl.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/scsi_ioctl.c @@ -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; --- linux-2.4.27/drivers/scsi/scsi_lib.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/scsi_lib.c @@ -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); --- linux-2.4.27/drivers/scsi/scsi_syms.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/scsi_syms.c @@ -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); --- linux-2.4.27/drivers/scsi/sd.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/sd.c @@ -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); --- linux-2.4.27/drivers/scsi/sr_ioctl.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/scsi/sr_ioctl.c @@ -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) --- /dev/null +++ linux-2.4.27/drivers/serial/21285.c @@ -0,0 +1,599 @@ +/* + * linux/drivers/char/serial_21285.c + * + * Driver for the serial port on the 21285 StrongArm-110 core logic chip. + * + * Based on drivers/char/serial.c + * + * $Id: 21285.c,v 1.4.2.1 2002/10/24 09:53:23 rmk Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BAUD_BASE (mem_fclk_21285/64) + +#ifdef CONFIG_DEVFS_FS +#define SERIAL_21285_NAME "tts/FB%d" +#define SERIAL_21285_AUXNAME "cua/FB%d" +#else +#define SERIAL_21285_NAME "ttyFB" +#define SERIAL_21285_AUXNAME "cuafb" +#endif + +#define SERIAL_21285_MAJOR 204 +#define SERIAL_21285_MINOR 4 + +#define SERIAL_21285_AUXMAJOR 205 +#define SERIAL_21285_AUXMINOR 4 + +#ifdef CONFIG_SERIAL_21285_OLD +#include +/* + * Compatability with a mistake made a long time ago. + * Note - the use of "ttyI", "/dev/ttyS0" and major/minor 5,64 + * is HIGHLY DEPRECIATED, and will be removed in the 2.5 + * kernel series. + * -- rmk 15/04/2000 + */ +#define SERIAL_21285_OLD_NAME "ttyI" +#define SERIAL_21285_OLD_MAJOR TTY_MAJOR +#define SERIAL_21285_OLD_MINOR 64 + +static struct tty_driver rs285_old_driver; +#endif + +static struct tty_driver rs285_driver, callout_driver; +static int rs285_refcount; +static struct tty_struct *rs285_table[1]; + +static struct termios *rs285_termios[1]; +static struct termios *rs285_termios_locked[1]; + +static char wbuf[1000], *putp = wbuf, *getp = wbuf, x_char; +static struct tty_struct *rs285_tty; +static DECLARE_MUTEX(rs285_sem); +static int rs285_use_count; +static unsigned long rs285_irq_enabled; + +#define TX_IRQ_BIT (0) +#define RX_IRQ_BIT (1) + +static void rs285_stop_tx(void) +{ + if (test_and_clear_bit(TX_IRQ_BIT, &rs285_irq_enabled)) + disable_irq(IRQ_CONTX); +} + +static void rs285_start_tx(void) +{ + if (!test_and_set_bit(TX_IRQ_BIT, &rs285_irq_enabled)) + enable_irq(IRQ_CONTX); +} + +static void rs285_stop_rx(void) +{ + if (test_and_clear_bit(RX_IRQ_BIT, &rs285_irq_enabled)) + disable_irq(IRQ_CONRX); +} + +static void rs285_start_rx(void) +{ + if (!test_and_set_bit(RX_IRQ_BIT, &rs285_irq_enabled)) + enable_irq(IRQ_CONRX); +} + +static int rs285_write_room(struct tty_struct *tty) +{ + return putp >= getp ? (sizeof(wbuf) - (long) putp + (long) getp) : ((long) getp - (long) putp - 1); +} + +static void rs285_rx_int(int irq, void *dev_id, struct pt_regs *regs) +{ + if (!rs285_tty) { + rs285_stop_rx(); + return; + } + while (!(*CSR_UARTFLG & 0x10)) { + int ch, flag; + ch = *CSR_UARTDR; + flag = *CSR_RXSTAT; + if (flag & 4) + tty_insert_flip_char(rs285_tty, 0, TTY_OVERRUN); + if (flag & 2) + flag = TTY_PARITY; + else if (flag & 1) + flag = TTY_FRAME; + tty_insert_flip_char(rs285_tty, ch, flag); + } + tty_flip_buffer_push(rs285_tty); +} + +static void rs285_send_xchar(struct tty_struct *tty, char ch) +{ + x_char = ch; + rs285_start_tx(); +} + +static void rs285_throttle(struct tty_struct *tty) +{ + if (I_IXOFF(tty)) + rs285_send_xchar(tty, STOP_CHAR(tty)); +} + +static void rs285_unthrottle(struct tty_struct *tty) +{ + if (I_IXOFF(tty)) { + if (x_char) + x_char = 0; + else + rs285_send_xchar(tty, START_CHAR(tty)); + } +} + +static void rs285_tx_int(int irq, void *dev_id, struct pt_regs *regs) +{ + while (!(*CSR_UARTFLG & 0x20)) { + if (x_char) { + *CSR_UARTDR = x_char; + x_char = 0; + continue; + } + if (putp == getp) { + rs285_stop_tx(); + break; + } + *CSR_UARTDR = *getp; + if (++getp >= wbuf + sizeof(wbuf)) + getp = wbuf; + } + if (rs285_tty) + wake_up_interruptible(&rs285_tty->write_wait); +} + +static inline int rs285_xmit(int ch) +{ + if (putp + 1 == getp || (putp + 1 == wbuf + sizeof(wbuf) && getp == wbuf)) + return 0; + *putp = ch; + if (++putp >= wbuf + sizeof(wbuf)) + putp = wbuf; + rs285_start_tx(); + return 1; +} + +static int rs285_write(struct tty_struct *tty, int from_user, + const u_char * buf, int count) +{ + int i; + + if (from_user && verify_area(VERIFY_READ, buf, count)) + return -EINVAL; + + for (i = 0; i < count; i++) { + char ch; + if (from_user) + __get_user(ch, buf + i); + else + ch = buf[i]; + if (!rs285_xmit(ch)) + break; + } + return i; +} + +static void rs285_put_char(struct tty_struct *tty, u_char ch) +{ + rs285_xmit(ch); +} + +static int rs285_chars_in_buffer(struct tty_struct *tty) +{ + return sizeof(wbuf) - rs285_write_room(tty); +} + +static void rs285_flush_buffer(struct tty_struct *tty) +{ + rs285_stop_tx(); + putp = getp = wbuf; + if (x_char) + rs285_start_tx(); +} + +static inline void rs285_set_cflag(int cflag) +{ + int h_lcr, baud, quot; + + switch (cflag & CSIZE) { + case CS5: + h_lcr = 0x10; + break; + case CS6: + h_lcr = 0x30; + break; + case CS7: + h_lcr = 0x50; + break; + default: /* CS8 */ + h_lcr = 0x70; + break; + + } + if (cflag & CSTOPB) + h_lcr |= 0x08; + if (cflag & PARENB) + h_lcr |= 0x02; + if (!(cflag & PARODD)) + h_lcr |= 0x04; + + switch (cflag & CBAUD) { + case B200: baud = 200; break; + case B300: baud = 300; break; + case B1200: baud = 1200; break; + case B1800: baud = 1800; break; + case B2400: baud = 2400; break; + case B4800: baud = 4800; break; + default: + case B9600: baud = 9600; break; + case B19200: baud = 19200; break; + case B38400: baud = 38400; break; + case B57600: baud = 57600; break; + case B115200: baud = 115200; break; + } + + /* + * The documented expression for selecting the divisor is: + * BAUD_BASE / baud - 1 + * However, typically BAUD_BASE is not divisible by baud, so + * we want to select the divisor that gives us the minimum + * error. Therefore, we want: + * int(BAUD_BASE / baud - 0.5) -> + * int(BAUD_BASE / baud - (baud >> 1) / baud) -> + * int((BAUD_BASE - (baud >> 1)) / baud) + */ + quot = (BAUD_BASE - (baud >> 1)) / baud; + + *CSR_UARTCON = 0; + *CSR_L_UBRLCR = quot & 0xff; + *CSR_M_UBRLCR = (quot >> 8) & 0x0f; + *CSR_H_UBRLCR = h_lcr; + *CSR_UARTCON = 1; +} + +static void rs285_set_termios(struct tty_struct *tty, struct termios *old) +{ + if (old && tty->termios->c_cflag == old->c_cflag) + return; + rs285_set_cflag(tty->termios->c_cflag); +} + + +static void rs285_stop(struct tty_struct *tty) +{ + rs285_stop_tx(); +} + +static void rs285_start(struct tty_struct *tty) +{ + rs285_start_tx(); +} + +static void rs285_wait_until_sent(struct tty_struct *tty, int timeout) +{ + int orig_jiffies = jiffies; + while (*CSR_UARTFLG & 8) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + set_current_state(TASK_RUNNING); +} + +static int rs285_open(struct tty_struct *tty, struct file *filp) +{ + int line, ret; + + MOD_INC_USE_COUNT; + + line = MINOR(tty->device) - tty->driver.minor_start; + if (line) + return -ENODEV; + + ret = down_interruptible(&rs285_sem); + if (ret) + return ret; + + tty->driver_data = NULL; + rs285_tty = tty; + + if (rs285_use_count == 0) { + rs285_irq_enabled = 3; + ret = request_irq(IRQ_CONRX, rs285_rx_int, 0, "rs285", NULL); + if (ret == 0) { + ret = request_irq(IRQ_CONTX, rs285_tx_int, 0, "rs285", + NULL); + if (ret) + free_irq(IRQ_CONRX, NULL); + } + } + + if (ret == 0) + rs285_use_count++; + + up(&rs285_sem); + + return ret; +} + +static void rs285_close(struct tty_struct *tty, struct file *filp) +{ + down(&rs285_sem); + if (!--rs285_use_count) { + rs285_wait_until_sent(tty, 0); + rs285_stop_rx(); + rs285_stop_tx(); + rs285_tty = NULL; + free_irq(IRQ_CONTX, NULL); + free_irq(IRQ_CONRX, NULL); + } + up(&rs285_sem); + MOD_DEC_USE_COUNT; +} + +static int __init rs285_init(void) +{ + int baud = B9600; + + if (machine_is_personal_server()) + baud = B57600; + + rs285_driver.magic = TTY_DRIVER_MAGIC; + rs285_driver.driver_name = "serial_21285"; + rs285_driver.name = SERIAL_21285_NAME; + rs285_driver.major = SERIAL_21285_MAJOR; + rs285_driver.minor_start = SERIAL_21285_MINOR; + rs285_driver.num = 1; + rs285_driver.type = TTY_DRIVER_TYPE_SERIAL; + rs285_driver.subtype = SERIAL_TYPE_NORMAL; + rs285_driver.init_termios = tty_std_termios; + rs285_driver.init_termios.c_cflag = baud | CS8 | CREAD | HUPCL | CLOCAL; + rs285_driver.flags = TTY_DRIVER_REAL_RAW; + rs285_driver.refcount = &rs285_refcount; + rs285_driver.table = rs285_table; + rs285_driver.termios = rs285_termios; + rs285_driver.termios_locked = rs285_termios_locked; + + rs285_driver.open = rs285_open; + rs285_driver.close = rs285_close; + rs285_driver.write = rs285_write; + rs285_driver.put_char = rs285_put_char; + rs285_driver.write_room = rs285_write_room; + rs285_driver.chars_in_buffer = rs285_chars_in_buffer; + rs285_driver.flush_buffer = rs285_flush_buffer; + rs285_driver.throttle = rs285_throttle; + rs285_driver.unthrottle = rs285_unthrottle; + rs285_driver.send_xchar = rs285_send_xchar; + rs285_driver.set_termios = rs285_set_termios; + rs285_driver.stop = rs285_stop; + rs285_driver.start = rs285_start; + rs285_driver.wait_until_sent = rs285_wait_until_sent; + + callout_driver = rs285_driver; + callout_driver.name = SERIAL_21285_AUXNAME; + callout_driver.major = SERIAL_21285_AUXMAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + +#ifdef CONFIG_SERIAL_21285_OLD + if (!machine_is_ebsa285() && !machine_is_netwinder()) { + rs285_old_driver = rs285_driver; + rs285_old_driver.name = SERIAL_21285_OLD_NAME; + rs285_old_driver.major = SERIAL_21285_OLD_MAJOR; + rs285_old_driver.minor_start = SERIAL_21285_OLD_MINOR; + + if (tty_register_driver(&rs285_old_driver)) + printk(KERN_ERR "Couldn't register old 21285 serial driver\n"); + } +#endif + + if (tty_register_driver(&rs285_driver)) + printk(KERN_ERR "Couldn't register 21285 serial driver\n"); + if (tty_register_driver(&callout_driver)) + printk(KERN_ERR "Couldn't register 21285 callout driver\n"); + + return 0; +} + +static void __exit rs285_fini(void) +{ + unsigned long flags; + int ret; + + save_flags(flags); + cli(); + ret = tty_unregister_driver(&callout_driver); + if (ret) + printk(KERN_ERR "Unable to unregister 21285 callout driver " + "(%d)\n", ret); + ret = tty_unregister_driver(&rs285_driver); + if (ret) + printk(KERN_ERR "Unable to unregister 21285 driver (%d)\n", + ret); +#ifdef CONFIG_SERIAL_21285_OLD + if (!machine_is_ebsa285() && !machine_is_netwinder()) { + ret = tty_unregister_driver(&rs285_old_driver); + if (ret) + printk(KERN_ERR "Unable to unregister old 21285 " + "driver (%d)\n", ret); + } +#endif + free_irq(IRQ_CONTX, NULL); + free_irq(IRQ_CONRX, NULL); + restore_flags(flags); +} + +module_init(rs285_init); +module_exit(rs285_fini); + +#ifdef CONFIG_SERIAL_21285_CONSOLE +/************** console driver *****************/ + +static void rs285_console_write(struct console *co, const char *s, u_int count) +{ + int i; + + rs285_stop_tx(); + for (i = 0; i < count; i++) { + while (*CSR_UARTFLG & 0x20); + *CSR_UARTDR = s[i]; + if (s[i] == '\n') { + while (*CSR_UARTFLG & 0x20); + *CSR_UARTDR = '\r'; + } + } + rs285_start_tx(); +} + +static kdev_t rs285_console_device(struct console *c) +{ + return MKDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR); +} + +static int __init rs285_console_setup(struct console *co, char *options) +{ + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow; + int cflag = CREAD | HUPCL | CLOCAL; + + if (machine_is_personal_server()) + baud = 57600; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + /* + * Now construct a cflag setting. + */ + switch (baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 9600: + cflag |= B9600; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + default: + cflag |= B9600; + break; + } + switch (bits) { + case 7: + cflag |= CS7; + break; + default: + cflag |= CS8; + break; + } + switch (parity) { + case 'o': + case 'O': + cflag |= PARODD; + break; + case 'e': + case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + rs285_set_cflag(cflag); + rs285_console_write(NULL, "\e[2J\e[Hboot ", 12); + if (options) + rs285_console_write(NULL, options, strlen(options)); + else + rs285_console_write(NULL, "no options", 10); + rs285_console_write(NULL, "\n", 1); + + return 0; +} + +#ifdef CONFIG_SERIAL_21285_OLD +static struct console rs285_old_cons = +{ + SERIAL_21285_OLD_NAME, + rs285_console_write, + NULL, + rs285_console_device, + NULL, + rs285_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; +#endif + +static struct console rs285_cons = +{ + name: SERIAL_21285_NAME, + write: rs285_console_write, + device: rs285_console_device, + setup: rs285_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init rs285_console_init(void) +{ +#ifdef CONFIG_SERIAL_21285_OLD + if (!machine_is_ebsa285() && !machine_is_netwinder()) + register_console(&rs285_old_cons); +#endif + register_console(&rs285_cons); +} + +#endif /* CONFIG_SERIAL_21285_CONSOLE */ + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver"); --- /dev/null +++ linux-2.4.27/drivers/serial/8250.c @@ -0,0 +1,2170 @@ +/* + * linux/drivers/serial/8250.c + * + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * $Id: 8250.c,v 1.14.2.8 2002/10/24 14:31:31 rmk Exp $ + * + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. Currently, we don't + * support this very well, and it may well be dropped from this driver + * in future. As such, mapbase should be NULL. + * + * membase is an 'ioremapped' cookie. This is compatible with the old + * serial.c driver, and is currently the preferred form. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "8250.h" + +/* + * Configuration: + * share_irqs - whether we pass SA_SHIRQ to request_irq(). This option + * is unsafe when used on edge-triggered interrupts. + */ +unsigned int share_irqs = SERIAL8250_SHARE_IRQS; + +/* + * Debugging. + */ +#if 0 +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#if 0 +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#define PASS_LIMIT 256 + +/* + * We default to IRQ0 for the "no irq" hack. Some + * machine types want others as well - they're free + * to redefine this in their header file. + */ +#define is_real_interrupt(irq) ((irq) != 0) + +/* + * This converts from our new CONFIG_ symbols to the symbols + * that asm/serial.h expects. You _NEED_ to comment out the + * linux/config.h include contained inside asm/serial.h for + * this to work. + */ +#undef CONFIG_SERIAL_MANY_PORTS +#undef CONFIG_SERIAL_DETECT_IRQ +#undef CONFIG_SERIAL_MULTIPORT +#undef CONFIG_HUB6 + +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ +#define CONFIG_SERIAL_DETECT_IRQ 1 +#endif +#ifdef CONFIG_SERIAL_8250_MULTIPORT +#define CONFIG_SERIAL_MULTIPORT 1 +#endif +#ifdef CONFIG_SERIAL_8250_HUB6 +#define CONFIG_HUB6 1 +#endif +#ifdef CONFIG_SERIAL_8250_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS 1 +#endif + +#include + +static struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS /* defined in asm/serial.h */ +}; + +#define UART_NR ARRAY_SIZE(old_serial_port) + +static struct tty_driver normal, callout; +static struct tty_struct *serial8250_table[UART_NR]; +static struct termios *serial8250_termios[UART_NR], *serial8250_termios_locked[UART_NR]; + +#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; +#endif /* CONFIG_SERIAL_8250_RSA */ + +struct uart_8250_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned int capabilities; /* port capabilities */ + unsigned char acr; + unsigned char ier; + unsigned short rev; + unsigned char lcr; + unsigned char mcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char efr; + unsigned int lsr_break_flag; + + /* + * We provide a per-port pm hook. + */ + void (*pm)(struct uart_port *port, + unsigned int state, unsigned int old); +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +static struct irq_info irq_lists[NR_IRQS]; + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "Cirrus", 1, 0 }, + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "Startech", 1, 0 }, + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO } +}; + +static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset) +{ + offset <<= up->port.regshift; + + switch (up->port.iotype) { +#ifdef CONFIG_SERIAL_8250_HUB6 + case SERIAL_IO_HUB6: + outb(up->port.hub6 - 1 + offset, up->port.iobase); + return inb(up->port.iobase + 1); +#endif + + case SERIAL_IO_MEM: + return readb(up->port.membase + offset); + + default: + return inb(up->port.iobase + offset); + } +} + +static _INLINE_ void +serial_out(struct uart_8250_port *up, int offset, int value) +{ + offset <<= up->port.regshift; + + switch (up->port.iotype) { +#ifdef CONFIG_SERIAL_8250_HUB6 + case SERIAL_IO_HUB6: + outb(up->port.hub6 - 1 + offset, up->port.iobase); + outb(value, up->port.iobase + 1); + break; +#endif + + case SERIAL_IO_MEM: + writeb(value, up->port.membase + offset); + break; + + default: + outb(value, up->port.iobase + offset); + } +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(up, offset) serial_in(up, offset) +#define serial_outp(up, offset, value) serial_out(up, offset, value) + + +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_8250_port *up, int offset, int value) +{ + serial_out(up, UART_SCR, offset); + serial_out(up, UART_ICR, value); +} + +static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) +{ + unsigned int value; + + serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); + serial_out(up, UART_SCR, offset); + value = serial_in(up, UART_ICR); + serial_icr_write(up, UART_ACR, up->acr); + + return value; +} + +#ifdef CONFIG_SERIAL_8250_RSA +/* + * Attempts to turn on the RSA FIFO. Returns zero on failure. + * We set the port uart clock rate if we succeed. + */ +static int __enable_rsa(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; + + return result; +} + +static void enable_rsa(struct uart_8250_port *up) +{ + if (up->port.type == PORT_RSA) { + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + __enable_rsa(up); + spin_unlock_irq(&up->port.lock); + } + if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_outp(up, UART_RSA_FRR, 0); + } +} + +/* + * Attempts to turn off the RSA FIFO. Returns zero on failure. + * It is unknown why interrupts were disabled in here. However, + * the caller is expected to preserve this behaviour by grabbing + * the spinlock before calling this function. + */ +static void disable_rsa(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + if (up->port.type == PORT_RSA && + up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; + spin_unlock_irq(&up->port.lock); + } +} +#endif /* CONFIG_SERIAL_8250_RSA */ + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +static int size_fifo(struct uart_8250_port *up) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(up, UART_FCR); + old_mcr = serial_inp(up, UART_MCR); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_MCR, UART_MCR_LOOP); + serial_outp(up, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(up, UART_DLL); + old_dlm = serial_inp(up, UART_DLM); + serial_outp(up, UART_DLL, 0x01); + serial_outp(up, UART_DLM, 0x00); + serial_outp(up, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(up, UART_TX, count); + mdelay(20);/* FIXME - schedule_timeout */ + for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(up, UART_RX); + serial_outp(up, UART_FCR, old_fcr); + serial_outp(up, UART_MCR, old_mcr); + serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_outp(up, UART_DLL, old_dll); + serial_outp(up, UART_DLM, old_dlm); + + return count; +} + +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void autoconfig_has_efr(struct uart_8250_port *up) +{ + unsigned char id1, id2, id3, rev, saved_dll, saved_dlm; + + /* + * First we check to see if it's an Oxford Semiconductor UART. + * + * If we have to do this here because some non-National + * Semiconductor clone chips lock up if you try writing to the + * LSR register (which serial_icr_read does) + */ + + /* + * Check for Oxford Semiconductor 16C950. + * + * EFR [4] must be set else this test fails. + * + * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca) + * claims that it's needed for 952 dual UART's (which are not + * recommended for new designs). + */ + up->acr = 0; + serial_out(up, UART_LCR, 0xBF); + serial_out(up, UART_EFR, 0x10); + serial_out(up, UART_LCR, 0x00); + id1 = serial_icr_read(up, UART_ID1); + id2 = serial_icr_read(up, UART_ID2); + id3 = serial_icr_read(up, UART_ID3); + rev = serial_icr_read(up, UART_REV); + + DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev); + + if (id1 == 0x16 && id2 == 0xC9 && + (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { + up->port.type = PORT_16C950; + up->rev = rev | (id3 << 8); + return; + } + + /* + * We check for a XR16C850 by setting DLL and DLM to 0, and then + * reading back DLL and DLM. The chip type depends on the DLM + * value read back: + * 0x10 - XR16C850 and the DLL contains the chip revision. + * 0x12 - XR16C2850. + * 0x14 - XR16C854. + */ + serial_outp(up, UART_LCR, UART_LCR_DLAB); + saved_dll = serial_inp(up, UART_DLL); + saved_dlm = serial_inp(up, UART_DLM); + serial_outp(up, UART_DLL, 0); + serial_outp(up, UART_DLM, 0); + id2 = serial_inp(up, UART_DLL); + id1 = serial_inp(up, UART_DLM); + serial_outp(up, UART_DLL, saved_dll); + serial_outp(up, UART_DLM, saved_dlm); + + DEBUG_AUTOCONF("850id=%02x:%02x ", id1, id2); + + if (id1 == 0x10 || id1 == 0x12 || id1 == 0x14) { + if (id1 == 0x10) + up->rev = id2; + up->port.type = PORT_16850; + return; + } + + /* + * It wasn't an XR16C850. + * + * We distinguish between the '654 and the '650 by counting + * how many bytes are in the FIFO. I'm using this for now, + * since that's the technique that was sent to me in the + * serial driver update, but I'm not convinced this works. + * I've had problems doing this in the past. -TYT + */ + if (size_fifo(up) == 64) + up->port.type = PORT_16654; + else + up->port.type = PORT_16650V2; +} + +/* + * We detected a chip without a FIFO. Only two fall into + * this category - the original 8250 and the 16450. The + * 16450 has a scratch register (accessible with LCR=0) + */ +static void autoconfig_8250(struct uart_8250_port *up) +{ + unsigned char scratch, status1, status2; + + up->port.type = PORT_8250; + + scratch = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0xa5); + status1 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0x5a); + status2 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, scratch); + + if (status1 == 0xa5 && status2 == 0x5a) + up->port.type = PORT_16450; +} + +/* + * We know that the chip has FIFOs. Does it have an EFR? The + * EFR is located in the same register position as the IIR and + * we know the top two bits of the IIR are currently set. The + * EFR should contain zero. Try to read the EFR. + */ +static void autoconfig_16550a(struct uart_8250_port *up) +{ + unsigned char status1, status2; + + up->port.type = PORT_16550A; + + /* + * Check for presence of the EFR when DLAB is set. + * Only ST16C650V1 UARTs pass this test. + */ + serial_outp(up, UART_LCR, UART_LCR_DLAB); + if (serial_in(up, UART_EFR) == 0) { + DEBUG_AUTOCONF("EFRv1 "); + up->port.type = PORT_16650; + return; + } + + /* + * Maybe it requires 0xbf to be written to the LCR. + * (other ST16C650V2 UARTs, TI16C752A, etc) + */ + serial_outp(up, UART_LCR, 0xBF); + if (serial_in(up, UART_EFR) == 0) { + DEBUG_AUTOCONF("EFRv2 "); + autoconfig_has_efr(up); + return; + } + + /* + * Check for a National Semiconductor SuperIO chip. + * Attempt to switch to bank 2, read the value of the LOOP bit + * from EXCR1. Switch back to bank 0, change it in MCR. Then + * switch back to bank 2, read it from EXCR1 again and check + * it's changed. If so, set baud_base in EXCR2 to 921600. + */ + serial_outp(up, UART_LCR, 0); + status1 = serial_in(up, UART_MCR); + serial_outp(up, UART_LCR, 0xE0); + status2 = serial_in(up, 0x02); /* EXCR1 */ + + if (!((status2 ^ status1) & UART_MCR_LOOP)) { + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP); + serial_outp(up, UART_LCR, 0xE0); + status2 = serial_in(up, 0x02); /* EXCR1 */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_MCR, status1); + + if ((status2 ^ status1) & UART_MCR_LOOP) { + serial_outp(up, UART_LCR, 0xE0); + status1 = serial_in(up, 0x04); /* EXCR1 */ + status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ + status1 |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ + serial_outp(up, 0x04, status1); + serial_outp(up, UART_LCR, 0); + + up->port.type = PORT_NS16550A; + up->port.uartclk = 921600*16; + return; + } + } + + /* + * No EFR. Try to detect a TI16750, which only sets bit 5 of + * the IIR when 64 byte FIFO mode is enabled when DLAB is set. + * Try setting it with and without DLAB set. Cheap clones + * set bit 5 without DLAB set. + */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + status1 = serial_in(up, UART_IIR) >> 5; + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + status2 = serial_in(up, UART_IIR) >> 5; + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + + DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2); + + if (status1 == 6 && status2 == 7) { + up->port.type = PORT_16750; + return; + } +} + +/* + * This routine is called by rs_init() to initialize a specific serial + * port. It determines what type of UART chip this serial port is + * using: 8250, 16450, 16550, 16550A. The important question is + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. + */ +static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) +{ + unsigned char status1, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + + if (!up->port.iobase && !up->port.mapbase && !up->port.membase) + return; + + DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ", + up->port.line, up->port.iobase, up->port.membase); + + /* + * We really do need global IRQs disabled here - we're going to + * be frobbing the chips IRQ enable register to see if it exists. + */ + spin_lock_irqsave(&up->port.lock, flags); + + if (!(up->port.flags & UPF_BUGGY_UART)) { + /* + * Do a simple existence test first; if we fail this, + * there's no point trying anything else. + * + * 0x80 is used as a nonsense port to prevent against + * false positives due to ISA bus float. The + * assumption is that 0x80 is a non-existent port; + * which should be safe since include/asm/io.h also + * makes this assumption. + * + * Note: this is safe as long as MCR bit 4 is clear + * and the device is in "PC" mode. + */ + scratch = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0F) { + /* + * We failed; there's nothing here + */ + DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", + scratch2, scratch3); + goto out; + } + } + + save_mcr = serial_in(up, UART_MCR); + save_lcr = serial_in(up, UART_LCR); + + /* + * Check to see if a UART is really there. Certain broken + * internal modems based on the Rockwell chipset fail this + * test, because they apparently don't implement the loopback + * test mode. So this test is skipped on the COM 1 through + * COM 4 ports. This *should* be safe, since no board + * manufacturer would be stupid enough to design a board + * that conflicts with COM 1-4 --- we hope! + */ + if (!(up->port.flags & UPF_SKIP_TEST)) { + serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(up, UART_MSR) & 0xF0; + serial_outp(up, UART_MCR, save_mcr); + if (status1 != 0x90) { + DEBUG_AUTOCONF("LOOP test failed (%02x) ", + status1); + goto out; + } + } + + /* + * We're pretty sure there's a port here. Lets find out what + * type of port it is. The IIR top two bits allows us to find + * out if its 8250 or 16450, 16550, 16550A or later. This + * determines what we test for next. + * + * We also initialise the EFR (if any) to zero for later. The + * EFR occupies the same register location as the FCR and IIR. + */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, 0); + serial_outp(up, UART_LCR, 0); + + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(up, UART_IIR) >> 6; + + DEBUG_AUTOCONF("iir=%d ", scratch); + + switch (scratch) { + case 0: + autoconfig_8250(up); + break; + case 1: + up->port.type = PORT_UNKNOWN; + break; + case 2: + up->port.type = PORT_16550; + break; + case 3: + autoconfig_16550a(up); + break; + } + +#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) + /* + * Only probe for RSA ports if we got the region. + */ + if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { + int i; + + for (i = 0 ; i < PORT_RSA_MAX ; ++i) { + if (!probe_rsa[i] && !force_rsa[i]) + break; + if (((probe_rsa[i] != up->port.iobase) || + check_region(up->port.iobase + UART_RSA_BASE, 16)) && + (force_rsa[i] != up->port.iobase)) + continue; + if (__enable_rsa(up)) { + up->port.type = PORT_RSA; + break; + } + } + } +#endif + serial_outp(up, UART_LCR, save_lcr); + + up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + up->capabilities = uart_config[up->port.type].flags; + + if (up->port.type == PORT_UNKNOWN) + goto out; + + /* + * Reset the UART. + */ +#ifdef CONFIG_SERIAL_8250_RSA + if (up->port.type == PORT_RSA) + serial_outp(up, UART_RSA_FRR, 0); +#endif + serial_outp(up, UART_MCR, save_mcr); + serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(up, UART_FCR, 0); + (void)serial_in(up, UART_RX); + serial_outp(up, UART_IER, 0); + + out: + spin_unlock_irqrestore(&up->port.lock, flags); + +#ifdef CONFIG_SERIAL_8250_RSA + if (up->port.iobase && up->port.type == PORT_RSA) { + release_region(up->port.iobase, 8); + request_region(up->port.iobase + UART_RSA_BASE, 16, + "serial_rsa"); + } +#endif + DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name); +} + +static void autoconfig_irq(struct uart_8250_port *up) +{ + unsigned char save_mcr, save_ier; + unsigned char save_ICP = 0; + unsigned int ICP = 0; + unsigned long irqs; + int irq; + + if (up->port.flags & UPF_FOURPORT) { + ICP = (up->port.iobase & 0xfe0) | 0x1f; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(up, UART_MCR); + save_ier = serial_inp(up, UART_IER); + serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(up, UART_MCR, 0); + udelay (10); + if (up->port.flags & UPF_FOURPORT) { + serial_outp(up, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(up, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(up, UART_LSR); + (void)serial_inp(up, UART_RX); + (void)serial_inp(up, UART_IIR); + (void)serial_inp(up, UART_MSR); + serial_outp(up, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(up, UART_MCR, save_mcr); + serial_outp(up, UART_IER, save_ier); + + if (up->port.flags & UPF_FOURPORT) + outb_p(save_ICP, ICP); + + up->port.irq = (irq > 0) ? irq : 0; +} + +static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } + if (up->port.type == PORT_16C950 && tty_stop) { + up->acr |= UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } + /* + * We only do this from uart_start + */ + if (tty_start && up->port.type == PORT_16C950) { + up->acr &= ~UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void serial8250_stop_rx(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void serial8250_enable_ms(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static _INLINE_ void +receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs) +{ + struct tty_struct *tty = up->port.info->tty; + unsigned char ch; + int max_count = 256; + + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + /* + * FIXME: Deadlock can happen here if we're a + * low-latency port. We're holding the per-port + * spinlock, and we call flush_to_ldisc-> + * n_tty_receive_buf->n_tty_receive_char-> + * opost->uart_put_char. + */ + tty->flip.tqueue.routine((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(up, UART_RX); + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + up->port.icount.rx++; + + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + *status &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch, regs)) + goto ignore_char; + if ((*status & up->port.ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((*status & UART_LSR_OE) && + tty->flip.count < TTY_FLIPBUF_SIZE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + ignore_char: + *status = serial_inp(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static _INLINE_ void transmit_chars(struct uart_8250_port *up) +{ + struct circ_buf *xmit = &up->port.info->xmit; + int count; + + if (up->port.x_char) { + serial_outp(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial8250_stop_tx(&up->port, 0); + return; + } + + count = up->port.fifosize; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + serial8250_stop_tx(&up->port, 0); +} + +static _INLINE_ void check_modem_status(struct uart_8250_port *up) +{ + int status; + + status = serial_in(up, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.info->delta_msr_wait); +} + +/* + * This handles the interrupt from one port. + */ +static inline void +serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs) +{ + unsigned int status = serial_inp(up, UART_LSR); + + DEBUG_INTR("status = %x...", status); + + if (status & UART_LSR_DR) + receive_chars(up, &status, regs); + check_modem_status(up); + if (status & UART_LSR_THRE) + transmit_chars(up); +} + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static void serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0; + + DEBUG_INTR("serial8250_interrupt(%d)...", irq); + + spin_lock(&i->lock); + + l = i->head; + do { + struct uart_8250_port *up; + unsigned int iir; + + up = list_entry(l, struct uart_8250_port, list); + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) { + spin_lock(&up->port.lock); + serial8250_handle_port(up, regs); + spin_unlock(&up->port.lock); + + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) { + /* If we hit this, we're dead. */ + printk(KERN_ERR "serial8250: too much work for " + "irq%d\n", irq); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +static int serial_link_irq_chain(struct uart_8250_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0; + + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&up->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(up->port.irq, serial8250_interrupt, + irq_flags, "serial", i); + if (ret < 0) + serial_do_unlink(i, up); + } + + return ret; +} + +static void serial_unlink_irq_chain(struct uart_8250_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + + BUG_ON(i->head == NULL); + + if (list_empty(i->head)) + free_irq(up->port.irq, i); + + serial_do_unlink(i, up); +} + +/* + * This function is used to handle ports that do not have an + * interrupt. This doesn't work very well for 16450's, but gives + * barely passable results for a 16550A. (Although at the expense + * of much CPU overhead). + */ +static void serial8250_timeout(unsigned long data) +{ + struct uart_8250_port *up = (struct uart_8250_port *)data; + unsigned int timeout; + unsigned int iir; + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) { + spin_lock(&up->port.lock); + serial8250_handle_port(up, NULL); + spin_unlock(&up->port.lock); + } + + timeout = up->port.timeout; + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + mod_timer(&up->timer, jiffies + timeout); +} + +static unsigned int serial8250_tx_empty(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial8250_get_mctrl(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + unsigned char status; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + status = serial_in(up, UART_MSR); + spin_unlock_irqrestore(&up->port.lock, flags); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned char mcr = 0; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; + + serial_out(up, UART_MCR, mcr); +} + +static void serial8250_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int serial8250_startup(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + unsigned char lsr, iir; + int retval; + + up->capabilities = uart_config[up->port.type].flags; + up->mcr = 0; + up->efr = 0; + up->ier = 0; + + if (up->port.type == PORT_16C950) { + /* Wake up and initialize UART */ + up->acr = 0; + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_IER, 0); + serial_outp(up, UART_LCR, 0); + serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, 0); + } + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * If this is an RSA port, see if we can kick it up to the + * higher speed clock. + */ + enable_rsa(up); +#endif + + /* + * Clear the FIFO buffers and disable them. + * (they will be reeanbled in change_speed()) + */ + if (up->capabilities & UART_CLEAR_FIFO) { + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_FCR, 0); + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(up, UART_LSR); + (void) serial_inp(up, UART_RX); + (void) serial_inp(up, UART_IIR); + (void) serial_inp(up, UART_MSR); + + /* + * At this point, there's no way the LSR could still be 0xff; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (!(up->port.flags & UPF_BUGGY_UART) && + (serial_inp(up, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", up->port.line); + return -ENODEV; + } + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!is_real_interrupt(up->port.irq)) { + unsigned int timeout = up->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + timeout); + } else { + retval = serial_link_irq_chain(up); + if (retval) + return retval; + } + + /* + * Now, initialize the UART + */ + serial_outp(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & UPF_FOURPORT) { + if (!is_real_interrupt(up->port.irq)) + up->port.mctrl |= TIOCM_OUT1; + } else + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + if (is_real_interrupt(up->port.irq)) + up->port.mctrl |= TIOCM_OUT2; + + serial8250_set_mctrl(&up->port, up->port.mctrl); + + /* + * Do a quick test to see if we receive an + * interrupt when we enable the TX irq. + */ + serial_outp(up, UART_IER, UART_IER_THRI); + lsr = serial_in(up, UART_LSR); + iir = serial_in(up, UART_IIR); + serial_outp(up, UART_IER, 0); + + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + up->capabilities |= UART_BAD_TX_ENABLE; + printk("ttyS%d - enabling bad tx status workarounds\n", + port->line); + } + + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via change_speed(), which will be occuring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(up, UART_IER, up->ier); + + if (up->port.flags & UPF_FOURPORT) { + unsigned int icp; + /* + * Enable interrupts on the AST Fourport board + */ + icp = (up->port.iobase & 0xfe0) | 0x01f; + outb_p(0x80, icp); + (void) inb_p(icp); + } + + /* + * And clear the interrupt registers again for luck. + */ + (void) serial_inp(up, UART_LSR); + (void) serial_inp(up, UART_RX); + (void) serial_inp(up, UART_IIR); + (void) serial_inp(up, UART_MSR); + + return 0; +} + +static void serial8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long flags; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_outp(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & UPF_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((up->port.iobase & 0xfe0) | 0x1f); + up->port.mctrl |= TIOCM_OUT1; + } else + up->port.mctrl &= ~TIOCM_OUT2; + + serial8250_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_FCR, 0); + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + disable_rsa(up); +#endif + + /* + * Read data port to reset things, and then unlink from + * the IRQ chain. + */ + (void) serial_in(up, UART_RX); + + if (!is_real_interrupt(up->port.irq)) + del_timer_sync(&up->timer); + else + serial_unlink_irq_chain(up); +} + +static void serial8250_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned char cval, fcr = 0; + unsigned long flags; + + switch (cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + default: + case CS8: + cval = 0x03; + break; + } + + if (cflag & CSTOPB) + cval |= 0x04; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* + * Work around a bug in the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 && + up->rev == 0x5201) + quot ++; + + if (up->capabilities & UART_USE_FIFO) { + if ((up->port.uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_8250_RSA + else if (up->port.type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (up->port.type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (iflag & IGNPAR) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + if (up->capabilities & UART_MCRAFE) { + /* + * TI16C750 hardware flow control + */ + up->mcr &= ~UART_MCR_AFE; + if (cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + } + if (up->capabilities & UART_EFRAFE) { + /* + * TI16C752/Startech hardware flow control + * FIXME: + * - TI16C752 requires control thresholds + * to be set for auto-RTS. + * - We only enable auto-CTS here. + * Note: ST16C654 does not allow MCR bit 1 + * to override RTS when UART_EFR_RTS is set. + */ + up->efr &= ~UART_EFR_CTS; + if (cflag & CRTSCTS) + up->efr |= UART_EFR_CTS; + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, up->efr); + } + + if (up->capabilities & UART_NATSEMI) { + /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ + serial_outp(up, UART_LCR, 0xe0); + } else { + serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + } + serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ + if (up->port.type == PORT_16750) + serial_outp(up, UART_FCR, fcr); /* set fcr */ + serial_outp(up, UART_LCR, cval); /* reset DLAB */ + up->lcr = cval; /* Save LCR */ + if (up->port.type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(up, UART_FCR, fcr); /* set fcr */ + } + serial8250_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +serial8250_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + if (state) { + /* sleep */ + if (up->capabilities & UART_STARTECH) { + /* Arrange to enter sleep mode */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_IER, UART_IERX_SLEEP); + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, 0); + serial_outp(up, UART_LCR, 0); + } + if (up->port.type == PORT_16750) { + /* Arrange to enter sleep mode */ + serial_outp(up, UART_IER, UART_IERX_SLEEP); + } + } else { + /* wake */ + if (up->capabilities & UART_STARTECH) { + /* Wake up UART */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + /* + * Turn off LCR == 0xBF so we actually set the IER + * register on the XR16C850 + */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_IER, 0); + /* + * Now reset LCR so we can turn off the ECB bit + */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, 0); + /* + * For a XR16C850, we need to set the trigger levels + */ + if (up->port.type == PORT_16850) { + unsigned char fctr; + + fctr = serial_inp(up, UART_FCTR) & + ~(UART_FCTR_RX | UART_FCTR_TX); + serial_outp(up, UART_FCTR, fctr | + UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(up, UART_TRG, UART_TRG_96); + serial_outp(up, UART_FCTR, fctr | + UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(up, UART_TRG, UART_TRG_96); + } + serial_outp(up, UART_LCR, 0); + } + + if (up->port.type == PORT_16750) { + /* Wake up UART */ + serial_outp(up, UART_IER, 0); + } + } +} + +/* + * Resource handling. This is complicated by the fact that resources + * depend on the port type. Maybe we should be claiming the standard + * 8250 ports, and then trying to get other resources as necessary? + */ +static int +serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res) +{ + unsigned int size = 8 << up->port.regshift; + int ret = 0; + + switch (up->port.iotype) { + case SERIAL_IO_MEM: + if (up->port.mapbase) { + *res = request_mem_region(up->port.mapbase, size, "serial"); + if (!*res) + ret = -EBUSY; + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + *res = request_region(up->port.iobase, size, "serial"); + if (!*res) + ret = -EBUSY; + break; + } + return ret; +} + +static int +serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res) +{ + unsigned int size = 8 << up->port.regshift; + unsigned long start; + int ret = 0; + + switch (up->port.iotype) { + case SERIAL_IO_MEM: + if (up->port.mapbase) { + start = up->port.mapbase; + start += UART_RSA_BASE << up->port.regshift; + *res = request_mem_region(start, size, "serial-rsa"); + if (!*res) + ret = -EBUSY; + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + start = up->port.iobase; + start += UART_RSA_BASE << up->port.regshift; + *res = request_region(start, size, "serial-rsa"); + if (!*res) + ret = -EBUSY; + break; + } + + return ret; +} + +static void serial8250_release_port(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned long start, offset = 0, size = 0; + + if (up->port.type == PORT_RSA) { + offset = UART_RSA_BASE << up->port.regshift; + size = 8; + } + + size <<= up->port.regshift; + + switch (up->port.iotype) { + case SERIAL_IO_MEM: + if (up->port.mapbase) { + /* + * Unmap the area. + */ + if (up->port.flags & UPF_IOREMAP) { + iounmap(up->port.membase); + up->port.membase = NULL; + } + + start = up->port.mapbase; + + if (size) + release_mem_region(start + offset, size); + release_mem_region(start, 8 << up->port.regshift); + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + start = up->port.iobase; + + if (size) + release_region(start + offset, size); + release_region(start + offset, 8 << up->port.regshift); + break; + + default: + break; + } +} + +static int serial8250_request_port(struct uart_port *port) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + struct resource *res = NULL, *res_rsa = NULL; + int ret = 0; + + if (up->port.flags & UPF_RESOURCES) { + if (up->port.type == PORT_RSA) { + ret = serial8250_request_rsa_resource(up, &res_rsa); + if (ret < 0) + return ret; + } + + ret = serial8250_request_std_resource(up, &res); + } + + /* + * If we have a mapbase, then request that as well. + */ + if (ret == 0 && up->port.flags & UPF_IOREMAP) { + int size = res->end - res->start + 1; + + up->port.membase = ioremap(up->port.mapbase, size); + if (!up->port.membase) + ret = -ENOMEM; + } + + if (ret < 0) { + if (res_rsa) + release_resource(res_rsa); + if (res) + release_resource(res); + } + return ret; +} + +static void serial8250_config_port(struct uart_port *port, int flags) +{ + struct uart_8250_port *up = (struct uart_8250_port *)port; + struct resource *res_std = NULL, *res_rsa = NULL; + int probeflags = PROBE_ANY; + int ret; + +#ifdef CONFIG_MCA + /* + * Don't probe for MCA ports on non-MCA machines. + */ + if (up->port.flags & UPF_BOOT_ONLYMCA && !MCA_bus) + return; +#endif + + /* + * Find the region that we can probe for. This in turn + * tells us whether we can probe for the type of port. + */ + if (up->port.flags & UPF_RESOURCES) { + ret = serial8250_request_std_resource(up, &res_std); + if (ret < 0) + return; + + ret = serial8250_request_rsa_resource(up, &res_rsa); + if (ret < 0) + probeflags &= ~PROBE_RSA; + } else { + probeflags &= ~PROBE_RSA; + } + + if (flags & UART_CONFIG_TYPE) + autoconfig(up, probeflags); + if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(up); + + /* + * If the port wasn't an RSA port, release the resource. + */ + if (up->port.type != PORT_RSA && res_rsa) + release_resource(res_rsa); + + if (up->port.type == PORT_UNKNOWN && res_std) + release_resource(res_std); +} + +static int +serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= NR_IRQS || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type > PORT_MAX_8250 || ser->type == PORT_CIRRUS || + ser->type == PORT_STARTECH) + return -EINVAL; + return 0; +} + +static const char * +serial8250_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops serial8250_pops = { + .tx_empty = serial8250_tx_empty, + .set_mctrl = serial8250_set_mctrl, + .get_mctrl = serial8250_get_mctrl, + .stop_tx = serial8250_stop_tx, + .start_tx = serial8250_start_tx, + .stop_rx = serial8250_stop_rx, + .enable_ms = serial8250_enable_ms, + .break_ctl = serial8250_break_ctl, + .startup = serial8250_startup, + .shutdown = serial8250_shutdown, + .change_speed = serial8250_change_speed, + .pm = serial8250_pm, + .type = serial8250_type, + .release_port = serial8250_release_port, + .request_port = serial8250_request_port, + .config_port = serial8250_config_port, + .verify_port = serial8250_verify_port, +}; + +static struct uart_8250_port serial8250_ports[UART_NR]; + +static void __init serial8250_isa_init_ports(void) +{ + struct uart_8250_port *up; + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port); + i++, up++) { + up->port.iobase = old_serial_port[i].port; + up->port.irq = irq_cannonicalize(old_serial_port[i].irq); + up->port.uartclk = old_serial_port[i].baud_base * 16; + up->port.flags = old_serial_port[i].flags | + UPF_RESOURCES; + up->port.hub6 = old_serial_port[i].hub6; + up->port.membase = old_serial_port[i].iomem_base; + up->port.iotype = old_serial_port[i].io_type; + up->port.regshift = old_serial_port[i].iomem_reg_shift; + up->port.ops = &serial8250_pops; + + if (up->port.iotype == UPIO_MEM && up->port.mapbase) + up->port.flags |= UPF_IOREMAP; + + if (share_irqs) + up->port.flags |= UPF_SHARE_IRQ; + } +} + +static void __init serial8250_register_ports(struct uart_driver *drv) +{ + int i; + + serial8250_isa_init_ports(); + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + up->port.line = i; + up->port.ops = &serial8250_pops; + init_timer(&up->timer); + up->timer.function = serial8250_timeout; + + /* + * ALPHA_KLUDGE_MCR needs to be killed. + */ + up->mcr_mask = ~ALPHA_KLUDGE_MCR; + up->mcr_force = ALPHA_KLUDGE_MCR; + + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_8250_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_8250_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) + udelay(1); + } +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +serial8250_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + unsigned int ier; + int i; + + /* + * First save the UER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(up); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(up, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(up); + serial_out(up, UART_TX, 13); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); +} + +static kdev_t serial8250_console_device(struct console *co) +{ + return MKDEV(TTY_MAJOR, 64 + co->index); +} + +static int __init serial8250_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &serial8250_ports[co->index].port; + + /* + * Temporary fix. + */ + spin_lock_init(&port->lock); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console serial8250_console = { + .name = "ttyS", + .write = serial8250_console_write, + .device = serial8250_console_device, + .setup = serial8250_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init serial8250_console_init(void) +{ + serial8250_isa_init_ports(); + register_console(&serial8250_console); +} + +#define SERIAL8250_CONSOLE &serial8250_console +#else +#define SERIAL8250_CONSOLE NULL +#endif + +static struct uart_driver serial8250_reg = { + .owner = THIS_MODULE, +#ifdef CONFIG_DEVFS_FS + .normal_name = "tts/%d", + .callout_name = "cua/%d", +#else + .normal_name = "ttyS", + .callout_name = "cua", +#endif + .normal_major = TTY_MAJOR, + .callout_major = TTYAUX_MAJOR, + .normal_driver = &normal, + .callout_driver = &callout, + .table = serial8250_table, + .termios = serial8250_termios, + .termios_locked = serial8250_termios_locked, + .minor = 64, + .nr = UART_NR, + .cons = SERIAL8250_CONSOLE, +}; + +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +static int __register_serial(struct serial_struct *req, int line) +{ + struct uart_port port; + + port.iobase = req->port; + port.membase = req->iomem_base; + port.irq = req->irq; + port.uartclk = req->baud_base * 16; + port.fifosize = req->xmit_fifo_size; + port.regshift = req->iomem_reg_shift; + port.iotype = req->io_type; + port.flags = req->flags | UPF_BOOT_AUTOCONF; + port.mapbase = req->iomap_base; + port.line = line; + + if (share_irqs) + port.flags |= UPF_SHARE_IRQ; + + if (HIGH_BITS_OFFSET) + port.iobase |= (long) req->port_high << HIGH_BITS_OFFSET; + + /* + * If a clock rate wasn't specified by the low level + * driver, then default to the standard clock rate. + */ + if (port.uartclk == 0) + port.uartclk = BASE_BAUD * 16; + + return uart_register_port(&serial8250_reg, &port); +} + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if necessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +int register_serial(struct serial_struct *req) +{ + return __register_serial(req, -1); +} + +/** + * unregister_serial - remove a 16x50 serial port at runtime + * @line: serial line number + * + * Remove one serial port. This may be called from interrupt + * context. + */ +void unregister_serial(int line) +{ + uart_unregister_port(&serial8250_reg, line); +} + +/* + * This is for ISAPNP only. + */ +void serial8250_get_irq_map(unsigned int *map) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + if (serial8250_ports[i].port.type != PORT_UNKNOWN && + serial8250_ports[i].port.irq < 16) + *map |= 1 << serial8250_ports[i].port.irq; + } +} + +static int __init serial8250_init(void) +{ + int ret, i; + + for (i = 0; i < NR_IRQS; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = uart_register_driver(&serial8250_reg); + if (ret >= 0) + serial8250_register_ports(&serial8250_reg); + + return ret; +} + +static void __exit serial8250_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port); + + uart_unregister_driver(&serial8250_reg); +} + +module_init(serial8250_init); +module_exit(serial8250_exit); + +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); +EXPORT_SYMBOL(serial8250_get_irq_map); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); + +MODULE_PARM(share_irqs, "i"); +MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" + " (unsafe)"); + +#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif /* CONFIG_SERIAL_8250_RSA */ + --- /dev/null +++ linux-2.4.27/drivers/serial/8250.h @@ -0,0 +1,88 @@ +/* + * linux/drivers/serial/8250.h + * + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * $Id: 8250.h,v 1.1.1.1.2.1 2002/10/24 09:53:24 rmk Exp $ + */ + +#include + +struct serial8250_probe { + struct module *owner; + int (*pci_init_one)(struct pci_dev *dev); + void (*pci_remove_one)(struct pci_dev *dev); + void (*pnp_init)(void); +}; + +int serial8250_register_probe(struct serial8250_probe *probe); +void serial8250_unregister_probe(struct serial8250_probe *probe); +void serial8250_get_irq_map(unsigned int *map); + +struct old_serial_port { + unsigned int uart; + unsigned int baud_base; + unsigned int port; + unsigned int irq; + unsigned int flags; + unsigned char hub6; + unsigned char io_type; + unsigned char *iomem_base; + unsigned short iomem_reg_shift; +}; + +struct serial8250_config { + const char *name; + unsigned int dfl_xmit_fifo_size; + unsigned int flags; +}; + +#define UART_CLEAR_FIFO 0x01 +#define UART_USE_FIFO 0x02 +#define UART_STARTECH 0x04 +#define UART_NATSEMI 0x08 +#define UART_MCRAFE 0x10 /* TI16C750-style auto-flow */ +#define UART_EFRAFE 0x20 /* TI16C752/startech auto-flow */ + +#define UART_BAD_TX_ENABLE 0x80000000 + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +#define PROBE_RSA (1 << 0) +#define PROBE_ANY (~0) + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +#ifdef CONFIG_SERIAL_8250_SHARE_IRQ +#define SERIAL8250_SHARE_IRQS 1 +#else +#define SERIAL8250_SHARE_IRQS 0 +#endif + +#if defined(__alpha__) && !defined(CONFIG_PCI) +/* + * Digital did something really horribly wrong with the OUT1 and OUT2 + * lines on at least some ALPHA's. The failure mode is that if either + * is cleared, the machine locks up with endless interrupts. + */ +#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) +#else +#define ALPHA_KLUDGE_MCR 0 +#endif --- /dev/null +++ linux-2.4.27/drivers/serial/8250_pci.c @@ -0,0 +1,1080 @@ +/* + * linux/drivers/char/serial_8250_pci.c + * + * Probe module for 8250/16550-type PCI serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * $Id: 8250_pci.c,v 1.8.2.1 2002/10/24 09:53:24 rmk Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 2.4.6 compatibility cruft ;( */ +#define pci_board __pci_board +#include +#undef pci_board + +#include +#include +#include + +#include "8250.h" + +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_IO) +#endif +#ifndef IS_PCI_REGION_IOMEM +#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_MEM) +#endif +#ifndef PCI_IRQ_RESOURCE +#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +#endif + +#ifndef pci_get_subvendor +#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +#define pci_get_subdevice(dev) ((dev)->subsystem_device) +#endif + +struct serial_private { + unsigned int nr; + struct pci_board *board; + int line[0]; +}; + +struct pci_board { + int flags; + int num_ports; + int base_baud; + int uart_offset; + int reg_shift; + int (*init_fn)(struct pci_dev *dev, struct pci_board *board, + int enable); + int first_uart_offset; +}; + +static int +get_pci_port(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned long port; + int base_idx; + int max_port; + int offset; + + base_idx = SPCI_FL_GET_BASE(board->flags); + if (board->flags & SPCI_FL_BASE_TABLE) + base_idx += idx; + + if (board->flags & SPCI_FL_REGION_SZ_CAP) { + max_port = pci_resource_len(dev, base_idx) / 8; + if (idx >= max_port) + return 1; + } + + offset = board->first_uart_offset; + + /* Timedia/SUNIX uses a mixture of BARs and offsets */ + /* Ugh, this is ugly as all hell --- TYT */ + if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ + switch(idx) { + case 0: base_idx=0; + break; + case 1: base_idx=0; offset=8; + break; + case 2: base_idx=1; + break; + case 3: base_idx=1; offset=8; + break; + case 4: /* BAR 2*/ + case 5: /* BAR 3 */ + case 6: /* BAR 4*/ + case 7: base_idx=idx-2; /* BAR 5*/ + } + + /* Some Titan cards are also a little weird */ + if (dev->vendor == PCI_VENDOR_ID_TITAN && + (dev->device == PCI_DEVICE_ID_TITAN_400L || + dev->device == PCI_DEVICE_ID_TITAN_800L)) { + switch (idx) { + case 0: base_idx = 1; + break; + case 1: base_idx = 2; + break; + default: + base_idx = 4; + offset = 8 * (idx - 2); + } + } + + port = pci_resource_start(dev, base_idx) + offset; + + if ((board->flags & SPCI_FL_BASE_TABLE) == 0) + port += idx * (board->uart_offset ? board->uart_offset : 8); + + if (IS_PCI_REGION_IOPORT(dev, base_idx)) { + req->port = port; + if (HIGH_BITS_OFFSET) + req->port_high = port >> HIGH_BITS_OFFSET; + else + req->port_high = 0; + return 0; + } + req->io_type = SERIAL_IO_MEM; + req->iomem_base = ioremap(port, board->uart_offset); + req->iomem_reg_shift = board->reg_shift; + req->port = 0; + return 0; +} + +static _INLINE_ int get_pci_irq(struct pci_dev *dev, + struct pci_board *board, + int idx) +{ + int base_idx; + + if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) + return dev->irq; + + base_idx = SPCI_FL_GET_IRQBASE(board->flags); + if (board->flags & SPCI_FL_IRQ_TABLE) + base_idx += idx; + + return PCI_IRQ_RESOURCE(dev, base_idx); +} + +/* + * Some PCI serial cards using the PLX 9050 PCI interface chip require + * that the card interrupt be explicitly enabled or disabled. This + * seems to be mainly needed on card using the PLX which also use I/O + * mapped memory. + */ +static int __devinit +pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u8 data, *p, irq_config; + int pci_config; + + irq_config = 0x41; + pci_config = PCI_COMMAND_MEMORY; + if (dev->vendor == PCI_VENDOR_ID_PANACOM) + irq_config = 0x43; + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the deep + * FIFOs + */ + irq_config = 0x5b; + pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + } + + pci_read_config_byte(dev, PCI_COMMAND, &data); + + if (enable) + pci_write_config_byte(dev, PCI_COMMAND, + data | pci_config); + + /* enable/disable interrupts */ + p = ioremap(pci_resource_start(dev, 0), 0x80); + writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c); + iounmap(p); + + if (!enable) + pci_write_config_byte(dev, PCI_COMMAND, + data & ~pci_config); + return 0; +} + + +/* + * SIIG serial cards have an PCI interface chip which also controls + * the UART clocking frequency. Each UART can be clocked independently + * (except cards equiped with 4 UARTs) and initial clocking settings + * are stored in the EEPROM chip. It can cause problems because this + * version of serial driver doesn't support differently clocked UART's + * on single PCI card. To prevent this, initialization functions set + * high frequency clocking for all UART's on given card. It is safe (I + * hope) because it doesn't touch EEPROM settings to prevent conflicts + * with other OSes (like M$ DOS). + * + * SIIG support added by Andrey Panin , 10/1999 + * + * There is two family of SIIG serial cards with different PCI + * interface chip and different configuration methods: + * - 10x cards have control registers in IO and/or memory space; + * - 20x cards have control registers in standard PCI configuration space. + */ + +#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) + +static int __devinit +pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u16 data, *p; + + if (!enable) return 0; + + p = ioremap(pci_resource_start(dev, 0), 0x80); + + switch (dev->device & 0xfff8) { + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; + } + + writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); + iounmap(p); + return 0; +} + +#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) + +static int __devinit +pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u8 data; + + if (!enable) return 0; + + /* Change clock frequency for the first UART. */ + pci_read_config_byte(dev, 0x6f, &data); + pci_write_config_byte(dev, 0x6f, data & 0xef); + + /* If this card has 2 UART, we have to do the same with second UART. */ + if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || + ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { + pci_read_config_byte(dev, 0x73, &data); + pci_write_config_byte(dev, 0x73, data & 0xef); + } + return 0; +} + +/* Added for EKF Intel i960 serial boards */ +static int __devinit +pci_inteli960ni_fn(struct pci_dev *dev, + struct pci_board *board, + int enable) +{ + unsigned long oldval; + + if (!(pci_get_subdevice(dev) & 0x1000)) + return(-1); + + if (!enable) /* is there something to deinit? */ + return(0); + + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void*) &oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return(-1); + } + return(0); +} + +/* + * Timedia has an explosion of boards, and to avoid the PCI table from + * growing *huge*, we use this function to collapse some 70 entries + * in the PCI table into one, for sanity's and compactness's sake. + */ +static unsigned short timedia_single_port[] = { + 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 }; +static unsigned short timedia_dual_port[] = { + 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, + 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, + 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, + 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, + 0xD079, 0 }; +static unsigned short timedia_quad_port[] = { + 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, + 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, + 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, + 0xB157, 0 }; +static unsigned short timedia_eight_port[] = { + 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, + 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 }; +static struct timedia_struct { + int num; + unsigned short *ids; +} timedia_data[] = { + { 1, timedia_single_port }, + { 2, timedia_dual_port }, + { 4, timedia_quad_port }, + { 8, timedia_eight_port }, + { 0, 0 } +}; + +static int __devinit +pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + int i, j; + unsigned short *ids; + + if (!enable) + return 0; + + for (i=0; timedia_data[i].num; i++) { + ids = timedia_data[i].ids; + for (j=0; ids[j]; j++) { + if (pci_get_subdevice(dev) == ids[j]) { + board->num_ports = timedia_data[i].num; + return 0; + } + } + } + return 0; +} + +static int __devinit +pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + return 0; +} + +/* + * This is the configuration table for all of the PCI serial boards + * which we support. It is directly indexed by the pci_board_num_t enum + * value, which is encoded in the pci_device_id PCI probe table's + * driver_data member. + */ +enum pci_board_num_t { + pbn_b0_1_115200, + pbn_default = 0, + + pbn_b0_2_115200, + pbn_b0_4_115200, + + pbn_b0_1_921600, + pbn_b0_2_921600, + pbn_b0_4_921600, + + pbn_b0_bt_1_115200, + pbn_b0_bt_2_115200, + pbn_b0_bt_1_460800, + pbn_b0_bt_2_460800, + + pbn_b1_1_115200, + pbn_b1_2_115200, + pbn_b1_4_115200, + pbn_b1_8_115200, + + pbn_b1_2_921600, + pbn_b1_4_921600, + pbn_b1_8_921600, + + pbn_b1_2_1382400, + pbn_b1_4_1382400, + pbn_b1_8_1382400, + + pbn_b2_8_115200, + pbn_b2_4_460800, + pbn_b2_8_460800, + pbn_b2_16_460800, + pbn_b2_4_921600, + pbn_b2_8_921600, + + pbn_b2_bt_1_115200, + pbn_b2_bt_2_115200, + pbn_b2_bt_4_115200, + pbn_b2_bt_2_921600, + + pbn_panacom, + pbn_panacom2, + pbn_panacom4, + pbn_plx_romulus, + pbn_oxsemi, + pbn_timedia, + pbn_intel_i960, + pbn_sgi_ioc3, +#ifdef CONFIG_DDB5074 + pbn_nec_nile4, +#endif +#if 0 + pbn_dci_pccom8, +#endif + pbn_xircom_combo, + + pbn_siig10x_0, + pbn_siig10x_1, + pbn_siig10x_2, + pbn_siig10x_4, + pbn_siig20x_0, + pbn_siig20x_2, + pbn_siig20x_4, + + pbn_computone_4, + pbn_computone_6, + pbn_computone_8, +}; + +static struct pci_board pci_boards[] __devinitdata = { + /* + * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, + * Offset to get to next UART's registers, + * Register shift to use for memory-mapped I/O, + * Initialization function, first UART offset + */ + + /* Generic serial board, pbn_b0_1_115200, pbn_default */ + { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, + pbn_default */ + + { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ + { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ + + { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ + { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ + { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ + + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ + + { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ + { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ + { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ + { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ + + { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ + { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ + { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ + + { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ + { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ + { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ + + { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ + { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ + { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ + { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ + { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ + { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ + + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ + + { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ + 0x20, 2, pci_plx9050_fn, 0x03 }, + /* This board uses the size of PCI Base region 0 to + * signal now many ports are available */ + { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ + { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ + 0, 0, pci_timedia_fn }, + /* EKF addition for i960 Boards form EKF with serial port */ + { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ + 8<<2, 2, pci_inteli960ni_fn, 0x10000}, + { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ + 1, 458333, 0, 0, 0, 0x20178 }, +#ifdef CONFIG_DDB5074 + /* + * NEC Vrc-5074 (Nile 4) builtin UART. + * Conditionally compiled in since this is a motherboard device. + */ + { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ + 64, 3, NULL, 0x300 }, +#endif +#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ /* pbn_dci_pccom8 */ + { SPCI_FL_BASE3, 8, 115200, 8 }, +#endif + { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ + 0, 0, pci_xircom_fn }, + + { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ + 0, 0, pci_siig20x_fn }, + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ + 0, 0, pci_siig20x_fn }, + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ + 0, 0, pci_siig20x_fn }, + + { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ + 0x40, 2, NULL, 0x200 }, + { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ + 0x40, 2, NULL, 0x200 }, + { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ + 0x40, 2, NULL, 0x200 }, +}; + +/* + * Given a complete unknown PCI device, try to use some heuristics to + * guess what the configuration might be, based on the pitiful PCI + * serial specs. Returns 0 on success, 1 on failure. + */ +static int __devinit serial_pci_guess_board(struct pci_dev *dev, + struct pci_board *board) +{ + int num_iomem = 0, num_port = 0, first_port = -1; + int i; + + /* + * If it is not a communications device or the programming + * interface is greater than 6, give up. + * + * (Should we try to make guesses for multiport serial devices + * later?) + */ + if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && + ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || + (dev->class & 0xff) > 6) + return 1; + + for (i=0; i < 6; i++) { + if (IS_PCI_REGION_IOPORT(dev, i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + if (IS_PCI_REGION_IOMEM(dev, i)) + num_iomem++; + } + + /* + * If there is 1 or 0 iomem regions, and exactly one port, use + * it. + */ + if (num_iomem <= 1 && num_port == 1) { + board->flags = first_port; + return 0; + } + return 1; +} + +/* + * return -1 to refuse + */ +static int pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct serial_private *priv; + struct pci_board *board, tmp; + struct serial_struct serial_req; + int base_baud, rc, k; + + board = &pci_boards[ent->driver_data]; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + if (ent->driver_data == pbn_default && + serial_pci_guess_board(dev, board)) + return -ENODEV; + else if (serial_pci_guess_board(dev, &tmp) == 0) { + printk(KERN_INFO "Redundant entry in serial pci_table. " + "Please send the output of\n" + "lspci -vv, this message (%d,%d,%d,%d)\n" + "and the manufacturer and name of " + "serial board or modem board\n" + "to serial-pci-info@lists.sourceforge.net.\n", + dev->vendor, dev->device, + pci_get_subvendor(dev), pci_get_subdevice(dev)); + } + + + priv = kmalloc(sizeof(struct serial_private) + + sizeof(unsigned int) * board->num_ports, + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * Run the initialization function, if any + */ + if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0)) { + kfree(priv); + return -ENODEV; + } + + base_baud = board->base_baud; + if (!base_baud) + base_baud = BASE_BAUD; + memset(&serial_req, 0, sizeof(serial_req)); + for (k=0; k < board->num_ports; k++) { + serial_req.irq = get_pci_irq(dev, board, k); + if (get_pci_port(dev, board, &serial_req, k)) + break; +#ifdef SERIAL_DEBUG_PCI + printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", + serial_req.port, serial_req.irq, serial_req.io_type); +#endif + serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; + serial_req.baud_base = base_baud; + priv->line[k] = register_serial(&serial_req); + if (priv->line[k] < 0) + break; + } + + priv->board = board; + priv->nr = k; + + pci_set_drvdata(dev, priv); + + return 0; +} + +static void pci_remove_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + int i; + + pci_set_drvdata(dev, NULL); + + for (i = 0; i < priv->nr; i++) + unregister_serial(priv->line[i]); + + priv->board->init_fn(dev, priv->board, 0); + + kfree(priv); +} + +static struct pci_device_id serial_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, + pbn_b1_2_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, + pbn_b1_4_921600 }, + + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_1_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + /* VScom SPCOM800, from sl@s.pl */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, + PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, + pbn_panacom }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom4 }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom2 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, + pbn_b2_8_460800 }, + /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ + /* (Exoray@isys.ca) */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, + 0x10b5, 0x106a, 0, 0, + pbn_plx_romulus }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_115200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_115200 }, + + /* Digitan DS560-558, from jimd@esoft.com */ + { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_115200 }, + + /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ + { PCI_VENDOR_ID_USR, 0x1008, + PCI_ANY_ID, PCI_ANY_ID, }, + + /* Titan Electronic cards */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE1, 1, 921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, + /* The 400L and 800L have a custom hack in get_pci_port */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE_TABLE, 4, 921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE_TABLE, 8, 921600 }, + + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + + /* Computone devices submitted by Doug McNash dmcnash@computone.com */ + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, + 0, 0, pbn_computone_4 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, + 0, 0, pbn_computone_8 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, + 0, 0, pbn_computone_6 }, + + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, + { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, + + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_460800 }, + + /* RAStel 2 port modem, gerg@moreton.com.au */ + { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + + /* EKF addition for i960 Boards form EKF with serial port */ + { PCI_VENDOR_ID_INTEL, 0x1960, + 0xE4BF, PCI_ANY_ID, 0, 0, + pbn_intel_i960 }, + + /* Xircom Cardbus/Ethernet combos */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_xircom_combo }, + + /* + * Untested PCI modems, sent in from various folks... + */ + + /* Elsa Model 56K PCI Modem, from Andreas Rath */ + { PCI_VENDOR_ID_ROCKWELL, 0x1004, + 0x1048, 0x1500, 0, 0, + pbn_b1_1_115200 }, + + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + 0xFF00, 0, 0, 0, + pbn_sgi_ioc3 }, + +#ifdef CONFIG_DDB5074 + /* + * NEC Vrc-5074 (Nile 4) builtin UART. + * Conditionally compiled in since this is a motherboard device. + */ + { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_nec_nile4 }, +#endif + +#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_dci_pccom8 }, +#endif + + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, }, + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00, }, + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, }, + { 0, } +}; + +static struct pci_driver serial_pci_driver = { + name: "serial", + probe: pci_init_one, + remove: pci_remove_one, + id_table: serial_pci_tbl, +}; + +static int __init serial8250_pci_init(void) +{ + return pci_module_init(&serial_pci_driver); +} + +static void __exit serial8250_pci_exit(void) +{ + pci_unregister_driver(&serial_pci_driver); +} + +module_init(serial8250_pci_init); +module_exit(serial8250_pci_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); +MODULE_GENERIC_TABLE(pci, serial_pci_tbl); --- /dev/null +++ linux-2.4.27/drivers/serial/8250_pnp.c @@ -0,0 +1,553 @@ +/* + * linux/drivers/char/serial_8250_pnp.c + * + * Probe module for 8250/16550-type ISAPNP serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * $Id: 8250_pnp.c,v 1.3.2.1 2002/10/24 09:53:25 rmk Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "8250.h" + +static struct serial_state rs_table[] = { }; +#define NR_PORTS 0 + +struct pnpbios_device_id +{ + char id[8]; + unsigned long driver_data; +}; + +static const struct pnpbios_device_id pnp_dev_table[] = { + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "AAC000F", 0 }, + /* Anchor Datacomm BV */ + /* SXPro 144 External Data Fax Modem Plug & Play */ + { "ADC0001", 0 }, + /* SXPro 288 External Data Fax Modem Plug & Play */ + { "ADC0002", 0 }, + /* Rockwell 56K ACF II Fax+Data+Voice Modem */ + { "AKY1021", SPCI_FL_NO_SHIRQ }, + /* AZT3005 PnP SOUND DEVICE */ + { "AZT4001", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Boca Research */ + /* Boca Complete Ofc Communicator 14.4 Data-FAX */ + { "BRI0A49", 0 }, + /* Boca Research 33,600 ACF Modem */ + { "BRI1400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI3400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI0A49", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Computer Peripherals Inc */ + /* EuroViVa CommCenter-33.6 SP PnP */ + { "CPI4050", 0 }, + /* Creative Labs */ + /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ + { "CTL3001", 0 }, + /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ + { "CTL3011", 0 }, + /* Creative */ + /* Creative Modem Blaster Flash56 DI5601-1 */ + { "DMB1032", 0 }, + /* Creative Modem Blaster V.90 DI5660 */ + { "DMB2001", 0 }, + /* FUJITSU */ + /* Fujitsu 33600 PnP-I2 R Plug & Play */ + { "FUJ0202", 0 }, + /* Fujitsu FMV-FX431 Plug & Play */ + { "FUJ0205", 0 }, + /* Fujitsu 33600 PnP-I4 R Plug & Play */ + { "FUJ0206", 0 }, + /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ + { "FUJ0209", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "GVC000F", 0 }, + /* Hayes */ + /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ + { "HAY0001", 0 }, + /* Hayes Optima 336 V.34 + FAX + Voice PnP */ + { "HAY000C", 0 }, + /* Hayes Optima 336B V.34 + FAX + Voice PnP */ + { "HAY000D", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5670", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5674", 0 }, + /* Hayes Accura 56K Fax Modem PnP */ + { "HAY5675", 0 }, + /* Hayes 288, V.34 + FAX */ + { "HAYF000", 0 }, + /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ + { "HAYF001", 0 }, + /* IBM */ + /* IBM Thinkpad 701 Internal Modem Voice */ + { "IBM0033", 0 }, + /* Intertex */ + /* Intertex 28k8 33k6 Voice EXT PnP */ + { "IXDC801", 0 }, + /* Intertex 33k6 56k Voice EXT PnP */ + { "IXDC901", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDD801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDD901", 0 }, + /* Intertex 28k8 33k6 Voice SP INT PnP */ + { "IXDF401", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDF801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDF901", 0 }, + /* Kortex International */ + /* KORTEX 28800 Externe PnP */ + { "KOR4522", 0 }, + /* KXPro 33.6 Vocal ASVD PnP */ + { "KORF661", 0 }, + /* Lasat */ + /* LASAT Internet 33600 PnP */ + { "LAS4040", 0 }, + /* Lasat Safire 560 PnP */ + { "LAS4540", 0 }, + /* Lasat Safire 336 PnP */ + { "LAS5440", 0 }, + /* Microcom, Inc. */ + /* Microcom TravelPorte FAST V.34 Plug & Play */ + { "MNP0281", 0 }, + /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ + { "MNP0336", 0 }, + /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ + { "MNP0339", 0 }, + /* Microcom DeskPorte 28.8P Plug & Play */ + { "MNP0342", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0500", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0501", 0 }, + /* Microcom DeskPorte 28.8S Internal Plug & Play */ + { "MNP0502", 0 }, + /* Motorola */ + /* Motorola BitSURFR Plug & Play */ + { "MOT1105", 0 }, + /* Motorola TA210 Plug & Play */ + { "MOT1111", 0 }, + /* Motorola HMTA 200 (ISDN) Plug & Play */ + { "MOT1114", 0 }, + /* Motorola BitSURFR Plug & Play */ + { "MOT1115", 0 }, + /* Motorola Lifestyle 28.8 Internal */ + { "MOT1190", 0 }, + /* Motorola V.3400 Plug & Play */ + { "MOT1501", 0 }, + /* Motorola Lifestyle 28.8 V.34 Plug & Play */ + { "MOT1502", 0 }, + /* Motorola Power 28.8 V.34 Plug & Play */ + { "MOT1505", 0 }, + /* Motorola ModemSURFR External 28.8 Plug & Play */ + { "MOT1509", 0 }, + /* Motorola Premier 33.6 Desktop Plug & Play */ + { "MOT150A", 0 }, + /* Motorola VoiceSURFR 56K External PnP */ + { "MOT150F", 0 }, + /* Motorola ModemSURFR 56K External PnP */ + { "MOT1510", 0 }, + /* Motorola ModemSURFR 56K Internal PnP */ + { "MOT1550", 0 }, + /* Motorola ModemSURFR Internal 28.8 Plug & Play */ + { "MOT1560", 0 }, + /* Motorola Premier 33.6 Internal Plug & Play */ + { "MOT1580", 0 }, + /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ + { "MOT15B0", 0 }, + /* Motorola VoiceSURFR 56K Internal PnP */ + { "MOT15F0", 0 }, + /* Com 1 */ + /* Deskline K56 Phone System PnP */ + { "MVX00A1", 0 }, + /* PC Rider K56 Phone System PnP */ + { "MVX00F2", 0 }, + /* Pace 56 Voice Internal Plug & Play Modem */ + { "PMC2430", 0 }, + /* Generic */ + /* Generic standard PC COM port */ + { "PNP0500", 0 }, + /* Generic 16550A-compatible COM port */ + { "PNP0501", 0 }, + /* Compaq 14400 Modem */ + { "PNPC000", 0 }, + /* Compaq 2400/9600 Modem */ + { "PNPC001", 0 }, + /* Dial-Up Networking Serial Cable between 2 PCs */ + { "PNPC031", 0 }, + /* Dial-Up Networking Parallel Cable between 2 PCs */ + { "PNPC032", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC100", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC101", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC102", 0 }, + /* Standard Modem*/ + { "PNPC103", 0 }, + /* Standard 9600 bps Modem*/ + { "PNPC104", 0 }, + /* Standard 14400 bps Modem*/ + { "PNPC105", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC106", 0 }, + /* Standard Modem */ + { "PNPC107", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC108", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC109", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10A", 0 }, + /* Standard Modem */ + { "PNPC10B", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC10C", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC10D", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10E", 0 }, + /* Standard Modem */ + { "PNPC10F", 0 }, + /* Standard PCMCIA Card Modem */ + { "PNP2000", 0 }, + /* Rockwell */ + /* Modular Technology */ + /* Rockwell 33.6 DPF Internal PnP */ + /* Modular Technology 33.6 Internal PnP */ + { "ROK0030", 0 }, + /* Kortex International */ + /* KORTEX 14400 Externe PnP */ + { "ROK0100", 0 }, + /* Viking Components, Inc */ + /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ + { "ROK4920", 0 }, + /* Rockwell */ + /* British Telecom */ + /* Modular Technology */ + /* Rockwell 33.6 DPF External PnP */ + /* BT Prologue 33.6 External PnP */ + /* Modular Technology 33.6 External PnP */ + { "RSS00A0", 0 }, + /* Viking 56K FAX INT */ + { "RSS0262", 0 }, + /* SupraExpress 28.8 Data/Fax PnP modem */ + { "SUP1310", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1421", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1590", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1760", 0 }, + /* Phoebe Micro */ + /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ + { "TEX0011", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "UAC000F", 0 }, + /* 3Com Corp. */ + /* Gateway Telepath IIvi 33.6 */ + { "USR0000", 0 }, + /* Sportster Vi 14.4 PnP FAX Voicemail */ + { "USR0004", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR0006", 0 }, + /* U.S. Robotics 33.6K Voice EXT PnP */ + { "USR0007", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR2002", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR2070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR2080", 0 }, + /* U.S. Robotics 56K FAX INT */ + { "USR3031", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR3080", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3090", 0 }, + /* U.S. Robotics 56K Message */ + { "USR9100", 0 }, + /* U.S. Robotics 56K FAX EXT PnP*/ + { "USR9160", 0 }, + /* U.S. Robotics 56K FAX INT PnP*/ + { "USR9170", 0 }, + /* U.S. Robotics 56K Voice EXT PnP*/ + { "USR9180", 0 }, + /* U.S. Robotics 56K Voice INT PnP*/ + { "USR9190", 0 }, + { "", 0 } +}; + +static void inline avoid_irq_share(struct pci_dev *dev) +{ + int i, map = 0x1FF8; + struct serial_state *state = rs_table; + struct isapnp_irq *irq; + struct isapnp_resources *res = dev->sysdata; + + for (i = 0; i < NR_PORTS; i++) { + if (state->type != PORT_UNKNOWN) + clear_bit(state->irq, &map); + state++; + } + + for ( ; res; res = res->alt) + for(irq = res->irq; irq; irq = irq->next) + irq->map = map; +} + +static char *modem_names[] __devinitdata = { + "MODEM", "Modem", "modem", "FAX", "Fax", "fax", + "56K", "56k", "K56", "33.6", "28.8", "14.4", + "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", + "33600", "28800", "14400", "V.90", "V.34", "V.32", 0 +}; + +static int __devinit check_name(char *name) +{ + char **tmp; + + for (tmp = modem_names; *tmp; tmp++) + if (strstr(name, *tmp)) + return 1; + + return 0; +} + +static int inline check_compatible_id(struct pci_dev *dev) +{ + int i; + for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) + if ((dev->vendor_compatible[i] == + ISAPNP_VENDOR('P', 'N', 'P')) && + (swab16(dev->device_compatible[i]) >= 0xc000) && + (swab16(dev->device_compatible[i]) <= 0xdfff)) + return 0; + return 1; +} + +/* + * Given a complete unknown ISA PnP device, try to use some heuristics to + * detect modems. Currently use such heuristic set: + * - dev->name or dev->bus->name must contain "modem" substring; + * - device must have only one IO region (8 byte long) with base adress + * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. + * + * Such detection looks very ugly, but can detect at least some of numerous + * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[] + * table. + */ +static int serial_pnp_guess_board(struct pci_dev *dev, int *flags) +{ + struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata; + struct isapnp_resources *resa; + + if (!(check_name(dev->name) || check_name(dev->bus->name)) && + !(check_compatible_id(dev))) + return -ENODEV; + + if (!res || res->next) + return -ENODEV; + + for (resa = res->alt; resa; resa = resa->alt) { + struct isapnp_port *port; + for (port = res->port; port; port = port->next) + if ((port->size == 8) && + ((port->min == 0x2f8) || + (port->min == 0x3f8) || + (port->min == 0x2e8) || + (port->min == 0x3e8))) + return 0; + } + + return -ENODEV; +} + +static int +pnp_init_one(struct pci_dev *dev, const struct pnpbios_device_id *ent, + char *slot_name) +{ + struct serial_struct serial_req; + int ret, line, flags = ent ? ent->driver_data : 0; + + if (!ent) { + ret = serial_pnp_guess_board(dev, &flags); + if (ret) + return ret; + } + + if (dev->prepare(dev) < 0) { + printk("serial: PNP device '%s' prepare failed\n", + slot_name); + return -ENODEV; + } + + if (dev->active) + return -ENODEV; + + if (flags & SPCI_FL_NO_SHIRQ) + avoid_irq_share(dev); + + if (dev->activate(dev) < 0) { + printk("serial: PNP device '%s' activate failed\n", + slot_name); + return -ENODEV; + } + + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.irq = dev->irq_resource[0].start; + serial_req.port = pci_resource_start(dev, 0); + if (HIGH_BITS_OFFSET) + serial_req.port = pci_resource_start(dev, 0) >> HIGH_BITS_OFFSET; + +#ifdef SERIAL_DEBUG_PCI + printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", + serial_req.port, serial_req.irq, serial_req.io_type); +#endif + + serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; + serial_req.baud_base = 115200; + line = register_serial(&serial_req); + + if (line >= 0) { + pci_set_drvdata(dev, (void *)(line + 1)); + + /* + * Public health warning: remove this once the 2.5 + * pnpbios_module_init() stuff is incorporated. + */ + dev->driver = (void *)pnp_dev_table; + } else + dev->deactivate(dev); + + return line >= 0 ? 0 : -ENODEV; +} + +static void pnp_remove_one(struct pci_dev *dev) +{ + int line = (int)pci_get_drvdata(dev); + + if (line) { + pci_set_drvdata(dev, NULL); + + unregister_serial(line - 1); + + dev->deactivate(dev); + } +} + +static char hex[] = "0123456789ABCDEF"; + +/* + * This function should vanish when 2.5 comes around and + * we have pnpbios_module_init() + */ +static void pnp_init(void) +{ + const struct pnpbios_device_id *id; + struct pci_dev *dev = NULL; + +#ifdef SERIAL_DEBUG_PNP + printk("Entered probe_serial_pnp()\n"); +#endif + + isapnp_for_each_dev(dev) { + char slot_name[8]; + u32 pnpid; + + if (dev->active) + continue; + + pnpid = dev->vendor << 16 | dev->device; + pnpid = cpu_to_le32(pnpid); + +#define HEX(id,a) hex[((id)>>a) & 15] +#define CHAR(id,a) (0x40 + (((id)>>a) & 31)) + slot_name[0] = CHAR(pnpid, 26); + slot_name[1] = CHAR(pnpid, 21); + slot_name[2] = CHAR(pnpid, 16); + slot_name[3] = HEX(pnpid, 12); + slot_name[4] = HEX(pnpid, 8); + slot_name[5] = HEX(pnpid, 4); + slot_name[6] = HEX(pnpid, 0); + slot_name[7] = '\0'; + + for (id = pnp_dev_table; id->id[0]; id++) + if (memcmp(id->id, slot_name, 7) == 0) + break; + + if (id->id[0]) + pnp_init_one(dev, id, slot_name); + else + pnp_init_one(dev, NULL, slot_name); + } + +#ifdef SERIAL_DEBUG_PNP + printk("Leaving probe_serial_pnp() (probe finished)\n"); +#endif +} + +static int __init serial8250_pnp_init(void) +{ + if (!isapnp_present()) { +#ifdef SERIAL_DEBUG_PNP + printk("Leaving probe_serial_pnp() (no isapnp)\n"); +#endif + return -ENODEV; + } + pnp_init(); + return 0; +} + +static void __exit serial8250_pnp_exit(void) +{ + struct pci_dev *dev = NULL; + + isapnp_for_each_dev(dev) { + if (dev->driver != (void *)pnp_dev_table) + continue; + pnp_remove_one(dev); + } +} + +module_init(serial8250_pnp_init); +module_exit(serial8250_pnp_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PNPBIOS serial probe module"); +MODULE_GENERIC_TABLE(pnp, pnp_dev_table); + --- /dev/null +++ linux-2.4.27/drivers/serial/Config.in @@ -0,0 +1,91 @@ +# +# Serial device configuration +# +# $Id: Config.in,v 1.4 2001/10/12 15:46:58 rmk Exp $ +# +mainmenu_option next_comment +comment 'Serial drivers' + +if [ "$CONFIG_ARM" = "y" ]; then + # I don't have this in my tree yet. + dep_bool 'Anakin serial port support' CONFIG_SERIAL_ANAKIN $CONFIG_ARCH_ANAKIN + dep_bool ' Console on Anakin serial port' CONFIG_SERIAL_ANAKIN_CONSOLE $CONFIG_SERIAL_ANAKIN + if [ "$CONFIG_SERIAL_ANAKIN" = "y" ]; then + int ' Default Anakin serial baudrate' CONFIG_ANAKIN_DEFAULT_BAUDRATE 9600 + fi + + dep_tristate 'ARM AMBA serial port support' CONFIG_SERIAL_AMBA $CONFIG_ARCH_INTEGRATOR + dep_bool ' Support for console on AMBA serial port' CONFIG_SERIAL_AMBA_CONSOLE $CONFIG_SERIAL_AMBA + if [ "$CONFIG_SERIAL_AMBA" = "y" ]; then + define_bool CONFIG_SERIAL_INTEGRATOR y + fi + + dep_tristate 'CLPS711X serial port support' CONFIG_SERIAL_CLPS711X $CONFIG_ARCH_CLPS711X + dep_bool ' Support for console on CLPS711X serial port' CONFIG_SERIAL_CLPS711X_CONSOLE $CONFIG_SERIAL_CLPS711X + + dep_bool 'DC21285 serial port support' CONFIG_SERIAL_21285 $CONFIG_FOOTBRIDGE + dep_bool ' Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD $CONFIG_SERIAL_21285 $CONFIG_OBSOLETE + dep_bool ' Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE $CONFIG_SERIAL_21285 + + dep_bool 'Excalibur serial port (uart00) support' CONFIG_SERIAL_UART00 $CONFIG_ARCH_CAMELOT + dep_bool ' Support for console on Excalibur serial port' CONFIG_SERIAL_UART00_CONSOLE $CONFIG_SERIAL_UART00 + + + dep_bool 'SA1100 serial port support' CONFIG_SERIAL_SA1100 $CONFIG_ARCH_SA1100 + dep_bool ' Console on SA1100 serial port' CONFIG_SERIAL_SA1100_CONSOLE $CONFIG_SERIAL_SA1100 + if [ "$CONFIG_SERIAL_SA1100" = "y" ]; then + int ' Default SA1100 serial baudrate' CONFIG_SA1100_DEFAULT_BAUDRATE 9600 + fi + + dep_tristate 'ARM Omaha serial port support' CONFIG_SERIAL_OMAHA $CONFIG_ARCH_OMAHA + dep_bool ' Support for console on Omaha serial port' CONFIG_SERIAL_OMAHA_CONSOLE $CONFIG_SERIAL_OMAHA + + dep_tristate 'AT91RM9200 serial port support' CONFIG_SERIAL_AT91 $CONFIG_ARCH_AT91RM9200 + dep_bool ' Console on AT91RM9200 serial port' CONFIG_SERIAL_AT91_CONSOLE $CONFIG_SERIAL_AT91 + +fi +# +# The new 8250/16550 serial drivers +dep_tristate '8250/16550 and compatible serial support (EXPERIMENTAL)' CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL +dep_bool ' Console on 8250/16550 and compatible serial port (EXPERIMENTAL)' CONFIG_SERIAL_8250_CONSOLE $CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL + +dep_mbool 'Extended 8250/16550 serial driver options' CONFIG_SERIAL_8250_EXTENDED $CONFIG_SERIAL_8250 +dep_bool ' Support more than 4 serial ports' CONFIG_SERIAL_8250_MANY_PORTS $CONFIG_SERIAL_8250_EXTENDED +dep_bool ' Support for sharing serial interrupts' CONFIG_SERIAL_8250_SHARE_IRQ $CONFIG_SERIAL_8250_EXTENDED +dep_bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_8250_DETECT_IRQ $CONFIG_SERIAL_8250_EXTENDED +dep_bool ' Support special multiport boards' CONFIG_SERIAL_8250_MULTIPORT $CONFIG_SERIAL_8250_EXTENDED +dep_bool ' Support Bell Technologies HUB6 card' CONFIG_SERIAL_8250_HUB6 $CONFIG_SERIAL_8250_EXTENDED + +if [ "$CONFIG_SERIAL_AMBA" = "y" -o \ + "$CONFIG_SERIAL_CLPS711X" = "y" -o \ + "$CONFIG_SERIAL_SA1100" = "y" -o \ + "$CONFIG_SERIAL_ANAKIN" = "y" -o \ + "$CONFIG_SERIAL_UART00" = "y" -o \ + "$CONFIG_SERIAL_8250" = "y" -o \ + "$CONFIG_SERIAL_OMAHA" = "y" -o \ + "$CONFIG_SERIAL_AT91" = "y" ]; then + define_bool CONFIG_SERIAL_CORE y +else + if [ "$CONFIG_SERIAL_AMBA" = "m" -o \ + "$CONFIG_SERIAL_CLPS711X" = "m" -o \ + "$CONFIG_SERIAL_SA1100" = "m" -o \ + "$CONFIG_SERIAL_ANAKIN" = "m" -o \ + "$CONFIG_SERIAL_UART00" = "m" -o \ + "$CONFIG_SERIAL_8250" = "m" -o \ + "$CONFIG_SERIAL_OMAHA" = "m" -o \ + "$CONFIG_SERIAL_AT91" = "m" ]; then + define_bool CONFIG_SERIAL_CORE m + fi +fi +if [ "$CONFIG_SERIAL_AMBA_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_CLPS711X_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_SA1100_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_ANAKIN_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_UART00_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_8250_CONSOLE" = "y" -o \ + "$CONFIG_SERIAL_OMAHA" = "y" -o \ + "$CONFIG_SERIAL_AT91_CONSOLE" = "y" ]; then + define_bool CONFIG_SERIAL_CORE_CONSOLE y +fi + +endmenu --- /dev/null +++ linux-2.4.27/drivers/serial/Makefile @@ -0,0 +1,39 @@ +# +# Makefile for the kernel serial device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# +# $Id: Makefile,v 1.2 2001/10/12 15:46:58 rmk Exp $ +# + +O_TARGET := serial.o + +export-objs := core.o 8250.o +obj-y := +obj-m := +obj-n := +obj- := + +serial-8250-y := +serial-8250-$(CONFIG_PCI) += 8250_pci.o +serial-8250-$(CONFIG_ISAPNP) += 8250_pnp.o +obj-$(CONFIG_SERIAL_CORE) += core.o +obj-$(CONFIG_SERIAL_21285) += 21285.o +obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) +obj-$(CONFIG_SERIAL_ANAKIN) += anakin.o +obj-$(CONFIG_SERIAL_AMBA) += amba.o +obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o +obj-$(CONFIG_SERIAL_SA1100) += sa1100.o +obj-$(CONFIG_SERIAL_UART00) += uart00.o +obj-$(CONFIG_SERIAL_OMAHA) += omaha.o +obj-$(CONFIG_SERIAL_AT91US3) += at91us3.o + +include $(TOPDIR)/Rules.make + +fastdep: + --- /dev/null +++ linux-2.4.27/drivers/serial/amba.c @@ -0,0 +1,770 @@ +/* + * linux/drivers/char/serial_amba.c + * + * Driver for AMBA serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: amba.c,v 1.9.2.2 2002/10/24 09:53:25 rmk Exp $ + * + * This is a generic driver for ARM AMBA-type serial ports. They + * have a lot of 16550-like features, but are not register compatable. + * Note that although they do have CTS, DCD and DSR inputs, they do + * not have an RI input, nor do they have DTR or RTS outputs. If + * required, these have to be supplied via some other means (eg, GPIO) + * and hooked into this driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#include + +#define UART_NR 2 + +#define SERIAL_AMBA_MAJOR 204 +#define SERIAL_AMBA_MINOR 16 +#define SERIAL_AMBA_NR UART_NR + +#define CALLOUT_AMBA_NAME "cuaam" +#define CALLOUT_AMBA_MAJOR 205 +#define CALLOUT_AMBA_MINOR 16 +#define CALLOUT_AMBA_NR UART_NR + +static struct tty_driver normal, callout; +static struct tty_struct *amba_table[UART_NR]; +static struct termios *amba_termios[UART_NR], *amba_termios_locked[UART_NR]; +#ifdef SUPPORT_SYSRQ +static struct console amba_console; +#endif + +#define AMBA_ISR_PASS_LIMIT 256 + +/* + * Access macros for the AMBA UARTs + */ +#define UART_GET_INT_STATUS(p) readb((p)->membase + AMBA_UARTIIR) +#define UART_PUT_ICR(p, c) writel((c), (p)->membase + AMBA_UARTICR) +#define UART_GET_FR(p) readb((p)->membase + AMBA_UARTFR) +#define UART_GET_CHAR(p) readb((p)->membase + AMBA_UARTDR) +#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + AMBA_UARTDR) +#define UART_GET_RSR(p) readb((p)->membase + AMBA_UARTRSR) +#define UART_GET_CR(p) readb((p)->membase + AMBA_UARTCR) +#define UART_PUT_CR(p,c) writel((c), (p)->membase + AMBA_UARTCR) +#define UART_GET_LCRL(p) readb((p)->membase + AMBA_UARTLCR_L) +#define UART_PUT_LCRL(p,c) writel((c), (p)->membase + AMBA_UARTLCR_L) +#define UART_GET_LCRM(p) readb((p)->membase + AMBA_UARTLCR_M) +#define UART_PUT_LCRM(p,c) writel((c), (p)->membase + AMBA_UARTLCR_M) +#define UART_GET_LCRH(p) readb((p)->membase + AMBA_UARTLCR_H) +#define UART_PUT_LCRH(p,c) writel((c), (p)->membase + AMBA_UARTLCR_H) +#define UART_RX_DATA(s) (((s) & AMBA_UARTFR_RXFE) == 0) +#define UART_TX_READY(s) (((s) & AMBA_UARTFR_TXFF) == 0) +#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0) + +#define UART_DUMMY_RSR_RX 256 +#define UART_PORT_SIZE 64 + +/* + * On the Integrator platform, the port RTS and DTR are provided by + * bits in the following SC_CTRLS register bits: + * RTS DTR + * UART0 7 6 + * UART1 5 4 + */ +#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) + +/* + * We wrap our port structure around the generic uart_port. + */ +struct uart_amba_port { + struct uart_port port; + unsigned int dtr_mask; + unsigned int rts_mask; + unsigned int old_status; +}; + +static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + unsigned int cr; + + cr = UART_GET_CR(port); + cr &= ~AMBA_UARTCR_TIE; + UART_PUT_CR(port, cr); +} + +static void ambauart_start_tx(struct uart_port *port, unsigned int tty_start) +{ + unsigned int cr; + + cr = UART_GET_CR(port); + cr |= AMBA_UARTCR_TIE; + UART_PUT_CR(port, cr); +} + +static void ambauart_stop_rx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CR(port); + cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE); + UART_PUT_CR(port, cr); +} + +static void ambauart_enable_ms(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CR(port); + cr |= AMBA_UARTCR_MSIE; + UART_PUT_CR(port, cr); +} + +static void +#ifdef SUPPORT_SYSRQ +ambauart_rx_chars(struct uart_port *port, struct pt_regs *regs) +#else +ambauart_rx_chars(struct uart_port *port) +#endif +{ + struct tty_struct *tty = port->info->tty; + unsigned int status, ch, rsr, max_count = 256; + + status = UART_GET_FR(port); + while (UART_RX_DATA(status) && max_count--) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_WARNING "TTY_DONT_FLIP set\n"); + return; + } + } + + ch = UART_GET_CHAR(port); + + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + port->icount.rx++; + + /* + * Note that the error handling code is + * out of the main execution path + */ + rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX; + if (rsr & AMBA_UARTRSR_ANY) { + if (rsr & AMBA_UARTRSR_BE) { + rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } else if (rsr & AMBA_UARTRSR_PE) + port->icount.parity++; + else if (rsr & AMBA_UARTRSR_FE) + port->icount.frame++; + if (rsr & AMBA_UARTRSR_OE) + port->icount.overrun++; + + rsr &= port->read_status_mask; + + if (rsr & AMBA_UARTRSR_BE) + *tty->flip.flag_buf_ptr = TTY_BREAK; + else if (rsr & AMBA_UARTRSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (rsr & AMBA_UARTRSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch, regs)) + goto ignore_char; + + if ((rsr & port->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((rsr & AMBA_UARTRSR_OE) && + tty->flip.count < TTY_FLIPBUF_SIZE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character + */ + *tty->flip.char_buf_ptr++ = 0; + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + tty->flip.count++; + } + ignore_char: + status = UART_GET_FR(port); + } + tty_flip_buffer_push(tty); + return; +} + +static void ambauart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + ambauart_stop_tx(port, 0); + return; + } + + count = port->fifosize >> 1; + do { + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + ambauart_stop_tx(port, 0); +} + +static void ambauart_modem_status(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int status, delta; + + UART_PUT_ICR(&uap->port, 0); + + status = UART_GET_FR(&uap->port) & AMBA_UARTFR_MODEM_ANY; + + delta = status ^ uap->old_status; + uap->old_status = status; + + if (!delta) + return; + + if (delta & AMBA_UARTFR_DCD) + uart_handle_dcd_change(&uap->port, status & AMBA_UARTFR_DCD); + + if (delta & AMBA_UARTFR_DSR) + uap->port.icount.dsr++; + + if (delta & AMBA_UARTFR_CTS) + uart_handle_cts_change(&uap->port, status & AMBA_UARTFR_CTS); + + wake_up_interruptible(&uap->port.info->delta_msr_wait); +} + +static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + + status = UART_GET_INT_STATUS(port); + do { + if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS)) +#ifdef SUPPORT_SYSRQ + ambauart_rx_chars(port, regs); +#else + ambauart_rx_chars(port); +#endif + if (status & AMBA_UARTIIR_TIS) + ambauart_tx_chars(port); + if (status & AMBA_UARTIIR_MIS) + ambauart_modem_status(port); + + if (pass_counter-- == 0) + break; + + status = UART_GET_INT_STATUS(port); + } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | + AMBA_UARTIIR_TIS)); +} + +static unsigned int ambauart_tx_empty(struct uart_port *port) +{ + return UART_GET_FR(port) & AMBA_UARTFR_BUSY ? 0 : TIOCSER_TEMT; +} + +static unsigned int ambauart_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int status; + + status = UART_GET_FR(port); + if (status & AMBA_UARTFR_DCD) + result |= TIOCM_CAR; + if (status & AMBA_UARTFR_DSR) + result |= TIOCM_DSR; + if (status & AMBA_UARTFR_CTS) + result |= TIOCM_CTS; + + return result; +} + +static void ambauart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int ctrls = 0, ctrlc = 0; + + if (mctrl & TIOCM_RTS) + ctrlc |= uap->rts_mask; + else + ctrls |= uap->rts_mask; + + if (mctrl & TIOCM_DTR) + ctrlc |= uap->dtr_mask; + else + ctrls |= uap->dtr_mask; + + __raw_writel(ctrls, SC_CTRLS); + __raw_writel(ctrlc, SC_CTRLC); +} + +static void ambauart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int lcr_h; + + spin_lock_irqsave(&port->lock, flags); + lcr_h = UART_GET_LCRH(port); + if (break_state == -1) + lcr_h |= AMBA_UARTLCR_H_BRK; + else + lcr_h &= ~AMBA_UARTLCR_H_BRK; + UART_PUT_LCRH(port, lcr_h); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int ambauart_startup(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, ambauart_int, 0, "amba", port); + if (retval) + return retval; + + /* + * initialise the old status of the modem signals + */ + uap->old_status = UART_GET_FR(port) & AMBA_UARTFR_MODEM_ANY; + + /* + * Finally, enable interrupts + */ + UART_PUT_CR(port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE | + AMBA_UARTCR_RTIE); + + return 0; +} + +static void ambauart_shutdown(struct uart_port *port) +{ + /* + * Free the interrupt + */ + free_irq(port->irq, port); + + /* + * disable all interrupts, disable the port + */ + UART_PUT_CR(port, 0); + + /* disable break condition and fifos */ + UART_PUT_LCRH(port, UART_GET_LCRH(port) & + ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN)); +} + +static void ambauart_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) +{ + unsigned int lcr_h, old_cr; + unsigned long flags; + +#if DEBUG + printk("ambauart_set_cflag(0x%x) called\n", cflag); +#endif + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + lcr_h = AMBA_UARTLCR_H_WLEN_5; + break; + case CS6: + lcr_h = AMBA_UARTLCR_H_WLEN_6; + break; + case CS7: + lcr_h = AMBA_UARTLCR_H_WLEN_7; + break; + default: // CS8 + lcr_h = AMBA_UARTLCR_H_WLEN_8; + break; + } + if (cflag & CSTOPB) + lcr_h |= AMBA_UARTLCR_H_STP2; + if (cflag & PARENB) { + lcr_h |= AMBA_UARTLCR_H_PEN; + if (!(cflag & PARODD)) + lcr_h |= AMBA_UARTLCR_H_EPS; + } + if (port->fifosize > 1) + lcr_h |= AMBA_UARTLCR_H_FEN; + + spin_lock_irqsave(&port->lock, flags); + + port->read_status_mask = AMBA_UARTRSR_OE; + if (iflag & INPCK) + port->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= AMBA_UARTRSR_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; + if (iflag & IGNBRK) { + port->ignore_status_mask |= AMBA_UARTRSR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + port->ignore_status_mask |= AMBA_UARTRSR_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_RSR_RX; + + old_cr = UART_GET_CR(port) & ~AMBA_UARTCR_MSIE; + + if (UART_ENABLE_MS(port, cflag)) + old_cr |= AMBA_UARTCR_MSIE; + + UART_PUT_CR(port, 0); + + /* Set baud rate */ + quot -= 1; + UART_PUT_LCRM(port, ((quot & 0xf00) >> 8)); + UART_PUT_LCRL(port, (quot & 0xff)); + + /* + * ----------v----------v----------v----------v----- + * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L + * ----------^----------^----------^----------^----- + */ + UART_PUT_LCRH(port, lcr_h); + UART_PUT_CR(port, old_cr); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *ambauart_type(struct uart_port *port) +{ + return port->type == PORT_AMBA ? "AMBA" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void ambauart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int ambauart_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_amba") + != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void ambauart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_AMBA; + ambauart_request_port(port); + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int ambauart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops amba_pops = { + .tx_empty = ambauart_tx_empty, + .set_mctrl = ambauart_set_mctrl, + .get_mctrl = ambauart_get_mctrl, + .stop_tx = ambauart_stop_tx, + .start_tx = ambauart_start_tx, + .stop_rx = ambauart_stop_rx, + .enable_ms = ambauart_enable_ms, + .break_ctl = ambauart_break_ctl, + .startup = ambauart_startup, + .shutdown = ambauart_shutdown, + .change_speed = ambauart_change_speed, + .type = ambauart_type, + .release_port = ambauart_release_port, + .request_port = ambauart_request_port, + .config_port = ambauart_config_port, + .verify_port = ambauart_verify_port, +}; + +static struct uart_amba_port amba_ports[UART_NR] = { + { + .port = { + .membase = (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE), + .mapbase = INTEGRATOR_UART0_BASE, + .iotype = SERIAL_IO_MEM, + .irq = IRQ_UARTINT0, + .uartclk = 14745600, + .fifosize = 16, + .ops = &amba_pops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, + .dtr_mask = 1 << 5, + .rts_mask = 1 << 4, + }, + { + .port = { + .membase = (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE), + .mapbase = INTEGRATOR_UART1_BASE, + .iotype = SERIAL_IO_MEM, + .irq = IRQ_UARTINT1, + .uartclk = 14745600, + .fifosize = 16, + .ops = &amba_pops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 1, + }, + .dtr_mask = 1 << 7, + .rts_mask = 1 << 6, + } +}; + +#ifdef CONFIG_SERIAL_AMBA_CONSOLE + +static void ambauart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &amba_ports[co->index].port; + unsigned int status, old_cr; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_cr = UART_GET_CR(port); + UART_PUT_CR(port, AMBA_UARTCR_UARTEN); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_FR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_FR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = UART_GET_FR(port); + } while (status & AMBA_UARTFR_BUSY); + UART_PUT_CR(port, old_cr); +} + +static kdev_t ambauart_console_device(struct console *co) +{ + return MKDEV(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + co->index); +} + +static void __init +ambauart_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + if (UART_GET_CR(port) & AMBA_UARTCR_UARTEN) { + unsigned int lcr_h, quot; + lcr_h = UART_GET_LCRH(port); + + *parity = 'n'; + if (lcr_h & AMBA_UARTLCR_H_PEN) { + if (lcr_h & AMBA_UARTLCR_H_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & 0x60) == AMBA_UARTLCR_H_WLEN_7) + *bits = 7; + else + *bits = 8; + + quot = UART_GET_LCRL(port) | UART_GET_LCRM(port) << 8; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init ambauart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &amba_ports[co->index].port; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + ambauart_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console amba_console = { + .name = "ttyAM", + .write = ambauart_console_write, + .device = ambauart_console_device, + .setup = ambauart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init ambauart_console_init(void) +{ + register_console(&amba_console); +} + +#define AMBA_CONSOLE &amba_console +#else +#define AMBA_CONSOLE NULL +#endif + +static struct uart_driver amba_reg = { + .owner = THIS_MODULE, + .normal_major = SERIAL_AMBA_MAJOR, +#ifdef CONFIG_DEVFS_FS + .normal_name = "ttyAM%d", + .callout_name = "cuaam%d", +#else + .normal_name = "ttyAM", + .callout_name = "cuaam", +#endif + .normal_driver = &normal, + .callout_major = CALLOUT_AMBA_MAJOR, + .callout_driver = &callout, + .table = amba_table, + .termios = amba_termios, + .termios_locked = amba_termios_locked, + .minor = SERIAL_AMBA_MINOR, + .nr = UART_NR, + .cons = AMBA_CONSOLE, +}; + +static int __init ambauart_init(void) +{ + int ret; + + ret = uart_register_driver(&amba_reg); + if (ret == 0) { + int i; + + for (i = 0; i < UART_NR; i++) + uart_add_one_port(&amba_reg, &amba_ports[i].port); + } + return ret; +} + +static void __exit ambauart_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&amba_reg, &amba_ports[i].port); + + uart_unregister_driver(&amba_reg); +} + +module_init(ambauart_init); +module_exit(ambauart_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("ARM AMBA serial port driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/serial/anakin.c @@ -0,0 +1,545 @@ +/* + * linux/drivers/char/serial_anakin.c + * + * Based on driver for AMBA serial ports, by ARM Limited, + * Deep Blue Solutions Ltd., Linus Torvalds and Theodore Ts'o. + * + * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. + * + * Copyright (C) 2001 Blue Mug, Inc. for Acunia N.V. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 20-Apr-2001 TTC Created + * 05-May-2001 W/TTC Updated for serial_core.c + * 27-Jun-2001 jonm Minor changes; add mctrl support, switch to + * SA_INTERRUPT. Works reliably now. No longer requires + * changes to the serial_core API. + * + * $Id: anakin.c,v 1.5.2.2 2002/10/24 09:53:25 rmk Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#define UART_NR 5 + +#define SERIAL_ANAKIN_NAME "ttyAN" +#define SERIAL_ANAKIN_MAJOR 204 +#define SERIAL_ANAKIN_MINOR 32 + +#define CALLOUT_ANAKIN_NAME "cuaan" +#define CALLOUT_ANAKIN_MAJOR 205 +#define CALLOUT_ANAKIN_MINOR 32 + +static struct tty_driver normal, callout; +static struct tty_struct *anakin_table[UART_NR]; +static struct termios *anakin_termios[UART_NR], *anakin_termios_locked[UART_NR]; +static struct uart_state anakin_state[UART_NR]; +static u_int txenable[NR_IRQS]; /* Software interrupt register */ + +static inline unsigned int +anakin_in(struct uart_port *port, u_int offset) +{ + return __raw_readl(port->base + offset); +} + +static inline void +anakin_out(struct uart_port *port, u_int offset, unsigned int value) +{ + __raw_writel(value, port->base + offset); +} + +static void +anakin_stop_tx(struct uart_port *port, u_int from_tty) +{ + txenable[port->irq] = 0; +} + +static inline void +anakin_transmit_buffer(struct uart_info *info) +{ + struct uart_port *port = info->port; + + while (!(anakin_in(port, 0x10) & TXEMPTY)); + anakin_out(port, 0x14, info->xmit.buf[info->xmit.tail]); + anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); + info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE-1); + info->state->icount.tx++; + + if (info->xmit.head == info->xmit.tail) + anakin_stop_tx(port, 0); +} + +static inline void +anakin_transmit_x_char(struct uart_info *info) +{ + struct uart_port *port = info->port; + + anakin_out(port, 0x14, info->x_char); + anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); + info->state->icount.tx++; + info->x_char = 0; +} + +static void +anakin_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) +{ + unsigned int flags; + + save_flags_cli(flags); + + // is it this... or below: if (nonempty + if (!txenable[port->irq]) { + txenable[port->irq] = TXENABLE; + + if ((anakin_in(port, 0x10) & TXEMPTY) && nonempty) { + anakin_transmit_buffer((struct uart_info*)port->unused); + } + } + + restore_flags(flags); +} + +static void +anakin_stop_rx(struct uart_port *port) +{ + unsigned long flags; + + save_flags_cli(flags); + while (anakin_in(port, 0x10) & RXRELEASE) + anakin_in(port, 0x14); + anakin_out(port, 0x18, anakin_in(port, 0x18) | BLOCKRX); + restore_flags(flags); +} + +static void +anakin_enable_ms(struct uart_port *port) +{ +} + +static inline void +anakin_rx_chars(struct uart_info *info) +{ + unsigned int ch; + struct tty_struct *tty = info->tty; + + if (!(anakin_in(info->port, 0x10) & RXRELEASE)) + return; + + ch = anakin_in(info->port, 0x14) & 0xff; + + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.char_buf_ptr++ = ch; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + info->state->icount.rx++; + tty->flip.count++; + } + tty_flip_buffer_push(tty); +} + +static inline void +anakin_overrun_chars(struct uart_info *info) +{ + unsigned int ch; + + ch = anakin_in(info->port, 0x14); + info->state->icount.overrun++; +} + +static inline void +anakin_tx_chars(struct uart_info *info) +{ + if (info->x_char) { + anakin_transmit_x_char(info); + return; + } + + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + anakin_stop_tx(info->port, 0); + return; + } + + anakin_transmit_buffer(info); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + UART_XMIT_SIZE) < WAKEUP_CHARS) + uart_event(info, EVT_WRITE_WAKEUP); +} + +static void +anakin_int(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int status; + struct uart_info *info = dev_id; + + status = anakin_in(info->port, 0x1c); + + if (status & RX) + anakin_rx_chars(info); + + if (status & OVERRUN) + anakin_overrun_chars(info); + + if (txenable[info->port->irq] && (status & TX)) + anakin_tx_chars(info); +} + +static u_int +anakin_tx_empty(struct uart_port *port) +{ + return anakin_in(port, 0x10) & TXEMPTY ? TIOCSER_TEMT : 0; +} + +static u_int +anakin_get_mctrl(struct uart_port *port) +{ + unsigned int status = 0; + + status |= (anakin_in(port, 0x10) & CTS ? TIOCM_CTS : 0); + status |= (anakin_in(port, 0x18) & DCD ? TIOCM_CAR : 0); + status |= (anakin_in(port, 0x18) & DTR ? TIOCM_DTR : 0); + status |= (anakin_in(port, 0x18) & RTS ? TIOCM_RTS : 0); + + return status; +} + +static void +anakin_set_mctrl(struct uart_port *port, u_int mctrl) +{ + unsigned int status; + + status = anakin_in(port, 0x18); + + if (mctrl & TIOCM_RTS) + status |= RTS; + else + status &= ~RTS; + + if (mctrl & TIOCM_CAR) + status |= DCD; + else + status &= ~DCD; + + anakin_out(port, 0x18, status); +} + +static void +anakin_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int status; + + status = anakin_in(port, 0x20); + + if (break_state == -1) + status |= SETBREAK; + else + status &= ~SETBREAK; + + anakin_out(port, 0x20, status); +} + +static int +anakin_startup(struct uart_port *port, struct uart_info *info) +{ + int retval; + unsigned int read,write; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, anakin_int, SA_INTERRUPT, "serial_anakin", info); + if (retval) + return retval; + + port->ops->set_mctrl(port, info->mctrl); + + /* + * initialise the old status of the modem signals + */ + port->old_status = 0; + + /* + * Finally, disable IRQ and softIRQs for first byte) + */ + txenable[port->irq] = 0; + read = anakin_in(port, 0x18); + write = (read & ~(RTS | DTR | BLOCKRX)) | IRQENABLE; + anakin_out(port, 0x18, write); + + /* Store the uart_info pointer so we can reference it in + * anakin_start_tx() */ + port->unused = (u_int)info; + + return 0; +} + +static void +anakin_shutdown(struct uart_port *port, struct uart_info *info) +{ + /* + * Free the interrupt + */ + free_irq(port->irq, info); + + /* + * disable all interrupts, disable the port + */ + anakin_out(port, 0x18, anakin_in(port, 0x18) & ~IRQENABLE); +} + +static void +anakin_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) +{ + unsigned int flags; + + save_flags_cli(flags); + while (!(anakin_in(port, 0x10) & TXEMPTY)); + anakin_out(port, 0x10, (anakin_in(port, 0x10) & ~PRESCALER) + | (quot << 3)); + + //parity always set to none + anakin_out(port, 0x18, anakin_in(port, 0x18) & ~PARITY); + restore_flags(flags); +} + +static const char *anakin_type(struct port *port) +{ + return port->type == PORT_ANAKIN ? "ANAKIN" : NULL; +} + +static struct uart_ops anakin_pops = { + tx_empty: anakin_tx_empty, + set_mctrl: anakin_set_mctrl, + get_mctrl: anakin_get_mctrl, + stop_tx: anakin_stop_tx, + start_tx: anakin_start_tx, + stop_rx: anakin_stop_rx, + enable_ms: anakin_enable_ms, + break_ctl: anakin_break_ctl, + startup: anakin_startup, + shutdown: anakin_shutdown, + change_speed: anakin_change_speed, + type: anakin_type, +}; + +static struct uart_port anakin_ports[UART_NR] = { + { + base: IO_BASE + UART0, + irq: IRQ_UART0, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + }, + { + base: IO_BASE + UART1, + irq: IRQ_UART1, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + }, + { + base: IO_BASE + UART2, + irq: IRQ_UART2, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + }, + { + base: IO_BASE + UART3, + irq: IRQ_UART3, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + }, + { + base: IO_BASE + UART4, + irq: IRQ_UART4, + uartclk: 3686400, + fifosize: 0, + ops: &anakin_pops, + }, +}; + + +#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE + +static void +anakin_console_write(struct console *co, const char *s, u_int count) +{ + struct uart_port *port = anakin_ports + co->index; + unsigned int flags, status, i; + + /* + * First save the status then disable the interrupts + */ + save_flags_cli(flags); + status = anakin_in(port, 0x18); + anakin_out(port, 0x18, status & ~IRQENABLE); + restore_flags(flags); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + while (!(anakin_in(port, 0x10) & TXEMPTY)); + + /* + * Send the character out. + * If a LF, also do CR... + */ + anakin_out(port, 0x14, *s); + anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); + + if (*s == 10) { + while (!(anakin_in(port, 0x10) & TXEMPTY)); + anakin_out(port, 0x14, 13); + anakin_out(port, 0x18, anakin_in(port, 0x18) + | SENDREQUEST); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the interrupts + */ + while (!(anakin_in(port, 0x10) & TXEMPTY)); + + if (status & IRQENABLE) + save_flags_cli(flags); + anakin_out(port, 0x18, anakin_in(port, 0x18) | IRQENABLE); + restore_flags(flags); +} + +static kdev_t +anakin_console_device(struct console *co) +{ + return MKDEV(SERIAL_ANAKIN_MAJOR, SERIAL_ANAKIN_MINOR + co->index); +} + +/* + * Read the current UART setup. + */ +static void __init +anakin_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + int paritycode; + + *baud = GETBAUD (anakin_in(port, 0x10) & PRESCALER); + paritycode = GETPARITY(anakin_in(port, 0x18) & PARITY); + switch (paritycode) { + case NONEPARITY: *parity = 'n'; break; + case ODDPARITY: *parity = 'o'; break; + case EVENPARITY: *parity = 'e'; break; + } + *bits = 8; +} + +static int __init +anakin_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = CONFIG_ANAKIN_DEFAULT_BAUDRATE; + int bits = 8; + int parity = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + port = uart_get_console(anakin_ports, UART_NR, co); + + if (options) + uart_parse_options(options, &baud, &parity, &bits); + else + anakin_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits); +} + +static struct console anakin_console = { + name: SERIAL_ANAKIN_NAME, + write: anakin_console_write, + device: anakin_console_device, + setup: anakin_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init +anakin_console_init(void) +{ + register_console(&anakin_console); +} + +#define ANAKIN_CONSOLE &anakin_console +#else +#define ANAKIN_CONSOLE NULL +#endif + +static struct uart_register anakin_reg = { + normal_major: SERIAL_ANAKIN_MAJOR, + normal_name: SERIAL_ANAKIN_NAME, + normal_driver: &normal, + callout_major: CALLOUT_ANAKIN_MAJOR, + callout_name: CALLOUT_ANAKIN_NAME, + callout_driver: &callout, + table: anakin_table, + termios: anakin_termios, + termios_locked: anakin_termios_locked, + minor: SERIAL_ANAKIN_MINOR, + nr: UART_NR, + state: anakin_state, + port: anakin_ports, + cons: ANAKIN_CONSOLE, +}; + +static int __init +anakin_init(void) +{ + return uart_register_port(&anakin_reg); +} + +__initcall(anakin_init); + +MODULE_DESCRIPTION("Anakin serial driver"); +MODULE_AUTHOR("Tak-Shing Chan "); +MODULE_SUPPORTED_DEVICE("ttyAN"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/serial/clps711x.c @@ -0,0 +1,635 @@ +/* + * linux/drivers/char/serial_clps711x.c + * + * Driver for CLPS711x serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: clps711x.c,v 1.12.2.2 2002/10/24 09:53:25 rmk Exp $ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define UART_NR 2 + +#define SERIAL_CLPS711X_NAME "ttyAM" +#define SERIAL_CLPS711X_MAJOR 204 +#define SERIAL_CLPS711X_MINOR 16 +#define SERIAL_CLPS711X_NR UART_NR + +#define CALLOUT_CLPS711X_NAME "cuaam" +#define CALLOUT_CLPS711X_MAJOR 205 +#define CALLOUT_CLPS711X_MINOR 16 +#define CALLOUT_CLPS711X_NR UART_NR + +static struct tty_driver normal, callout; +static struct tty_struct *clps711x_table[UART_NR]; +static struct termios *clps711x_termios[UART_NR], *clps711x_termios_locked[UART_NR]; + +/* + * We use the relevant SYSCON register as a base address for these ports. + */ +#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1) +#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1) +#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1) +#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1) + +#define TX_IRQ(port) ((port)->irq) +#define RX_IRQ(port) ((port)->irq + 1) + +#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) + +#define tx_enabled(port) ((port)->unused[0]) + +static void +clps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + if (tx_enabled(port)) { + disable_irq(TX_IRQ(port)); + tx_enabled(port) = 0; + } +} + +static void +clps711xuart_start_tx(struct uart_port *port, unsigned int tty_start) +{ + if (!tx_enabled(port)) { + enable_irq(TX_IRQ(port)); + tx_enabled(port) = 1; + } +} + +static void clps711xuart_stop_rx(struct uart_port *port) +{ + disable_irq(RX_IRQ(port)); +} + +static void clps711xuart_enable_ms(struct uart_port *port) +{ +} + +static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + struct tty_struct *tty = port->info->tty; + unsigned int status, ch, flg, ignored = 0; + + status = clps_readl(SYSFLG(port)); + while (!(status & SYSFLG_URXFE)) { + ch = clps_readl(UARTDR(port)); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + port->icount.rx++; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + if (ch & UART_ANY_ERR) + goto handle_error; + + if (uart_handle_sysrq_char(port, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = clps_readl(SYSFLG(port)); + } + out: + tty_flip_buffer_push(tty); + return; + + handle_error: + if (ch & UARTDR_PARERR) + port->icount.parity++; + else if (ch & UARTDR_FRMERR) + port->icount.frame++; + if (ch & UARTDR_OVERR) + port->icount.overrun++; + + if (ch & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + ch &= port->read_status_mask; + + if (ch & UARTDR_PARERR) + flg = TTY_PARITY; + else if (ch & UARTDR_FRMERR) + flg = TTY_FRAME; + + if (ch & UARTDR_OVERR) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + clps_writel(port->x_char, UARTDR(port)); + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + clps711xuart_stop_tx(port, 0); + return; + } + + count = port->fifosize >> 1; + do { + clps_writel(xmit->buf[xmit->tail], UARTDR(port)); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + clps711xuart_stop_tx(port, 0); +} + +static unsigned int clps711xuart_tx_empty(struct uart_port *port) +{ + unsigned int status = clps_readl(SYSFLG(port)); + return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; +} + +static unsigned int clps711xuart_get_mctrl(struct uart_port *port) +{ + unsigned int port_addr; + unsigned int result = 0; + unsigned int status; + + port_addr = SYSFLG(port); + if (port_addr == SYSFLG1) { + status = clps_readl(SYSFLG1); + if (status & SYSFLG1_DCD) + result |= TIOCM_CAR; + if (status & SYSFLG1_DSR) + result |= TIOCM_DSR; + if (status & SYSFLG1_CTS) + result |= TIOCM_CTS; + } + + return result; +} + +static void +clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) +{ +} + +static void clps711xuart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int ubrlcr; + + spin_lock_irqsave(&port->lock, flags); + ubrlcr = clps_readl(UBRLCR(port)); + if (break_state == -1) + ubrlcr |= UBRLCR_BREAK; + else + ubrlcr &= ~UBRLCR_BREAK; + clps_writel(ubrlcr, UBRLCR(port)); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int clps711xuart_startup(struct uart_port *port) +{ + unsigned int syscon; + int retval; + + tx_enabled(port) = 1; + + /* + * Allocate the IRQs + */ + retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0, + "clps711xuart_tx", port); + if (retval) + return retval; + + retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0, + "clps711xuart_rx", port); + if (retval) { + free_irq(TX_IRQ(port), port); + return retval; + } + + /* + * enable the port + */ + syscon = clps_readl(SYSCON(port)); + syscon |= SYSCON_UARTEN; + clps_writel(syscon, SYSCON(port)); + + return 0; +} + +static void clps711xuart_shutdown(struct uart_port *port) +{ + unsigned int ubrlcr, syscon; + + /* + * Free the interrupt + */ + free_irq(TX_IRQ(port), port); /* TX interrupt */ + free_irq(RX_IRQ(port), port); /* RX interrupt */ + + /* + * disable the port + */ + syscon = clps_readl(SYSCON(port)); + syscon &= ~SYSCON_UARTEN; + clps_writel(syscon, SYSCON(port)); + + /* + * disable break condition and fifos + */ + ubrlcr = clps_readl(UBRLCR(port)); + ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK); + clps_writel(ubrlcr, UBRLCR(port)); +} + +static void clps711xuart_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) +{ + unsigned int ubrlcr; + unsigned long flags; + +#if DEBUG + printk("clps711xuart_change_speed(cflag=0x%x, iflag=0x%x, quot=%d) called\n", + cflag, iflag, quot); +#endif + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + ubrlcr = UBRLCR_WRDLEN5; + break; + case CS6: + ubrlcr = UBRLCR_WRDLEN6; + break; + case CS7: + ubrlcr = UBRLCR_WRDLEN7; + break; + default: // CS8 + ubrlcr = UBRLCR_WRDLEN8; + break; + } + if (cflag & CSTOPB) + ubrlcr |= UBRLCR_XSTOP; + if (cflag & PARENB) { + ubrlcr |= UBRLCR_PRTEN; + if (!(cflag & PARODD)) + ubrlcr |= UBRLCR_EVENPRT; + } + if (port->fifosize > 1) + ubrlcr |= UBRLCR_FIFOEN; + + spin_lock_irqsave(&port->lock, flags); + + port->read_status_mask = UARTDR_OVERR; + if (iflag & INPCK) + port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR; + if (iflag & IGNBRK) { + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (iflag & IGNPAR) + port->ignore_status_mask |= UARTDR_OVERR; + } + + quot -= 1; + + clps_writel(ubrlcr | quot, UBRLCR(port)); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *clps711xuart_type(struct uart_port *port) +{ + return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; +} + +/* + * Configure/autoconfigure the port. + */ +static void clps711xuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_CLPS711X; +} + +static void clps711xuart_release_port(struct uart_port *port) +{ +} + +static int clps711xuart_request_port(struct uart_port *port) +{ + return 0; +} + +static struct uart_ops clps711x_pops = { + .tx_empty = clps711xuart_tx_empty, + .set_mctrl = clps711xuart_set_mctrl_null, + .get_mctrl = clps711xuart_get_mctrl, + .stop_tx = clps711xuart_stop_tx, + .start_tx = clps711xuart_start_tx, + .stop_rx = clps711xuart_stop_rx, + .enable_ms = clps711xuart_enable_ms, + .break_ctl = clps711xuart_break_ctl, + .startup = clps711xuart_startup, + .shutdown = clps711xuart_shutdown, + .change_speed = clps711xuart_change_speed, + .type = clps711xuart_type, + .config_port = clps711xuart_config_port, + .release_port = clps711xuart_release_port, + .request_port = clps711xuart_request_port, +}; + +static struct uart_port clps711x_ports[UART_NR] = { + { + .iobase = SYSCON1, + .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */ + .uartclk = 3686400, + .fifosize = 16, + .ops = &clps711x_pops, + .line = 0, + .flags = ASYNC_BOOT_AUTOCONF, + }, + { + .iobase = SYSCON2, + .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */ + .uartclk = 3686400, + .fifosize = 16, + .ops = &clps711x_pops, + .line = 1, + .flags = ASYNC_BOOT_AUTOCONF, + } +}; + +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + * + * Note that this is called with interrupts already disabled + */ +static void +clps711xuart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = clps711x_ports + co->index; + unsigned int status, syscon; + int i; + + /* + * Ensure that the port is enabled. + */ + syscon = clps_readl(SYSCON(port)); + clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = clps_readl(SYSFLG(port)); + } while (status & SYSFLG_UTXFF); + clps_writel(s[i], UARTDR(port)); + if (s[i] == '\n') { + do { + status = clps_readl(SYSFLG(port)); + } while (status & SYSFLG_UTXFF); + clps_writel('\r', UARTDR(port)); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the uart state. + */ + do { + status = clps_readl(SYSFLG(port)); + } while (status & SYSFLG_UBUSY); + + clps_writel(syscon, SYSCON(port)); +} + +static kdev_t clps711xuart_console_device(struct console *co) +{ + return MKDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR + co->index); +} + +static void __init +clps711xuart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { + unsigned int ubrlcr, quot; + + ubrlcr = clps_readl(UBRLCR(port)); + + *parity = 'n'; + if (ubrlcr & UBRLCR_PRTEN) { + if (ubrlcr & UBRLCR_EVENPRT) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) + *bits = 7; + else + *bits = 8; + + quot = ubrlcr & UBRLCR_BAUD_MASK; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init clps711xuart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + port = uart_get_console(clps711x_ports, UART_NR, co); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + clps711xuart_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console clps711x_console = { + .name = SERIAL_CLPS711X_NAME, + .write = clps711xuart_console_write, + .device = clps711xuart_console_device, + .setup = clps711xuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init clps711xuart_console_init(void) +{ + register_console(&clps711x_console); +} + +#define CLPS711X_CONSOLE &clps711x_console +#else +#define CLPS711X_CONSOLE NULL +#endif + +static struct uart_driver clps711x_reg = { +#ifdef CONFIG_DEVFS_FS + .normal_name = SERIAL_CLPS711X_NAME, + .callout_name = CALLOUT_CLPS711X_NAME, +#else + .normal_name = SERIAL_CLPS711X_NAME, + .callout_name = CALLOUT_CLPS711X_NAME, +#endif + + .normal_major = SERIAL_CLPS711X_MAJOR, + .normal_driver = &normal, + .callout_major = CALLOUT_CLPS711X_MAJOR, + .callout_driver = &callout, + + .table = clps711x_table, + .termios = clps711x_termios, + .termios_locked = clps711x_termios_locked, + + .minor = SERIAL_CLPS711X_MINOR, + .nr = UART_NR, + + .cons = CLPS711X_CONSOLE, +}; + +static int __init clps711xuart_init(void) +{ + int ret, i; + + printk(KERN_INFO "Serial: CLPS711x driver\n"); + + ret = uart_register_driver(&clps711x_reg); + if (ret) + return ret; + + for (i = 0; i < UART_NR; i++) + uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); + + return 0; +} + +static void __exit clps711xuart_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); + + uart_unregister_driver(&clps711x_reg); +} + +module_init(clps711xuart_init); +module_exit(clps711xuart_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("CLPS-711x generic serial driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/serial/core.c @@ -0,0 +1,2584 @@ +/* + * linux/drivers/serial/core.c + * + * Driver core for serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: core.c,v 1.20.2.5 2002/03/13 15:22:26 rmk Exp $ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK(x...) printk(x) +#else +#define DPRINTK(x...) do { } while (0) +#endif + +#ifndef CONFIG_PM +#define pm_access(pm) do { } while (0) +#define pm_unregister(pm) do { } while (0) +#endif + +/* + * This is used to lock changes in serial line configuration. + */ +static DECLARE_MUTEX(port_sem); + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +static void uart_change_speed(struct uart_state *state, struct termios *old_termios); +static void uart_wait_until_sent(struct tty_struct *tty, int timeout); +static void uart_change_pm(struct uart_state *state, int pm_state); + +/* + * This routine is used by the interrupt handler to schedule processing in + * the software interrupt portion of the driver. + */ +void uart_write_wakeup(struct uart_port *port) +{ + struct uart_info *info = port->info; + tasklet_schedule(&info->tlet); +} + +static void uart_stop(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_tx(port, 1); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __uart_start(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + + if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && + !tty->stopped && !tty->hw_stopped) + port->ops->start_tx(port, 1); +} + +static void uart_start(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long flags; + + pm_access(state->pm); + + spin_lock_irqsave(&port->lock, flags); + __uart_start(tty); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void uart_tasklet_action(unsigned long data) +{ + struct uart_state *state = (struct uart_state *)data; + struct tty_struct *tty; + + tty = state->info->tty; + if (tty) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + tty->ldisc.write_wakeup(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +static inline void +uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) +{ + unsigned long flags; + unsigned int old; + + spin_lock_irqsave(&port->lock, flags); + old = port->mctrl; + port->mctrl = (old & ~clear) | set; + if (old != port->mctrl) + port->ops->set_mctrl(port, port->mctrl); + spin_unlock_irqrestore(&port->lock, flags); +} + +#define uart_set_mctrl(port,set) uart_update_mctrl(port,set,0) +#define uart_clear_mctrl(port,clear) uart_update_mctrl(port,0,clear) + +static inline unsigned int uart_get_altspeed(struct uart_port *port) +{ + unsigned int flags = port->flags & UPF_SPD_MASK; + unsigned int altbaud = 0; + + if (flags == ASYNC_SPD_HI) + altbaud = 57600; + if (flags == ASYNC_SPD_VHI) + altbaud = 115200; + if (flags == ASYNC_SPD_SHI) + altbaud = 230400; + if (flags == ASYNC_SPD_WARP) + altbaud = 460800; + + return altbaud; +} + +/* + * Startup the port. This will be called once per open. All calls + * will be serialised by the per-port semaphore. + */ +static int uart_startup(struct uart_state *state, int init_hw) +{ + struct uart_info *info = state->info; + struct uart_port *port = state->port; + unsigned long page; + int retval = 0; + + if (info->flags & UIF_INITIALIZED) + return 0; + + /* + * Set the TTY IO error marker - we will only clear this + * once we have successfully opened the port. Also set + * up the tty->alt_speed kludge + */ + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (port->type == PORT_UNKNOWN) + return 0; + + /* + * Initialise and allocate the transmit and temporary + * buffer. + */ + if (!info->xmit.buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + info->xmit.buf = (unsigned char *) page; + info->tmpbuf = info->xmit.buf + UART_XMIT_SIZE; + init_MUTEX(&info->tmpbuf_sem); + uart_circ_clear(&info->xmit); + } + + port->mctrl = 0; + + retval = port->ops->startup(port); + if (retval == 0) { + if (init_hw) { + /* + * Initialise the hardware port settings. + */ + uart_change_speed(state, NULL); + + /* + * Setup the RTS and DTR signals once the + * port is open and ready to respond. + */ + if (info->tty->termios->c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); + } + + info->flags |= UIF_INITIALIZED; + + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + if (retval && capable(CAP_SYS_ADMIN)) + retval = 0; + + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. Calls to + * uart_shutdown are serialised by the per-port semaphore. + */ +static void uart_shutdown(struct uart_state *state) +{ + struct uart_info *info = state->info; + struct uart_port *port = state->port; + + if (!(info->flags & UIF_INITIALIZED)) + return; + + /* + * Turn off DTR and RTS early. + */ + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free + * the irq here so the queue might never be woken up. Note + * that we won't end up waiting on delta_msr_wait again since + * any outstanding file descriptors should be pointing at + * hung_up_tty_fops now. + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * Free the IRQ and disable the port. + */ + port->ops->shutdown(port); + + /* + * Free the transmit buffer page. + */ + if (info->xmit.buf) { + free_page((unsigned long)info->xmit.buf); + info->xmit.buf = NULL; + info->tmpbuf = NULL; + } + + /* + * kill off our tasklet + */ + tasklet_kill(&info->tlet); + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~UIF_INITIALIZED; +} + +/** + * uart_update_timeout - update per-port FIFO timeout. + * @port: uart_port structure describing the port. + * @cflag: termios cflag value + * @quot: uart clock divisor quotient + * + * Set the port FIFO timeout value. The @cflag value should + * reflect the actual hardware settings. + */ +void +uart_update_timeout(struct uart_port *port, unsigned int cflag, + unsigned int baud) +{ + unsigned int bits; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + bits = 7; + break; + case CS6: + bits = 8; + break; + case CS7: + bits = 9; + break; + default: + bits = 10; + break; // CS8 + } + + if (cflag & CSTOPB) + bits++; + if (cflag & PARENB) + bits++; + + /* + * The total number of bits to be transmitted in the fifo. + */ + bits = bits * port->fifosize; + + /* + * Figure the timeout to send the above number of bits. + * Add .02 seconds of slop + */ + port->timeout = (HZ * bits) / baud + HZ/50; +} + +EXPORT_SYMBOL(uart_update_timeout); + +static inline u_int uart_calculate_quot(struct uart_port *port, u_int baud) +{ + u_int quot; + + /* Special case: B0 rate */ + if (!baud) + baud = 9600; + + /* Old HI/VHI/custom speed handling */ + if (baud == 38400 && + ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = port->custom_divisor; + else + quot = port->uartclk / (16 * baud); + + return quot; +} + +static void +uart_change_speed(struct uart_state *state, struct termios *old_termios) +{ + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; + struct termios *termios; + unsigned int quot, baud, cflag, try; + + /* + * If we have no tty, termios, or the port does not exist, + * then we can't set the parameters for this port. + */ + if (!tty || !tty->termios || port->type == PORT_UNKNOWN) + return; + + termios = tty->termios; + + cflag = termios->c_cflag; + + for (try = 0; try < 2; try ++) { + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(tty); + quot = uart_calculate_quot(port, baud); + if (quot) + break; + + /* + * Oops, the quotient was zero. Try again with + * the old baud rate if possible. + */ + termios->c_cflag &= ~CBAUD; + if (old_termios) { + termios->c_cflag |= + (old_termios->c_cflag & CBAUD); + old_termios = NULL; + continue; + } + + /* + * As a last resort, if the quotient is zero, + * default to 9600 bps + */ + termios->c_cflag |= B9600; + } + + uart_update_timeout(port, cflag, port->uartclk / (16 * quot)); + + if (termios->c_cflag & CRTSCTS) + state->info->flags |= UIF_CTS_FLOW; + else + state->info->flags &= ~UIF_CTS_FLOW; + if (termios->c_cflag & CLOCAL) + state->info->flags &= ~UIF_CHECK_CD; + else + state->info->flags |= UIF_CHECK_CD; + + /* + * Set up parity check flag + */ + pm_access(state->pm); + + port->ops->change_speed(port, cflag, termios->c_iflag, quot); +} + +static inline void +__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c) +{ + unsigned long flags; + + if (!circ->buf) + return; + + spin_lock_irqsave(&port->lock, flags); + if (uart_circ_chars_free(circ) != 0) { + circ->buf[circ->head] = c; + circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static inline int +__uart_user_write(struct uart_port *port, struct circ_buf *circ, + const unsigned char *buf, int count) +{ + unsigned long flags; + int c, ret = 0; + + if (down_interruptible(&port->info->tmpbuf_sem)) + return -EINTR; + + while (1) { + int c1; + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(port->info->tmpbuf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + spin_lock_irqsave(&port->lock, flags); + c1 = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(circ->buf + circ->head, port->info->tmpbuf, c); + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + spin_unlock_irqrestore(&port->lock, flags); + buf += c; + count -= c; + ret += c; + } + up(&port->info->tmpbuf_sem); + + return ret; +} + +static inline int +__uart_kern_write(struct uart_port *port, struct circ_buf *circ, + const unsigned char *buf, int count) +{ + unsigned long flags; + int c, ret = 0; + + spin_lock_irqsave(&port->lock, flags); + while (1) { + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(circ->buf + circ->head, buf, c); + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + spin_unlock_irqrestore(&port->lock, flags); + + return ret; +} + +static void uart_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct uart_state *state = tty->driver_data; + + __uart_put_char(state->port, &state->info->xmit, ch); +} + +static void uart_flush_chars(struct tty_struct *tty) +{ + uart_start(tty); +} + +static int +uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, + int count) +{ + struct uart_state *state = tty->driver_data; + int ret; + + if (!state->info->xmit.buf) + return 0; + + if (from_user) + ret = __uart_user_write(state->port, &state->info->xmit, buf, count); + else + ret = __uart_kern_write(state->port, &state->info->xmit, buf, count); + + uart_start(tty); + return ret; +} + +static int uart_write_room(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + return uart_circ_chars_free(&state->info->xmit); +} + +static int uart_chars_in_buffer(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + return uart_circ_chars_pending(&state->info->xmit); +} + +static void uart_flush_buffer(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long flags; + + DPRINTK("uart_flush_buffer(%d) called\n", + MINOR(tty->device) - tty->driver.minor_start); + + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->info->xmit); + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void uart_send_xchar(struct tty_struct *tty, char ch) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long flags; + + if (port->ops->send_xchar) + port->ops->send_xchar(port, ch); + else { + port->x_char = ch; + if (ch) { + spin_lock_irqsave(&port->lock, flags); + port->ops->start_tx(port, 0); + spin_unlock_irqrestore(&port->lock, flags); + } + } +} + +static void uart_throttle(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + if (I_IXOFF(tty)) + uart_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + uart_clear_mctrl(state->port, TIOCM_RTS); +} + +static void uart_unthrottle(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + + if (I_IXOFF(tty)) { + if (port->x_char) + port->x_char = 0; + else + uart_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) + uart_set_mctrl(port, TIOCM_RTS); +} + +static int uart_get_info(struct uart_state *state, struct serial_struct *retinfo) +{ + struct uart_port *port = state->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = port->type; + tmp.line = port->line; + tmp.port = port->iobase; + if (HIGH_BITS_OFFSET) + tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; + tmp.irq = port->irq; + tmp.flags = port->flags; + tmp.xmit_fifo_size = port->fifosize; + tmp.baud_base = port->uartclk / 16; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = port->custom_divisor; + tmp.hub6 = port->hub6; + tmp.io_type = port->iotype; + tmp.iomem_reg_shift = port->regshift; + tmp.iomem_base = (void *)port->mapbase; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int +uart_set_info(struct uart_state *state, struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct uart_port *port = state->port; + unsigned long new_port; + unsigned int change_irq, change_port, old_flags; + unsigned int old_custom_divisor; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + /* + * This semaphore protects state->count. It is also + * very useful to prevent opens. Also, take the + * port configuration semaphore to make sure that a + * module insertion/removal doesn't change anything + * under us. + */ + down(&state->sem); + + change_irq = new_serial.irq != port->irq; + + /* + * Since changing the 'type' of the port changes its resource + * allocations, we should treat type changes the same as + * IO port changes. + */ + change_port = new_port != port->iobase || + (unsigned long)new_serial.iomem_base != port->mapbase || + new_serial.hub6 != port->hub6 || + new_serial.io_type != port->iotype || + new_serial.iomem_reg_shift != port->regshift || + new_serial.type != port->type; + + old_flags = port->flags; + old_custom_divisor = port->custom_divisor; + + if (!capable(CAP_SYS_ADMIN)) { + retval = -EPERM; + if (change_irq || change_port || + (new_serial.baud_base != port->uartclk / 16) || + (new_serial.close_delay != state->close_delay) || + (new_serial.closing_wait != state->closing_wait) || + (new_serial.xmit_fifo_size != port->fifosize) || + (((new_serial.flags ^ old_flags) & ~UPF_USR_MASK) != 0)) + goto exit; + port->flags = ((port->flags & ~UPF_USR_MASK) | + (new_serial.flags & UPF_USR_MASK)); + port->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + /* + * Ask the low level driver to verify the settings. + */ + if (port->ops->verify_port) + retval = port->ops->verify_port(port, &new_serial); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)) + retval = -EINVAL; + + if (retval) + goto exit; + + if (change_port || change_irq) { + retval = -EBUSY; + + /* + * Make sure that we are the sole user of this port. + */ + if (uart_users(state) > 1) + goto exit; + + /* + * We need to shutdown the serial port at the old + * port/type/irq combination. + */ + uart_shutdown(state); + } + + if (change_port) { + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + + old_iobase = port->iobase; + old_mapbase = port->mapbase; + old_type = port->type; + old_hub6 = port->hub6; + old_iotype = port->iotype; + old_shift = port->regshift; + + /* + * Free and release old regions + */ + if (old_type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->iobase = new_port; + port->type = new_serial.type; + port->hub6 = new_serial.hub6; + port->iotype = new_serial.io_type; + port->regshift = new_serial.iomem_reg_shift; + port->mapbase = (unsigned long)new_serial.iomem_base; + + /* + * Claim and map the new regions + */ + if (port->type != PORT_UNKNOWN) { + retval = port->ops->request_port(port); + } else { + /* Always success - Jean II */ + retval = 0; + } + + /* + * If we fail to request resources for the + * new port, try to restore the old settings. + */ + if (retval && old_type != PORT_UNKNOWN) { + port->iobase = old_iobase; + port->type = old_type; + port->hub6 = old_hub6; + port->iotype = old_iotype; + port->regshift = old_shift; + port->mapbase = old_mapbase; + retval = port->ops->request_port(port); + /* + * If we failed to restore the old settings, + * we fail like this. + */ + if (retval) + port->type = PORT_UNKNOWN; + + /* + * We failed anyway. + */ + retval = -EBUSY; + } + } + + port->irq = new_serial.irq; + port->uartclk = new_serial.baud_base * 16; + port->flags = (port->flags & ~UPF_CHANGE_MASK) | + (new_serial.flags & UPF_CHANGE_MASK); + port->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ / 100; + state->closing_wait = new_serial.closing_wait * HZ / 100; + port->fifosize = new_serial.xmit_fifo_size; + if (state->info->tty) + state->info->tty->low_latency = + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + + check_and_exit: + retval = 0; + if (port->type == PORT_UNKNOWN) + goto exit; + if (state->info->flags & UIF_INITIALIZED) { + if (((old_flags ^ port->flags) & UPF_SPD_MASK) || + old_custom_divisor != port->custom_divisor) { + state->info->tty->alt_speed = uart_get_altspeed(port); + uart_change_speed(state, NULL); + } + } else + retval = uart_startup(state, 1); + exit: + up(&state->sem); + return retval; +} + + +/* + * uart_get_lsr_info - get line status register info. + * Note: uart_ioctl protects us against hangups. + */ +static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) +{ + struct uart_port *port = state->port; + unsigned int result; + + result = port->ops->tx_empty(port); + + /* + * If we're about to load something into the transmit + * register, we'll pretend the transmitter isn't empty to + * avoid a race condition (depending on when the transmit + * interrupt happens). + */ + if (port->x_char || + ((uart_circ_chars_pending(&state->info->xmit) > 0) && + !state->info->tty->stopped && !state->info->tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + return put_user(result, value); +} + +static int uart_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + int result = -EIO; + + down(&state->sem); + if ((!file || !tty_hung_up_p(file)) && + !(tty->flags & (1 << TTY_IO_ERROR))) { + result = port->mctrl; + result |= port->ops->get_mctrl(port); + } + up(&state->sem); + + return result; +} + +static int +uart_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + int ret = -EIO; + + down(&state->sem); + if ((!file || !tty_hung_up_p(file)) && + !(tty->flags & (1 << TTY_IO_ERROR))) { + uart_update_mctrl(port, set, clear); + ret = 0; + } + up(&state->sem); + return ret; +} + +static void uart_break_ctl(struct tty_struct *tty, int break_state) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + + BUG_ON(!kernel_locked()); + + down(&state->sem); + + if (port->type != PORT_UNKNOWN) + port->ops->break_ctl(port, break_state); + + up(&state->sem); +} + +static int uart_do_autoconfig(struct uart_state *state) +{ + struct uart_port *port = state->port; + int flags, ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + /* + * Take the per-port semaphore. This prevents count from + * changing, and hence any extra opens of the port while + * we're auto-configuring. + */ + if (down_interruptible(&state->sem)) + return -ERESTARTSYS; + + ret = -EBUSY; + if (uart_users(state) == 1) { + uart_shutdown(state); + + /* + * If we already have a port type configured, + * we must release its resources. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + + /* + * This will claim the ports resources if + * a port is found. + */ + port->ops->config_port(port, flags); + + ret = uart_startup(state, 1); + } + up(&state->sem); + return ret; +} + +/* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ +static int +uart_wait_modem_status(struct uart_state *state, unsigned long arg) +{ + struct uart_port *port = state->port; + DECLARE_WAITQUEUE(wait, current); + struct uart_icount cprev, cnow; + int ret; + + /* + * note the counters on entry + */ + spin_lock_irq(&port->lock); + memcpy(&cprev, &port->icount, sizeof(struct uart_icount)); + + /* + * Force modem status interrupts on + */ + port->ops->enable_ms(port); + spin_unlock_irq(&port->lock); + + add_wait_queue(&state->info->delta_msr_wait, &wait); + for (;;) { + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + set_current_state(TASK_INTERRUPTIBLE); + + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + ret = 0; + break; + } + + schedule(); + + /* see if a signal did it */ + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + cprev = cnow; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&state->info->delta_msr_wait, &wait); + + return ret; +} + +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int +uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt) +{ + struct serial_icounter_struct icount; + struct uart_icount cnow; + struct uart_port *port = state->port; + + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + +/* + * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. + */ +static int +uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct uart_state *state = tty->driver_data; + int ret = -ENOIOCTLCMD; + + BUG_ON(!kernel_locked()); + + /* + * These ioctls don't rely on the hardware to be present. + */ + switch (cmd) { + case TIOCGSERIAL: + ret = uart_get_info(state, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = uart_set_info(state, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = uart_do_autoconfig(state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } + + /* + * The following should only be used when hardware is present. + */ + switch (cmd) { + case TIOCMIWAIT: + ret = uart_wait_modem_status(state, arg); + break; + + case TIOCGICOUNT: + ret = uart_get_count(state, (struct serial_icounter_struct *)arg); + break; + + case TIOCMGET: + { + int val; + val = uart_tiocmget(tty, filp); + if (val >= 0) { + ret = put_user(val, (int *)arg); + } else { + ret = val; + } + } + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + { + int val, set = 0, clear = 0; + ret = get_user(val, (int *)arg); + if (ret) + break; + + switch (cmd) { + case TIOCMBIS: + set = val; + break; + case TIOCMBIC: + clear = val; + break; + case TIOCMSET: + set = val; + clear = ~val; + break; + } + + set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; + clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; + + ret = uart_tiocmset(tty, filp, set, clear); + } + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + down(&state->sem); + + if (tty_hung_up_p(filp)) { + ret = -EIO; + goto out_up; + } + + /* + * All these rely on hardware being present and need to be + * protected against the tty being hung up. + */ + switch (cmd) { + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(state, (unsigned int *)arg); + break; + + default: { + struct uart_port *port = state->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; + } + } + out_up: + up(&state->sem); + out: + return ret; +} + +static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct uart_state *state = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + BUG_ON(!kernel_locked()); + + /* + * These are the bits that are used to setup various + * flags in the low level driver. + */ +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + if ((cflag ^ old_termios->c_cflag) == 0 && + RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) + return; + + uart_change_speed(state, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) + uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + unsigned int mask = TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + mask |= TIOCM_RTS; + uart_set_mctrl(state->port, mask); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { + spin_lock_irqsave(&state->port->lock, flags); + tty->hw_stopped = 0; + __uart_start(tty); + spin_unlock_irqrestore(&state->port->lock, flags); + } + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&state->info->open_wait); +#endif +} + +/* + * In 2.4.5, calls to this will be serialized via the BKL in + * linux/drivers/char/tty_io.c:tty_release() + * linux/drivers/char/tty_io.c:do_tty_handup() + */ +static void uart_close(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; + struct uart_state *state = tty->driver_data; + struct uart_port *port; + + BUG_ON(!kernel_locked()); + + if (!state || !state->port) + return; + + port = state->port; + + DPRINTK("uart_close(%d) called\n", port->line); + + down(&state->sem); + + if (tty_hung_up_p(filp)) + goto done; + + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("uart_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for %s%d: %d\n", + tty->driver.name, port->line, state->count); + state->count = 0; + } + if (state->count) + goto done; + + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (state->info->flags & UIF_NORMAL_ACTIVE) + state->normal_termios = *tty->termios; + if (state->info->flags & UIF_CALLOUT_ACTIVE) + state->callout_termios = *tty->termios; + + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters by + * setting tty->closing. + */ + tty->closing = 1; + + if (state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); + + /* + * At this point, we stop accepting input. To do this, we + * disable the receive line status interrupts. + */ + if (state->info->flags & UIF_INITIALIZED) { + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_rx(port); + spin_unlock_irqrestore(&port->lock, flags); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + uart_wait_until_sent(tty, port->timeout); + } + + uart_shutdown(state); + uart_flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + state->info->tty = NULL; + + if (state->info->blocked_open) { + if (state->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(state->close_delay); + set_current_state(TASK_RUNNING); + } + } else if (!uart_console(port)) { + uart_change_pm(state, 3); + } + + /* + * Wake up anyone trying to open this port. + */ + state->info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE); + wake_up_interruptible(&state->info->open_wait); + + done: + up(&state->sem); + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); +} + +static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; + unsigned long char_time, expire; + + BUG_ON(!kernel_locked()); + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (port->timeout - HZ/50) / port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than port->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*port->timeout. + */ + if (timeout == 0 || timeout > 2 * port->timeout) + timeout = 2 * port->timeout; + + expire = jiffies + timeout; + + DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n", + port->line, jiffies, expire); + + /* + * Check whether the transmitter is empty every 'char_time'. + * 'timeout' / 'expire' give us the maximum amount of time + * we wait. + */ + while (!port->ops->tx_empty(port)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +} + +/* + * This is called with the BKL held in + * linux/drivers/char/tty_io.c:do_tty_hangup() + * We're called from the eventd thread, so we can sleep for + * a _short_ time only. + */ +static void uart_hangup(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + BUG_ON(!kernel_locked()); + DPRINTK("uart_hangup(%d)\n", state->port->line); + + down(&state->sem); + if (state->info && state->info->flags & (UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE)) { + uart_flush_buffer(tty); + uart_shutdown(state); + state->count = 0; + state->info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE); + state->info->tty = NULL; + wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->delta_msr_wait); + } + up(&state->sem); +} + +/* + * Copy across the serial console cflag setting into the termios settings + * for the initial open of the port. This allows continuity between the + * kernel settings, and the settings init adopts when it opens the port + * for the first time. + */ +static void uart_update_termios(struct uart_state *state) +{ + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; + + if (uart_console(port) && port->cons->cflag) { + tty->termios->c_cflag = port->cons->cflag; + port->cons->cflag = 0; + } + + /* + * If the device failed to grab its irq resources, + * or some other error occurred, don't try to talk + * to the port hardware. + */ + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + /* + * Make termios settings take effect. + */ + uart_change_speed(state, NULL); + + /* + * And finally enable the RTS and DTR signals. + */ + if (tty->termios->c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } +} + +/* + * Block the open until the port is ready. We must be called with + * the per-port semaphore held. + */ +static int +uart_block_til_ready(struct file *filp, struct uart_state *state) +{ + DECLARE_WAITQUEUE(wait, current); + struct uart_info *info = state->info; + struct uart_port *port = state->port; + struct termios *termios; + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (info->tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & UIF_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & UIF_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & UIF_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= UIF_CALLOUT_ACTIVE; + return 0; + } + + if (info->flags & UIF_CALLOUT_ACTIVE) { + termios = &state->normal_termios; + } else { + termios = state->info->tty->termios; + } + + info->blocked_open++; + state->count--; + + add_wait_queue(&info->open_wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + /* + * If we have been hung up, tell userspace/restart open. + */ + if (tty_hung_up_p(filp) || info->tty == NULL) + break; + + /* + * If the port has been closed, tell userspace/restart open. + */ + if (!(info->flags & UIF_INITIALIZED)) + break; + + /* + * If non-blocking mode is set, or CLOCAL mode is set, + * we don't want to wait for the modem status lines to + * indicate that the port is ready. + * + * Also, if the port is not enabled/configured, we want + * to allow the open to succeed here. Note that we will + * have set TTY_IO_ERROR for a non-existant port. + */ + if ((filp->f_flags & O_NONBLOCK) || + (termios->c_cflag & CLOCAL) || + (info->tty->flags & (1 << TTY_IO_ERROR))) { + break; + } + + if (!(info->flags & UIF_CALLOUT_ACTIVE)) { + /* + * Set DTR to allow modem to know we're waiting. Do + * not set RTS here - we want to make sure we catch + * the data from the modem. + */ + if (info->tty->termios->c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR); + + /* + * and wait for the carrier to indicate that the + * modem is ready for us. + */ + if (port->ops->get_mctrl(port) & TIOCM_CAR) + break; + } + + up(&state->sem); + schedule(); + down(&state->sem); + + if (signal_pending(current)) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + + state->count++; + info->blocked_open--; + + info->flags |= UIF_NORMAL_ACTIVE; + + if (signal_pending(current)) + return -ERESTARTSYS; + + if (!info->tty || tty_hung_up_p(filp)) + return -EAGAIN; + + return 0; +} + +static struct uart_state *uart_get(struct uart_driver *drv, int line) +{ + struct uart_state *state; + + down(&port_sem); + state = drv->state + line; + if (down_interruptible(&state->sem)) { + state = ERR_PTR(-ERESTARTSYS); + goto out; + } + + state->count++; + if (!state->port) { + state->count--; + up(&state->sem); + state = ERR_PTR(-ENXIO); + goto out; + } + + if (!state->info) { + state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); + if (state->info) { + memset(state->info, 0, sizeof(struct uart_info)); + init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->delta_msr_wait); + + /* + * Link the info into the other structures. + */ + state->port->info = state->info; + + tasklet_init(&state->info->tlet, uart_tasklet_action, + (unsigned long)state); + } else { + state->count--; + up(&state->sem); + state = ERR_PTR(-ENOMEM); + } + } + + out: + up(&port_sem); + return state; +} + +/* + * In 2.4.5, calls to uart_open are serialised by the BKL in + * linux/fs/devices.c:chrdev_open() + * Note that if this fails, then uart_close() _will_ be called. + */ +static int uart_open(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; + struct uart_state *state; + int retval, line = MINOR(tty->device) - tty->driver.minor_start; + + BUG_ON(!kernel_locked()); + DPRINTK("uart_open(%d) called\n", line); + + /* + * tty->driver->num won't change, so we won't fail here with + * tty->driver_data set to something non-NULL (and therefore + * we won't get caught by uart_close()). + */ + retval = -ENODEV; + if (line >= tty->driver.num) + goto fail; + + if (!try_inc_mod_count(drv->owner)) + goto fail; + + /* + * We take the semaphore inside uart_get to guarantee that we won't + * be re-entered while allocating the info structure, or while we + * request any IRQs that the driver may need. This also has the nice + * side-effect that it delays the action of uart_hangup, so we can + * guarantee that info->tty will always contain something reasonable. + */ + state = uart_get(drv, line); + if (IS_ERR(state)) { + retval = PTR_ERR(state); + if (!tty->driver_data) + goto out; + else + goto fail; + } + + /* + * Once we set tty->driver_data here, we are guaranteed that + * uart_close() will decrement the driver module use count. + * Any failures from here onwards should not touch the count. + */ + tty->driver_data = state; + tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->alt_speed = uart_get_altspeed(state->port); + state->info->tty = tty; + + /* + * If the port is in the middle of closing, bail out now. + */ + if (tty_hung_up_p(filp)) { + retval = -EAGAIN; + state->count--; + up(&state->sem); + goto fail; + } + + /* + * Make sure the device is in D0 state. + */ + if (state->count == 1) + uart_change_pm(state, 0); + + /* + * Start up the serial port. + */ + retval = uart_startup(state, 0); + + /* + * If we succeeded, wait until the port is ready. + */ + if (retval == 0) + retval = uart_block_til_ready(filp, state); + + /* + * If this is the first open to succeed, adjust things to suit. + */ + if (retval == 0 && state->count == 1) { + if (state->port->flags & UPF_SPLIT_TERMIOS) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = state->normal_termios; + else + *tty->termios = state->callout_termios; + } + + uart_update_termios(state); + + state->info->session = current->session; + state->info->pgrp = current->pgrp; + } + up(&state->sem); + + return 0; + + out: + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); + fail: + return retval; +} + +static const char *uart_type(struct uart_port *port) +{ + const char *str = NULL; + + if (port->ops->type) + str = port->ops->type(port); + + if (!str) + str = "unknown"; + + return str; +} + +#ifdef CONFIG_PROC_FS + +static int uart_line_info(char *buf, struct uart_driver *drv, int i) +{ + struct uart_state *state = drv->state + i; + struct uart_port *port = state->port; + char stat_buf[32]; + unsigned int status; + int ret; + + if (!port) + return 0; + + ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d", + port->line, uart_type(port), + port->iobase, port->irq); + + if (port->type == PORT_UNKNOWN) { + strcat(buf, "\n"); + return ret + 1; + } + + status = port->ops->get_mctrl(port); + + ret += sprintf(buf + ret, " tx:%d rx:%d", + port->icount.tx, port->icount.rx); + if (port->icount.frame) + ret += sprintf(buf + ret, " fe:%d", + port->icount.frame); + if (port->icount.parity) + ret += sprintf(buf + ret, " pe:%d", + port->icount.parity); + if (port->icount.brk) + ret += sprintf(buf + ret, " brk:%d", + port->icount.brk); + if (port->icount.overrun) + ret += sprintf(buf + ret, " oe:%d", + port->icount.overrun); + +#define INFOBIT(bit,str) \ + if (port->mctrl & (bit)) \ + strcat(stat_buf, (str)) +#define STATBIT(bit,str) \ + if (status & (bit)) \ + strcat(stat_buf, (str)) + + stat_buf[0] = '\0'; + stat_buf[1] = '\0'; + INFOBIT(TIOCM_RTS, "|RTS"); + STATBIT(TIOCM_CTS, "|CTS"); + INFOBIT(TIOCM_DTR, "|DTR"); + STATBIT(TIOCM_DSR, "|DSR"); + STATBIT(TIOCM_CAR, "|CD"); + STATBIT(TIOCM_RNG, "|RI"); + if (stat_buf[0]) + stat_buf[0] = ' '; + strcat(stat_buf, "\n"); + + ret += sprintf(buf + ret, stat_buf); + return ret; +} + +static int uart_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct tty_driver *ttydrv = data; + struct uart_driver *drv = ttydrv->driver_state; + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", + "", "", ""); + for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) { + l = uart_line_info(page + len, drv, i); + len += l; + if (len + begin > off + count) + goto done; + if (len + begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; + done: + if (off >= len + begin) + return 0; + *start = page + (off - begin); + return (count < begin + len - off) ? count : (begin + len - off); +} +#endif + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +/* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ +struct uart_port * __init +uart_get_console(struct uart_port *ports, int nr, struct console *co) +{ + int idx = co->index; + + if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 && + ports[idx].membase == NULL)) + for (idx = 0; idx < nr; idx++) + if (ports[idx].iobase != 0 || + ports[idx].membase != NULL) + break; + + co->index = idx; + + return ports + idx; +} + +/** + * uart_parse_options - Parse serial port baud/parity/bits/flow contro. + * @options: pointer to option string + * @baud: pointer to an 'int' variable for the baud rate. + * @parity: pointer to an 'int' variable for the parity. + * @bits: pointer to an 'int' variable for the number of data bits. + * @flow: pointer to an 'int' variable for the flow control character. + * + * uart_parse_options decodes a string containing the serial console + * options. The format of the string is , + * eg: 115200n8r + */ +void __init +uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) +{ + char *s = options; + + *baud = simple_strtoul(s, NULL, 10); + while (*s >= '0' && *s <= '9') + s++; + if (*s) + *parity = *s++; + if (*s) + *bits = *s++ - '0'; + if (*s) + *flow = *s; +} + +struct baud_rates { + unsigned int rate; + unsigned int cflag; +}; + +static struct baud_rates baud_rates[] = { + { 921600, B921600 }, + { 460800, B460800 }, + { 230400, B230400 }, + { 115200, B115200 }, + { 57600, B57600 }, + { 38400, B38400 }, + { 19200, B19200 }, + { 9600, B9600 }, + { 4800, B4800 }, + { 2400, B2400 }, + { 1200, B1200 }, + { 0, B38400 } +}; + +/** + * uart_set_options - setup the serial console parameters + * @port: pointer to the serial ports uart_port structure + * @co: console pointer + * @baud: baud rate + * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) + * @bits: number of data bits + * @flow: flow control character - 'r' (rts) + */ +int __init +uart_set_options(struct uart_port *port, struct console *co, + int baud, int parity, int bits, int flow) +{ + struct termios termios; + unsigned int quot; + int i; + + memset(&termios, 0, sizeof(struct termios)); + + termios.c_cflag = CREAD | HUPCL | CLOCAL; + + /* + * Construct a cflag setting. + */ + for (i = 0; baud_rates[i].rate; i++) + if (baud_rates[i].rate <= baud) + break; + + termios.c_cflag |= baud_rates[i].cflag; + baud = baud_rates[i].rate; + if (baud == 0) + baud = 38400; + + if (bits == 7) + termios.c_cflag |= CS7; + else + termios.c_cflag |= CS8; + + switch (parity) { + case 'o': case 'O': + termios.c_cflag |= PARODD; + /*fall through*/ + case 'e': case 'E': + termios.c_cflag |= PARENB; + break; + } + + if (flow == 'r') + termios.c_cflag |= CRTSCTS; + + quot = (port->uartclk / (16 * baud)); + port->ops->change_speed(port, termios.c_cflag, 0, quot); + co->cflag = termios.c_cflag; + + return 0; +} + +extern void ambauart_console_init(void); +extern void anakin_console_init(void); +extern void clps711xuart_console_init(void); +extern void sa1100_rs_console_init(void); +extern void serial8250_console_init(void); +extern void at91_console_init(void); + +/* + * Central "initialise all serial consoles" container. Needs to be killed. + */ +void __init uart_console_init(void) +{ +#ifdef CONFIG_SERIAL_AMBA_CONSOLE + ambauart_console_init(); +#endif +#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE + anakin_console_init(); +#endif +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + clps711xuart_console_init(); +#endif +#ifdef CONFIG_SERIAL_SA1100_CONSOLE + sa1100_rs_console_init(); +#endif +#ifdef CONFIG_SERIAL_AT91_CONSOLE + at91_console_init(); +#endif +#ifdef CONFIG_SERIAL_8250_CONSOLE + serial8250_console_init(); +#endif +#ifdef CONFIG_SERIAL_UART00_CONSOLE + uart00_console_init(); +#endif +} +#endif /* CONFIG_SERIAL_CORE_CONSOLE */ + +static void uart_change_pm(struct uart_state *state, int pm_state) +{ + struct uart_port *port = state->port; + if (port->ops->pm) + port->ops->pm(port, pm_state, 0); +} + +#ifdef CONFIG_PM +int uart_suspend_port(struct uart_state *state) +{ + struct uart_port *port = state->port; + + down(&state->sem); + if (port) { + /* + * Disable the console device before suspending. + */ + if (uart_console(port)) + port->cons->flags &= ~CON_ENABLED; + + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; + + spin_lock_irq(&port->lock); + ops->stop_tx(port, 0); + ops->set_mctrl(port, 0); + ops->stop_rx(port); + spin_unlock_irq(&port->lock); + + /* + * Wait for the transmitter to empty. + */ + while (!ops->tx_empty(port)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10*HZ/1000); + } + set_current_state(TASK_RUNNING); + + ops->shutdown(port); + } + + uart_change_pm(state, 3); + } + + up(&state->sem); + + return 0; +} + +int uart_resume_port(struct uart_state *state) +{ + struct uart_port *port = state->port; + + down(&state->sem); + if (port) { + uart_change_pm(state, 0); + + /* + * Re-enable the console device after suspending. + */ + if (uart_console(port)) { + uart_change_speed(state, NULL); + port->cons->flags |= CON_ENABLED; + } + + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; + + ops->set_mctrl(port, 0); + ops->startup(port); + uart_change_speed(state, NULL); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, port->mctrl); + ops->start_tx(port, 0); + spin_unlock_irq(&port->lock); + } + } + + up(&state->sem); + + return 0; +} + +/* + * Wakeup support. + */ +static int uart_pm_set_wakeup(struct uart_state *state, int data) +{ + int err = 0; + + if (state->port->ops->set_wake) + err = state->port->ops->set_wake(state->port, data); + + return err; +} + +static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct uart_state *state = dev->data; + int err = 0; + + if (state->port && state->port->type == PORT_UNKNOWN) + return 0; + + switch (rqst) { + case PM_SUSPEND: + err = uart_suspend_port(state); + break; + + case PM_RESUME: + err = uart_resume_port(state); + break; + + case PM_SET_WAKEUP: + err = uart_pm_set_wakeup(state, (int)data); + break; + } + return err; +} +#endif + +static inline void +uart_report_port(struct uart_driver *drv, struct uart_port *port) +{ + printk("%s%d at ", drv->normal_name, port->line); + switch (port->iotype) { + case UPIO_PORT: + printk("I/O 0x%x", port->iobase); + break; + case UPIO_HUB6: + printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6); + break; + case UPIO_MEM: + printk("MMIO 0x%lx", port->mapbase); + break; + } + printk(" (irq = %d) is a %s\n", port->irq, uart_type(port)); +} + +static void +uart_configure_port(struct uart_driver *drv, struct uart_state *state, + struct uart_port *port) +{ + unsigned int flags; + + /* + * If there isn't a port here, don't do anything further. + */ + if (!port->iobase && !port->mapbase && !port->membase) + return; + + /* + * Now do the auto configuration stuff. Note that config_port + * is expected to claim the resources and map the port for us. + */ + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (port->flags & UPF_BOOT_AUTOCONF) { + port->type = PORT_UNKNOWN; + port->ops->config_port(port, flags); + } + + if (port->type != PORT_UNKNOWN) { + unsigned long flags; + + uart_report_port(drv, port); + + /* + * Ensure that the modem control lines are de-activated. + * We probably don't need a spinlock around this, but + */ + spin_lock_irqsave(&port->lock, flags); + port->ops->set_mctrl(port, 0); + spin_unlock_irqrestore(&port->lock, flags); + + /* + * Power down all ports by default, except the + * console if we have one. + */ + if (!uart_console(port)) + uart_change_pm(state, 3); + } +} + +/* + * This reverses the effects of uart_configure_port, hanging up the + * port before removal. + */ +static void +uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) +{ + struct uart_port *port = state->port; + struct uart_info *info = state->info; + + if (info && info->tty) + tty_vhangup(info->tty); + + down(&state->sem); + + state->info = NULL; + + /* + * Free the port IO and memory resources, if any. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + /* + * Indicate that there isn't a port here anymore. + */ + port->type = PORT_UNKNOWN; + + /* + * Kill the tasklet, and free resources. + */ + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } + + up(&state->sem); +} + +/** + * uart_register_driver - register a driver with the uart core layer + * @drv: low level driver structure + * + * Register a uart driver with the core driver. We in turn register + * with the tty layer, and initialise the core driver per-port state. + * + * We have a proc file in /proc/tty/driver which is named after the + * normal driver. + * + * drv->port should be NULL, and the per-port structures should be + * registered using uart_add_one_port after this call has succeeded. + */ +int uart_register_driver(struct uart_driver *drv) +{ + struct tty_driver *normal, *callout; + int i, retval; + + BUG_ON(drv->state); + + /* + * Maybe we should be using a slab cache for this, especially if + * we have a large number of ports to handle. Note that we also + * allocate space for an integer for reference counting. + */ + drv->state = kmalloc(sizeof(struct uart_state) * drv->nr + + sizeof(int), GFP_KERNEL); + retval = -ENOMEM; + if (!drv->state) + goto out; + + memset(drv->state, 0, sizeof(struct uart_state) * drv->nr + + sizeof(int)); + + normal = drv->normal_driver; + callout = drv->callout_driver; + + normal->magic = TTY_DRIVER_MAGIC; + normal->driver_name = drv->normal_name; + normal->name = drv->normal_name; + normal->major = drv->normal_major; + normal->minor_start = drv->minor; + normal->num = drv->nr; + normal->type = TTY_DRIVER_TYPE_SERIAL; + normal->subtype = SERIAL_TYPE_NORMAL; + normal->init_termios = tty_std_termios; + normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + normal->refcount = (int *)(drv->state + drv->nr); + normal->table = drv->table; + normal->termios = drv->termios; + normal->termios_locked = drv->termios_locked; + normal->driver_state = drv; + + normal->open = uart_open; + normal->close = uart_close; + normal->write = uart_write; + normal->put_char = uart_put_char; + normal->flush_chars = uart_flush_chars; + normal->write_room = uart_write_room; + normal->chars_in_buffer = uart_chars_in_buffer; + normal->flush_buffer = uart_flush_buffer; + normal->ioctl = uart_ioctl; + normal->throttle = uart_throttle; + normal->unthrottle = uart_unthrottle; + normal->send_xchar = uart_send_xchar; + normal->set_termios = uart_set_termios; + normal->stop = uart_stop; + normal->start = uart_start; + normal->hangup = uart_hangup; + normal->break_ctl = uart_break_ctl; + normal->wait_until_sent = uart_wait_until_sent; +#ifdef CONFIG_PROC_FS + normal->read_proc = uart_read_proc; +#endif + + /* + * The callout device is just like the normal device except for + * the major number and the subtype code. + */ + *callout = *normal; + callout->name = drv->callout_name; + callout->major = drv->callout_major; + callout->subtype = SERIAL_TYPE_CALLOUT; + callout->read_proc = NULL; + callout->proc_entry = NULL; + + /* + * Initialise the UART state(s). + */ + for (i = 0; i < drv->nr; i++) { + struct uart_state *state = drv->state + i; + + state->callout_termios = callout->init_termios; + state->normal_termios = normal->init_termios; + state->close_delay = 5 * HZ / 10; + state->closing_wait = 30 * HZ; + + init_MUTEX(&state->sem); + } + + retval = tty_register_driver(normal); + if (retval) + goto out; + + retval = tty_register_driver(callout); + if (retval) + tty_unregister_driver(normal); + + out: + if (retval < 0) { + kfree(drv->state); + } + return retval; +} + +/** + * uart_unregister_driver - remove a driver from the uart core layer + * @drv: low level driver structure + * + * Remove all references to a driver from the core driver. The low + * level driver must have removed all its ports via the + * uart_remove_one_port() if it registered them with uart_add_one_port(). + * (ie, drv->port == NULL) + */ +void uart_unregister_driver(struct uart_driver *drv) +{ + tty_unregister_driver(drv->normal_driver); + tty_unregister_driver(drv->callout_driver); + + kfree(drv->state); + drv->state = NULL; +} + +/** + * uart_add_one_port - attach a driver-defined port structure + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure to use for this port. + * + * This allows the driver to register its own uart_port structure + * with the core driver. The main purpose is to allow the low + * level uart drivers to expand uart_port, rather than having yet + * more levels of structures. + */ +int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state; + int ret = 0; + + BUG_ON(in_interrupt()); + + if (port->line >= drv->nr) + return -EINVAL; + + state = drv->state + port->line; + + down(&port_sem); + if (state->port) { + ret = -EINVAL; + goto out; + } + + state->port = port; + + spin_lock_init(&port->lock); + port->cons = drv->cons; + port->info = state->info; + + uart_configure_port(drv, state, port); + + /* + * Register the port whether it's detected or not. This allows + * setserial to be used to alter this ports parameters. + */ + tty_register_devfs(drv->normal_driver, 0, drv->minor + port->line); + tty_register_devfs(drv->callout_driver, 0, drv->minor + port->line); + +#ifdef CONFIG_PM + port->cons = drv->cons; + state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); + if (state->pm) + state->pm->data = state; +#endif + + out: + up(&port_sem); + + return ret; +} + +/** + * uart_remove_one_port - detach a driver defined port structure + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure for this port + * + * This unhooks (and hangs up) the specified port structure from the + * core driver. No further calls will be made to the low-level code + * for this port. + */ +int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state = drv->state + port->line; + + BUG_ON(in_interrupt()); + + if (state->port != port) + printk(KERN_ALERT "Removing wrong port: %p != %p\n", + state->port, port); + + down(&port_sem); + + pm_unregister(state->pm); + + /* + * Remove the devices from devfs + */ + tty_unregister_devfs(drv->normal_driver, drv->minor + port->line); + tty_unregister_devfs(drv->callout_driver, drv->minor + port->line); + + uart_unconfigure_port(drv, state); + state->port = NULL; + up(&port_sem); + + return 0; +} + +/* + * Are the two ports equivalent? + */ +static int uart_match_port(struct uart_port *port1, struct uart_port *port2) +{ + if (port1->iotype != port2->iotype) + return 0; + + switch (port1->iotype) { + case UPIO_PORT: + return (port1->iobase == port2->iobase); + case UPIO_HUB6: + return (port1->iobase == port2->iobase) && + (port1->hub6 == port2->hub6); + case UPIO_MEM: + return (port1->membase == port2->membase); + } + return 0; +} + +/* + * Try to find an unused uart_state slot for a port. + */ +static struct uart_state * +uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port) +{ + int i; + + /* + * First, find a port entry which matches. Note: if we do + * find a matching entry, and it has a non-zero use count, + * then we can't register the port. + */ + for (i = 0; i < drv->nr; i++) + if (uart_match_port(drv->state[i].port, port)) + return &drv->state[i]; + + /* + * We didn't find a matching entry, so look for the first + * free entry. We look for one which hasn't been previously + * used (indicated by zero iobase). + */ + for (i = 0; i < drv->nr; i++) + if (drv->state[i].port->type == PORT_UNKNOWN && + drv->state[i].port->iobase == 0 && + drv->state[i].count == 0) + return &drv->state[i]; + + /* + * That also failed. Last resort is to find any currently + * entry which doesn't have a real port associated with it. + */ + for (i = 0; i < drv->nr; i++) + if (drv->state[i].port->type == PORT_UNKNOWN && + drv->state[i].count == 0) + return &drv->state[i]; + + return NULL; +} + +/** + * uart_register_port: register uart settings with a port + * @drv: pointer to the uart low level driver structure for this port + * @port: uart port structure describing the port + * + * Register UART settings with the specified low level driver. Detect + * the type of the port if UPF_BOOT_AUTOCONF is set, and detect the + * IRQ if UPF_AUTO_IRQ is set. + * + * We try to pick the same port for the same IO base address, so that + * when a modem is plugged in, unplugged and plugged back in, it gets + * allocated the same port. + * + * Returns negative error, or positive line number. + */ +int uart_register_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state; + int ret; + + down(&port_sem); + + state = uart_find_match_or_unused(drv, port); + + if (state) { + /* + * Ok, we've found a line that we can use. + * + * If we find a port that matches this one, and it appears + * to be in-use (even if it doesn't have a type) we shouldn't + * alter it underneath itself - the port may be open and + * trying to do useful work. + */ + if (uart_users(state) != 0) { + ret = -EBUSY; + goto out; + } + + /* + * If the port is already initialised, don't touch it. + */ + if (state->port->type == PORT_UNKNOWN) { + state->port->iobase = port->iobase; + state->port->membase = port->membase; + state->port->irq = port->irq; + state->port->uartclk = port->uartclk; + state->port->fifosize = port->fifosize; + state->port->regshift = port->regshift; + state->port->iotype = port->iotype; + state->port->flags = port->flags; + state->port->line = state - drv->state; + state->port->mapbase = port->mapbase; + + uart_configure_port(drv, state, state->port); + } + + ret = state->port->line; + } else + ret = -ENOSPC; + out: + up(&port_sem); + return ret; +} + +/** + * uart_unregister_port - de-allocate a port + * @drv: pointer to the uart low level driver structure for this port + * @line: line index previously returned from uart_register_port() + * + * Hang up the specified line associated with the low level driver, + * and mark the port as unused. + */ +void uart_unregister_port(struct uart_driver *drv, int line) +{ + struct uart_state *state; + + if (line < 0 || line >= drv->nr) { + printk(KERN_ERR "Attempt to unregister %s%d\n", + drv->normal_name, line); + return; + } + + state = drv->state + line; + + down(&port_sem); + uart_unconfigure_port(drv, state); + up(&port_sem); +} + +EXPORT_SYMBOL(uart_write_wakeup); +EXPORT_SYMBOL(uart_register_driver); +EXPORT_SYMBOL(uart_unregister_driver); +EXPORT_SYMBOL(uart_register_port); +EXPORT_SYMBOL(uart_unregister_port); +EXPORT_SYMBOL(uart_add_one_port); +EXPORT_SYMBOL(uart_remove_one_port); + +MODULE_DESCRIPTION("Serial driver core"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/serial/omaha.c @@ -0,0 +1,584 @@ +/* + * linux/drivers/char/omaha.c + * + * Driver for Omaha serial port + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999-2002 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: serial_amba.c,v 1.4 2001/07/17 20:34:27 rmk Exp $ + * + * This is a generic driver for ARM AMBA-type serial ports. They + * have a lot of 16550-like features, but are not register compatable. + * Note that although they do have CTS, DCD and DSR inputs, they do + * not have an RI input, nor do they have DTR or RTS outputs. If + * required, these have to be supplied via some other means (eg, GPIO) + * and hooked into this driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL_OMAHA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#include + +#define UART_NR 1 + +#define SERIAL_OMAHA_MAJOR 204 +#define SERIAL_OMAHA_MINOR 32 +#define SERIAL_OMAHA_NR UART_NR + +#define CALLOUT_OMAHA_NAME "cuaom" +#define CALLOUT_OMAHA_MAJOR 205 +#define CALLOUT_OMAHA_MINOR 32 +#define CALLOUT_OMAHA_NR UART_NR + +static struct tty_driver normal, callout; +static struct tty_struct *omaha_table[UART_NR]; +static struct termios *omaha_termios[UART_NR], *omaha_termios_locked[UART_NR]; +#ifdef SUPPORT_SYSRQ +static struct console omaha_console; +#endif + +#define OMAHA_ISR_PASS_LIMIT 256 + +/* + * Access macros for the Omaha UARTs + */ + +#define UART_GET_FR(p) readb((p)->membase + OMAHA_UTRSTAT) +#define UART_GET_CHAR(p) readb((p)->membase + OMAHA_URXH) +#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + OMAHA_UTXH) +#define UART_GET_RSR(p) readb((p)->membase + OMAHA_UERSTAT) +#define UART_FIFO_STATUS(p) (readl((p)->membase + OMAHA_UFSTAT)) +#define UART_RX_DATA(s) (((s) & OMAHA_RXFF_CNT) != 0) +#define UART_TX_DATA(s) (!((s) & OMAHA_TXFF)) +#define UART_TX_READY(s) (((s) & OMAHA_UTX_EMPTY)) +#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & OMAHA_UTXEMPTY) != 0) + +#define UART_DUMMY_RSR_RX 256 +#define UART_PORT_SIZE 64 + +#define RX_IRQ(port) ((port)->irq) +#define TX_IRQ(port) ((port)->irq + 5) + +/* + * Our private driver data mappings. + */ +#define drv_old_status driver_priv + +static void omahauart_stop_tx(struct uart_port *port, u_int from_tty) +{ + disable_irq(TX_IRQ(port)); +} + +static void omahauart_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) +{ + if (nonempty) + enable_irq(TX_IRQ(port)); +} + +static void omahauart_stop_rx(struct uart_port *port) +{ + disable_irq(RX_IRQ(port)); +} + +static void omahauart_enable_ms(struct uart_port *port) +{ + // Do nothing... +} + +static void +#ifdef SUPPORT_SYSRQ +omahauart_rx_chars(struct uart_info *info, struct pt_regs *regs) +#else +omahauart_rx_chars(struct uart_info *info) +#endif +{ + struct tty_struct *tty = info->tty; + volatile unsigned int status, data, ch, rsr, max_count = 256; + struct uart_port *port = info->port; + + status = UART_FIFO_STATUS(port); + while (UART_RX_DATA(status) && max_count--) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_WARNING "TTY_DONT_FLIP set\n"); + return; + } + } + + ch = UART_GET_CHAR(port); + + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + port->icount.rx++; + + /* + * Note that the error handling code is + * out of the main execution path + */ + rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX; + if (rsr & 0xf) { + if (rsr & OMAHA_UART_BREAK) { + rsr &= ~(OMAHA_UART_FRAME | OMAHA_UART_PARITY); + port->icount.brk++; + if (uart_handle_break(info, &omaha_console)) + goto ignore_char; + } else if (rsr & OMAHA_UART_PARITY) + port->icount.parity++; + else if (rsr & OMAHA_UART_FRAME) + port->icount.frame++; + if (rsr & OMAHA_UART_OVERRUN) + port->icount.overrun++; + + rsr &= port->read_status_mask; + + if (rsr & OMAHA_UART_BREAK) + *tty->flip.flag_buf_ptr = TTY_BREAK; + else if (rsr & OMAHA_UART_PARITY) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (rsr & OMAHA_UART_FRAME) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + + if (uart_handle_sysrq_char(info, ch, regs)) + goto ignore_char; + + if ((rsr & port->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((rsr & OMAHA_UART_OVERRUN) && + tty->flip.count < TTY_FLIPBUF_SIZE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character + */ + *tty->flip.char_buf_ptr++ = 0; + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + tty->flip.count++; + } + ignore_char: + status = UART_FIFO_STATUS(port); + } + tty_flip_buffer_push(tty); + return; +} + +static void omahauart_tx_chars(struct uart_info *info) +{ + struct uart_port *port = info->port; + volatile unsigned int status; + + if (port->x_char) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + omahauart_stop_tx(port, 0); + return; + } + + status = UART_FIFO_STATUS(info->port); + + // FIll FIFO as far as possible + while(UART_TX_DATA(UART_FIFO_STATUS(info->port))) + { + UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } + + if (CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) < + WAKEUP_CHARS) + uart_event(info, EVT_WRITE_WAKEUP); + + if (info->xmit.head == info->xmit.tail) + omahauart_stop_tx(info->port, 0); +} + +static void omahauart_int_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + volatile unsigned int status, pass_counter = OMAHA_ISR_PASS_LIMIT; + + status = UART_FIFO_STATUS(info->port); + do { + // TX if FIFO not full + if (UART_TX_DATA(status)) + omahauart_tx_chars(info); + + if (pass_counter-- == 0) + break; + + status = UART_FIFO_STATUS(info->port); + } while (UART_TX_DATA(status)); +} + +static void omahauart_int_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + volatile unsigned int status, pass_counter = OMAHA_ISR_PASS_LIMIT; + + status = UART_FIFO_STATUS(info->port); + do { + if (UART_RX_DATA(status)) +#ifdef SUPPORT_SYSRQ + omahauart_rx_chars(info, regs); +#else + omahauart_rx_chars(info); +#endif + + if (pass_counter-- == 0) + break; + + status = UART_FIFO_STATUS(info->port); + } while (UART_RX_DATA(status)); +} + +static u_int omahauart_tx_empty(struct uart_port *port) +{ + return UART_FIFO_STATUS(port) ? 0 : TIOCSER_TEMT; +} + +static int omahauart_get_mctrl(struct uart_port *port) +{ + // Report no errors. + + return 0; +} + +static void omahauart_set_mctrl(struct uart_port *port, u_int mctrl) +{ + // Do nothing. +} + +static void omahauart_break_ctl(struct uart_port *port, int break_state) +{ + // Do nothing. +} + +static int omahauart_startup(struct uart_port *port, struct uart_info *info) +{ + unsigned int tmp; + int retval; + + /* + * Allocate the IRQs + */ + retval = request_irq(TX_IRQ(port), omahauart_int_tx, 0, "omaha_uart_tx", info); + if (retval) + return retval; + + retval = request_irq(RX_IRQ(port), omahauart_int_rx, 0, "omaha_uart_rx", info); + + if (retval) + { + free_irq(TX_IRQ(port), info); + return retval; + } + + /* + * initialise the old status of the modem signals + */ + info->drv_old_status = 0; + + // Clear all errors + writel(0, port->membase + OMAHA_UERSTAT); + + // Enable FIFO, 16-byte watermark, also do reset (auto-clearing) + writel(0xF7, port->membase + OMAHA_UFCON); + + // Level driven TX/RX ints, with rx timeout enabled + tmp = readl(port->membase + OMAHA_UCON); + tmp |= 0x280; // rx is pulse driven... + writel(tmp, port->membase + OMAHA_UCON); + + return 0; +} + +static void omahauart_shutdown(struct uart_port *port, struct uart_info *info) +{ + /* + * Free the interrupt + */ + free_irq(TX_IRQ(port), info); /* TX interrupt */ + free_irq(RX_IRQ(port), info); /* RX interrupt */ + +} + +static void omahauart_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) +{ + // Do nothing. +} + +static const char *omahauart_type(struct uart_port *port) +{ + return port->type == PORT_OMAHA ? "OMAHA" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void omahauart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int omahauart_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_omaha") + != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void omahauart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_OMAHA; + omahauart_request_port(port); + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int omahauart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_OMAHA) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops omaha_pops = { + .tx_empty = omahauart_tx_empty, + .set_mctrl = omahauart_set_mctrl, + .get_mctrl = omahauart_get_mctrl, + .stop_tx = omahauart_stop_tx, + .start_tx = omahauart_start_tx, + .stop_rx = omahauart_stop_rx, + .enable_ms = omahauart_enable_ms, + .break_ctl = omahauart_break_ctl, + .startup = omahauart_startup, + .shutdown = omahauart_shutdown, + .change_speed = omahauart_change_speed, + .type = omahauart_type, + .release_port = omahauart_release_port, + .request_port = omahauart_request_port, + .config_port = omahauart_config_port, + .verify_port = omahauart_verify_port, +}; + +static struct uart_port omaha_ports[UART_NR] = { + { + .membase = (void *)IO_ADDRESS(OMAHA_UART0_BASE), + .mapbase = OMAHA_UART0_BASE, + .iotype = SERIAL_IO_MEM, + .irq = OMAHA_INT_URXD0, + .uartclk = 10000000, + .fifosize = 8, + .unused = { 4, 5 }, /*Udriver_priv: PORT_CTRLS(5, 4), */ + .ops = &omaha_pops, + .flags = ASYNC_BOOT_AUTOCONF, + } +}; + +#ifdef CONFIG_SERIAL_OMAHA_CONSOLE +static void omahauart_console_write(struct console *co, const char *s, u_int count) +{ + struct uart_port *port = omaha_ports + co->index; + unsigned int status; + int i; + + /* + * First save the CR then disable the interrupts + */ + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_FR(port); + } while ((status & OMAHA_UTX_EMPTY) == 0); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_FR(port); + } while ((status & OMAHA_UTX_EMPTY) == 0); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = UART_GET_FR(port); + } while ((status & OMAHA_UTX_EMPTY) == 0); +} + +static kdev_t omahauart_console_device(struct console *co) +{ + return MKDEV(SERIAL_OMAHA_MAJOR, SERIAL_OMAHA_MINOR + co->index); +} + +static int omahauart_console_wait_key(struct console *co) +{ + struct uart_port *port = omaha_ports + co->index; + unsigned int status; + + do { + status = UART_FIFO_STATUS(port); + } while (!UART_RX_DATA(status)); + return UART_GET_CHAR(port); +} + +static void __init +omahauart_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + // Do nothing. +} + +static int __init omahauart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + port = uart_get_console(omaha_ports, UART_NR, co); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + omahauart_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console omaha_console = { + .write = omahauart_console_write, + .device = omahauart_console_device, + .wait_key = omahauart_console_wait_key, + .setup = omahauart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init omahauart_console_init(void) +{ + register_console(&omaha_console); +} + +#define OMAHA_CONSOLE &omaha_console +#else +#define OMAHA_CONSOLE NULL +#endif + +static struct uart_driver omaha_reg = { + .owner = THIS_MODULE, + .normal_major = SERIAL_OMAHA_MAJOR, +#ifdef CONFIG_DEVFS_FS + .normal_name = "ttyOM%d", + .callout_name = "cuaom%d", +#else + .normal_name = "ttyOM", + .callout_name = "cuaom", +#endif + .normal_driver = &normal, + .callout_major = CALLOUT_OMAHA_MAJOR, + .callout_driver = &callout, + .table = omaha_table, + .termios = omaha_termios, + .termios_locked = omaha_termios_locked, + .minor = SERIAL_OMAHA_MINOR, + .nr = UART_NR, + .port = omaha_ports, + .cons = OMAHA_CONSOLE, +}; + +static int __init omahauart_init(void) +{ + return uart_register_driver(&omaha_reg); +} + +static void __exit omahauart_exit(void) +{ + uart_unregister_driver(&omaha_reg); +} + +module_init(omahauart_init); +module_exit(omahauart_exit); --- /dev/null +++ linux-2.4.27/drivers/serial/sa1100.c @@ -0,0 +1,904 @@ +/* + * linux/drivers/char/serial_sa1100.c + * + * Driver for SA11x0 serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: sa1100.c,v 1.14.2.4 2002/10/24 09:53:25 rmk Exp $ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +/* We've been assigned a range on the "Low-density serial ports" major */ +#define SERIAL_SA1100_MAJOR 204 +#define CALLOUT_SA1100_MAJOR 205 +#define MINOR_START 5 + +#define NR_PORTS 3 + +#define SA1100_ISR_PASS_LIMIT 256 + +/* + * Convert from ignore_status_mask or read_status_mask to UTSR[01] + */ +#define SM_TO_UTSR0(x) ((x) & 0xff) +#define SM_TO_UTSR1(x) ((x) >> 8) +#define UTSR0_TO_SM(x) ((x)) +#define UTSR1_TO_SM(x) ((x) << 8) + +#define UART_GET_UTCR0(sport) __raw_readl((sport)->port.membase + UTCR0) +#define UART_GET_UTCR1(sport) __raw_readl((sport)->port.membase + UTCR1) +#define UART_GET_UTCR2(sport) __raw_readl((sport)->port.membase + UTCR2) +#define UART_GET_UTCR3(sport) __raw_readl((sport)->port.membase + UTCR3) +#define UART_GET_UTSR0(sport) __raw_readl((sport)->port.membase + UTSR0) +#define UART_GET_UTSR1(sport) __raw_readl((sport)->port.membase + UTSR1) +#define UART_GET_CHAR(sport) __raw_readl((sport)->port.membase + UTDR) + +#define UART_PUT_UTCR0(sport,v) __raw_writel((v),(sport)->port.membase + UTCR0) +#define UART_PUT_UTCR1(sport,v) __raw_writel((v),(sport)->port.membase + UTCR1) +#define UART_PUT_UTCR2(sport,v) __raw_writel((v),(sport)->port.membase + UTCR2) +#define UART_PUT_UTCR3(sport,v) __raw_writel((v),(sport)->port.membase + UTCR3) +#define UART_PUT_UTSR0(sport,v) __raw_writel((v),(sport)->port.membase + UTSR0) +#define UART_PUT_UTSR1(sport,v) __raw_writel((v),(sport)->port.membase + UTSR1) +#define UART_PUT_CHAR(sport,v) __raw_writel((v),(sport)->port.membase + UTDR) + +/* + * This is the size of our serial port register set. + */ +#define UART_PORT_SIZE 0x24 + +static struct tty_driver normal, callout; +static struct tty_struct *sa1100_table[NR_PORTS]; +static struct termios *sa1100_termios[NR_PORTS], *sa1100_termios_locked[NR_PORTS]; +static int (*sa1100_open)(struct uart_port *); +static void (*sa1100_close)(struct uart_port *); +#ifdef SUPPORT_SYSRQ +static struct console sa1100_console; +#endif + +/* + * This determines how often we check the modem status signals + * for any change. They generally aren't connected to an IRQ + * so we have to poll them. We also check immediately before + * filling the TX fifo incase CTS has been dropped. + */ +#define MCTRL_TIMEOUT (250*HZ/1000) + +struct sa1100_port { + struct uart_port port; + struct timer_list timer; + unsigned int old_status; +}; + +/* + * Handle any change of modem status signal since we were last called. + */ +static void sa1100_mctrl_check(struct sa1100_port *sport) +{ + unsigned int status, changed; + + status = sport->port.ops->get_mctrl(&sport->port); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.info->delta_msr_wait); +} + +/* + * This is our per-port timeout handler, for checking the + * modem status signals. + */ +static void sa1100_timeout(unsigned long data) +{ + struct sa1100_port *sport = (struct sa1100_port *)data; + unsigned long flags; + + if (sport->port.info) { + spin_lock_irqsave(&sport->port.lock, flags); + sa1100_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +} + +/* + * interrupts disabled on entry + */ +static void sa1100_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + u32 utcr3; + + utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE); + sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS); +} + +/* + * interrupts may not be disabled on entry + */ +static void sa1100_start_tx(struct uart_port *port, unsigned int tty_start) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + u32 utcr3; + + spin_lock_irqsave(&sport->port.lock, flags); + utcr3 = UART_GET_UTCR3(sport); + sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS); + UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +/* + * Interrupts enabled + */ +static void sa1100_stop_rx(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + u32 utcr3; + + utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void sa1100_enable_ms(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + mod_timer(&sport->timer, jiffies); +} + +static void +sa1100_rx_chars(struct sa1100_port *sport, struct pt_regs *regs) +{ + struct tty_struct *tty = sport->port.info->tty; + unsigned int status, ch, flg, ignored = 0; + + status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | + UTSR0_TO_SM(UART_GET_UTSR0(sport)); + while (status & UTSR1_TO_SM(UTSR1_RNE)) { + ch = UART_GET_CHAR(sport); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + sport->port.icount.rx++; + + flg = TTY_NORMAL; + + /* + * note that the error handling code is + * out of the main execution path + */ + if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) + goto handle_error; + + if (uart_handle_sysrq_char(&sport->port, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | + UTSR0_TO_SM(UART_GET_UTSR0(sport)); + } + out: + tty_flip_buffer_push(tty); + return; + + handle_error: + if (status & UTSR1_TO_SM(UTSR1_PRE)) + sport->port.icount.parity++; + else if (status & UTSR1_TO_SM(UTSR1_FRE)) + sport->port.icount.frame++; + if (status & UTSR1_TO_SM(UTSR1_ROR)) + sport->port.icount.overrun++; + + if (status & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + status &= sport->port.read_status_mask; + + if (status & UTSR1_TO_SM(UTSR1_PRE)) + flg = TTY_PARITY; + else if (status & UTSR1_TO_SM(UTSR1_FRE)) + flg = TTY_FRAME; + + if (status & UTSR1_TO_SM(UTSR1_ROR)) { + /* + * overrun does *not* affect the character + * we read from the FIFO + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + goto error_return; +} + +static void sa1100_tx_chars(struct sa1100_port *sport) +{ + struct circ_buf *xmit = &sport->port.info->xmit; + + if (sport->port.x_char) { + UART_PUT_CHAR(sport, sport->port.x_char); + sport->port.icount.tx++; + sport->port.x_char = 0; + return; + } + + /* + * Check the modem control lines before + * transmitting anything. + */ + sa1100_mctrl_check(sport); + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + sa1100_stop_tx(&sport->port, 0); + return; + } + + /* + * Tried using FIFO (not checking TNF) for fifo fill: + * still had the '4 bytes repeated' problem. + */ + while (UART_GET_UTSR1(sport) & UTSR1_TNF) { + UART_PUT_CHAR(sport, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + sa1100_stop_tx(&sport->port, 0); +} + +static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sa1100_port *sport = dev_id; + unsigned int status, pass_counter = 0; + + spin_lock(&sport->port.lock); + status = UART_GET_UTSR0(sport); + status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; + do { + if (status & (UTSR0_RFS | UTSR0_RID)) { + /* Clear the receiver idle bit, if set */ + if (status & UTSR0_RID) + UART_PUT_UTSR0(sport, UTSR0_RID); + sa1100_rx_chars(sport, regs); + } + + /* Clear the relevant break bits */ + if (status & (UTSR0_RBB | UTSR0_REB)) + UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB)); + + if (status & UTSR0_RBB) + sport->port.icount.brk++; + + if (status & UTSR0_REB) + uart_handle_break(&sport->port); + + if (status & UTSR0_TFS) + sa1100_tx_chars(sport); + if (pass_counter++ > SA1100_ISR_PASS_LIMIT) + break; + status = UART_GET_UTSR0(sport); + status &= SM_TO_UTSR0(sport->port.read_status_mask) | + ~UTSR0_TFS; + } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID)); + spin_unlock(&sport->port.lock); +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int sa1100_tx_empty(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT; +} + +static unsigned int sa1100_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +/* + * Interrupts always disabled. + */ +static void sa1100_break_ctl(struct uart_port *port, int break_state) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + unsigned int utcr3; + + spin_lock_irqsave(&sport->port.lock, flags); + utcr3 = UART_GET_UTCR3(sport); + if (break_state == -1) + utcr3 |= UTCR3_BRK; + else + utcr3 &= ~UTCR3_BRK; + UART_PUT_UTCR3(sport, utcr3); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static int sa1100_startup(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(sport->port.irq, sa1100_int, 0, + "sa11x0-uart", sport); + if (retval) + return retval; + + /* + * If there is a specific "open" function + * (to register control line interrupts) + */ + if (sa1100_open) { + retval = sa1100_open(port); + if (retval) { + free_irq(sport->port.irq, sport); + return retval; + } + } + + /* + * Finally, clear and enable interrupts + */ + UART_PUT_UTSR0(sport, -1); + UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE); + + return 0; +} + +static void sa1100_shutdown(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + /* + * Stop our timer. + */ + del_timer_sync(&sport->timer); + + /* + * Free the interrupt + */ + free_irq(sport->port.irq, sport); + + /* + * If there is a specific "close" function (to unregister + * control line interrupts) + */ + if (sa1100_close) + sa1100_close(port); + + /* + * Disable all interrupts, port and break condition. + */ + UART_PUT_UTCR3(sport, 0); +} + +static void sa1100_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + unsigned int utcr0, old_utcr3; + + if ((cflag & CSIZE) == CS8) + utcr0 = UTCR0_DSS; + else + utcr0 = 0; + + if (cflag & CSTOPB) + utcr0 |= UTCR0_SBS; + if (cflag & PARENB) { + utcr0 |= UTCR0_PE; + if (!(cflag & PARODD)) + utcr0 |= UTCR0_OES; + } + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS); + sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR); + if (iflag & INPCK) + sport->port.read_status_mask |= + UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); + if (iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= + UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); + + /* + * Characters to ignore + */ + sport->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + sport->port.ignore_status_mask |= + UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); + if (iflag & IGNBRK) { + sport->port.ignore_status_mask |= + UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + sport->port.ignore_status_mask |= + UTSR1_TO_SM(UTSR1_ROR); + } + + del_timer_sync(&sport->timer); + + /* + * disable interrupts and drain transmitter + */ + old_utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)); + + while (UART_GET_UTSR1(sport) & UTSR1_TBY) + barrier(); + + /* then, disable everything */ + UART_PUT_UTCR3(sport, 0); + + /* set the parity, stop bits and data size */ + UART_PUT_UTCR0(sport, utcr0); + + /* set the baud rate */ + quot -= 1; + UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8)); + UART_PUT_UTCR2(sport, (quot & 0xff)); + + UART_PUT_UTSR0(sport, -1); + + UART_PUT_UTCR3(sport, old_utcr3); + + if (UART_ENABLE_MS(&sport->port, cflag)) + sa1100_enable_ms(&sport->port); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *sa1100_type(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return sport->port.type == PORT_SA1100 ? "SA1100" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void sa1100_release_port(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + release_mem_region(sport->port.mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int sa1100_request_port(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, + "sa11x0-uart") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void sa1100_config_port(struct uart_port *port, int flags) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + if (flags & UART_CONFIG_TYPE && + sa1100_request_port(&sport->port) == 0) + sport->port.type = PORT_SA1100; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_SA1100 and PORT_UNKNOWN + */ +static int sa1100_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)sport->port.mapbase != ser->iomem_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops sa1100_pops = { + .tx_empty = sa1100_tx_empty, + .set_mctrl = sa1100_set_mctrl, + .get_mctrl = sa1100_get_mctrl, + .stop_tx = sa1100_stop_tx, + .start_tx = sa1100_start_tx, + .stop_rx = sa1100_stop_rx, + .enable_ms = sa1100_enable_ms, + .break_ctl = sa1100_break_ctl, + .startup = sa1100_startup, + .shutdown = sa1100_shutdown, + .change_speed = sa1100_change_speed, + .type = sa1100_type, + .release_port = sa1100_release_port, + .request_port = sa1100_request_port, + .config_port = sa1100_config_port, + .verify_port = sa1100_verify_port, +}; + +static struct sa1100_port sa1100_ports[NR_PORTS]; + +/* + * Setup the SA1100 serial ports. Note that we don't include the IrDA + * port here since we have our own SIR/FIR driver (see drivers/net/irda) + * + * Note also that we support "console=ttySAx" where "x" is either 0 or 1. + * Which serial port this ends up being depends on the machine you're + * running this kernel on. I'm not convinced that this is a good idea, + * but that's the way it traditionally works. + * + * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer + * used here. + */ +static void __init sa1100_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < NR_PORTS; i++) { + sa1100_ports[i].port.uartclk = 3686400; + sa1100_ports[i].port.ops = &sa1100_pops; + sa1100_ports[i].port.fifosize = 8; + sa1100_ports[i].port.line = i; + sa1100_ports[i].port.iotype = SERIAL_IO_MEM; + init_timer(&sa1100_ports[i].timer); + sa1100_ports[i].timer.function = sa1100_timeout; + sa1100_ports[i].timer.data = (unsigned long)&sa1100_ports[i]; + } + + /* + * make transmit lines outputs, so that when the port + * is closed, the output is in the MARK state. + */ + PPDR |= PPC_TXD1 | PPC_TXD3; + PPSR |= PPC_TXD1 | PPC_TXD3; +} + +void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns) +{ + if (fns->enable_ms) + sa1100_pops.enable_ms = fns->enable_ms; + if (fns->get_mctrl) + sa1100_pops.get_mctrl = fns->get_mctrl; + if (fns->set_mctrl) + sa1100_pops.set_mctrl = fns->set_mctrl; + sa1100_open = fns->open; + sa1100_close = fns->close; + sa1100_pops.pm = fns->pm; + sa1100_pops.set_wake = fns->set_wake; +} + +void __init sa1100_register_uart(int idx, int port) +{ + if (idx >= NR_PORTS) { + printk(KERN_ERR "%s: bad index number %d\n", __FUNCTION__, idx); + return; + } + + switch (port) { + case 1: + sa1100_ports[idx].port.membase = (void *)&Ser1UTCR0; + sa1100_ports[idx].port.mapbase = _Ser1UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser1UART; + sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; + + case 2: + sa1100_ports[idx].port.membase = (void *)&Ser2UTCR0; + sa1100_ports[idx].port.mapbase = _Ser2UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser2ICP; + sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; + + case 3: + sa1100_ports[idx].port.membase = (void *)&Ser3UTCR0; + sa1100_ports[idx].port.mapbase = _Ser3UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser3UART; + sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; + + default: + printk(KERN_ERR "%s: bad port number %d\n", __FUNCTION__, port); + } +} + + +#ifdef CONFIG_SERIAL_SA1100_CONSOLE + +/* + * Interrupts are disabled on entering + */ +static void +sa1100_console_write(struct console *co, const char *s, unsigned int count) +{ + struct sa1100_port *sport = &sa1100_ports[co->index]; + unsigned int old_utcr3, status, i; + + /* + * First, save UTCR3 and then disable interrupts + */ + old_utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) | + UTCR3_TXE); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_UTSR1(sport); + } while (!(status & UTSR1_TNF)); + UART_PUT_CHAR(sport, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_UTSR1(sport); + } while (!(status & UTSR1_TNF)); + UART_PUT_CHAR(sport, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore UTCR3 + */ + do { + status = UART_GET_UTSR1(sport); + } while (status & UTSR1_TBY); + UART_PUT_UTCR3(sport, old_utcr3); +} + +static kdev_t sa1100_console_device(struct console *co) +{ + return MKDEV(SERIAL_SA1100_MAJOR, MINOR_START + co->index); +} + +/* + * If the port was already initialised (eg, by a boot loader), try to determine + * the current setup. + */ +static void __init +sa1100_console_get_options(struct sa1100_port *sport, int *baud, + int *parity, int *bits) +{ + unsigned int utcr3; + + utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE); + if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) { + /* ok, the port was enabled */ + unsigned int utcr0, quot; + + utcr0 = UART_GET_UTCR0(sport); + + *parity = 'n'; + if (utcr0 & UTCR0_PE) { + if (utcr0 & UTCR0_OES) + *parity = 'e'; + else + *parity = 'o'; + } + + if (utcr0 & UTCR0_DSS) + *bits = 8; + else + *bits = 7; + + quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8; + quot &= 0xfff; + *baud = sport->port.uartclk / (16 * (quot + 1)); + } +} + +static int __init +sa1100_console_setup(struct console *co, char *options) +{ + struct sa1100_port *sport; + int baud = CONFIG_SA1100_DEFAULT_BAUDRATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= NR_PORTS) + co->index = 0; + sport = &sa1100_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + sa1100_console_get_options(sport, &baud, &parity, &bits); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct console sa1100_console = { + .name = "ttySA", + .write = sa1100_console_write, + .device = sa1100_console_device, + .setup = sa1100_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init sa1100_rs_console_init(void) +{ + sa1100_init_ports(); + register_console(&sa1100_console); +} + +#define SA1100_CONSOLE &sa1100_console +#else +#define SA1100_CONSOLE NULL +#endif + +static struct uart_driver sa1100_reg = { + .owner = THIS_MODULE, + .normal_major = SERIAL_SA1100_MAJOR, +#ifdef CONFIG_DEVFS_FS + .normal_name = "ttySA%d", + .callout_name = "cusa%d", +#else + .normal_name = "ttySA", + .callout_name = "cusa", +#endif + .normal_driver = &normal, + .callout_major = CALLOUT_SA1100_MAJOR, + .callout_driver = &callout, + .table = sa1100_table, + .termios = sa1100_termios, + .termios_locked = sa1100_termios_locked, + .minor = MINOR_START, + .nr = NR_PORTS, + .cons = SA1100_CONSOLE, +}; + +static int __init sa1100_serial_init(void) +{ + int i, ret; + + sa1100_init_ports(); + + ret = uart_register_driver(&sa1100_reg); + if (ret) + return ret; + + for (i = 0; i < NR_PORTS; i++) + uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port); + + return 0; +} + +static void __exit sa1100_serial_exit(void) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_remove_one_port(&sa1100_reg, &sa1100_ports[i].port); + + uart_unregister_driver(&sa1100_reg); +} + +module_init(sa1100_serial_init); +module_exit(sa1100_serial_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("SA1100 generic serial port driver"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/serial/uart00.c @@ -0,0 +1,903 @@ +/* + * linux/drivers/serial/uart00.c + * + * Driver for UART00 serial ports + * + * Based on drivers/char/serial_amba.c, by ARM Limited & + * Deep Blue Solutions Ltd. + * Copyright 2001 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: uart00.c,v 1.3.2.5 2002/10/24 09:53:26 rmk Exp $ + * + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL_UART00_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#define UART00_TYPE (volatile unsigned int*) +#include +#include + +#undef DEBUG +#define UART_NR 2 + +#define SERIAL_UART00_NAME "ttyUA" +#define SERIAL_UART00_MAJOR 204 +#define SERIAL_UART00_MINOR 16 /* Temporary - will change in future */ +#define SERIAL_UART00_NR UART_NR +#define UART_PORT_SIZE 0x50 + +#define CALLOUT_UART00_NAME "cuaua" +#define CALLOUT_UART00_MAJOR 205 +#define CALLOUT_UART00_MINOR 16 /* Temporary - will change in future */ +#define CALLOUT_UART00_NR UART_NR + +static struct tty_driver normal, callout; +static struct tty_struct *uart00_table[UART_NR]; +static struct termios *uart00_termios[UART_NR], *uart00_termios_locked[UART_NR]; +static struct console uart00_console; +static struct uart_driver uart00_reg; + + +#define UART00_ISR_PASS_LIMIT 256 + +/* + * Access macros for the UART00 UARTs + */ +#define UART_GET_INT_STATUS(p) inl(UART_ISR((p)->membase)) +#define UART_PUT_IES(p, c) outl(c,UART_IES((p)->membase)) +#define UART_GET_IES(p) inl(UART_IES((p)->membase)) +#define UART_PUT_IEC(p, c) outl(c,UART_IEC((p)->membase)) +#define UART_GET_IEC(p) inl(UART_IEC((p)->membase)) +#define UART_PUT_CHAR(p, c) outl(c,UART_TD((p)->membase)) +#define UART_GET_CHAR(p) inl(UART_RD((p)->membase)) +#define UART_GET_RSR(p) inl(UART_RSR((p)->membase)) +#define UART_GET_RDS(p) inl(UART_RDS((p)->membase)) +#define UART_GET_MSR(p) inl(UART_MSR((p)->membase)) +#define UART_GET_MCR(p) inl(UART_MCR((p)->membase)) +#define UART_PUT_MCR(p, c) outl(c,UART_MCR((p)->membase)) +#define UART_GET_MC(p) inl(UART_MC((p)->membase)) +#define UART_PUT_MC(p, c) outl(c,UART_MC((p)->membase)) +#define UART_GET_TSR(p) inl(UART_TSR((p)->membase)) +#define UART_GET_DIV_HI(p) inl(UART_DIV_HI((p)->membase)) +#define UART_PUT_DIV_HI(p,c) outl(c,UART_DIV_HI((p)->membase)) +#define UART_GET_DIV_LO(p) inl(UART_DIV_LO((p)->membase)) +#define UART_PUT_DIV_LO(p,c) outl(c,UART_DIV_LO((p)->membase)) +#define UART_RX_DATA(s) ((s) & UART_RSR_RX_LEVEL_MSK) +#define UART_TX_READY(s) (((s) & UART_TSR_TX_LEVEL_MSK) < 15) +//#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & UART00_UARTFR_TMSK) == 0) + +static void uart00_stop_tx(struct uart_port *port, u_int from_tty) +{ + + UART_PUT_IEC(port, UART_IEC_TIE_MSK); +} + +static void uart00_stop_rx(struct uart_port *port) +{ + + UART_PUT_IEC(port, UART_IEC_RE_MSK); +} + +static void uart00_enable_ms(struct uart_port *port) +{ + + UART_PUT_IES(port, UART_IES_ME_MSK); +} + +static void +uart00_rx_chars(struct uart_port *port, struct pt_regs *regs) +{ + struct uart_info *info = port->info; + struct tty_struct *tty = info->tty; + unsigned int status, ch, rds, flg, ignored = 0; + + + status = UART_GET_RSR(port); + while (UART_RX_DATA(status)) { + + /* + * We need to read rds before reading the + * character from the fifo + */ + rds = UART_GET_RDS(port); + ch = UART_GET_CHAR(port); + port->icount.rx++; + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + + flg = TTY_NORMAL; + + /* + * Note that the error handling code is + * out of the main execution path + */ + if (rds & (UART_RDS_BI_MSK |UART_RDS_FE_MSK|UART_RDS_PE_MSK)) + goto handle_error; + if (uart_handle_sysrq_char(port, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = UART_GET_RSR(port); + } +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + if (rds & UART_RDS_BI_MSK) { + status &= ~(UART_RDS_FE_MSK | UART_RDS_PE_MSK); + port->icount.brk++; + +#ifdef SUPPORT_SYSRQ + if (uart_handle_break(port)) + goto ignore_char; +#endif + } else if (rds & UART_RDS_PE_MSK) + port->icount.parity++; + else if (rds & UART_RDS_FE_MSK) + port->icount.frame++; + if (rds & UART_RDS_OE_MSK) + port->icount.overrun++; + + if (rds & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + rds &= port->read_status_mask; + + if (rds & UART_RDS_BI_MSK) + flg = TTY_BREAK; + else if (rds & UART_RDS_PE_MSK) + flg = TTY_PARITY; + else if (rds & UART_RDS_FE_MSK) + flg = TTY_FRAME; + + if (rds & UART_RDS_OE_MSK) { + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +static void uart00_tx_chars(struct uart_port *port) +{ + int count; + struct uart_info *info = port->info; + + if (port->x_char) { + while((UART_GET_TSR(port)& UART_TSR_TX_LEVEL_MSK)==15); + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + uart00_stop_tx(port, 0); + return; + } + + count = port->fifosize >> 1; + do { + while((UART_GET_TSR(port)& UART_TSR_TX_LEVEL_MSK)==15); + UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + UART_XMIT_SIZE) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (info->xmit.head == info->xmit.tail) + uart00_stop_tx(port, 0); +} + +static void uart00_start_tx(struct uart_port *port, u_int from_tty) +{ + UART_PUT_IES(port,UART_IES_TIE_MSK ); + uart00_tx_chars(port); +} + +static void uart00_modem_status(struct uart_port *port) +{ + unsigned int status; + struct uart_icount *icount = &port->icount; + struct uart_info *info = port->info; + + status = UART_GET_MSR(port); + + if (!status & (UART_MSR_DCTS_MSK | UART_MSR_DDSR_MSK | + UART_MSR_TERI_MSK | UART_MSR_DDCD_MSK)) + return; + + if (status & UART_MSR_DDCD_MSK) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((port->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD_MSK)) + hardpps(); +#endif + if (info->flags & ASYNC_CHECK_CD) { + if (status & UART_MSR_DCD_MSK) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_CALLOUT_NOHUP))) { + if (info->tty) + tty_hangup(info->tty); + } + } + } + + if (status & UART_MSR_DDSR_MSK) + icount->dsr++; + + if (status & UART_MSR_DCTS_MSK) { + icount->cts++; + + if (info->flags & ASYNC_CTS_FLOW) { + status &= UART_MSR_CTS_MSK; + + if (info->tty->hw_stopped) { + if (status) { + info->tty->hw_stopped = 0; + port->ops->start_tx(port, 0); + uart_write_wakeup(port); + } + } else { + if (!status) { + info->tty->hw_stopped = 1; + port->ops->stop_tx(port, 0); + } + } + } + } + wake_up_interruptible(&info->delta_msr_wait); + +} + +static void uart00_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = dev_id; + unsigned int status, pass_counter = 0; + + status = UART_GET_INT_STATUS(port); + do { + + if (status & UART_ISR_RI_MSK) + uart00_rx_chars(port, regs); + if (status & (UART_ISR_TI_MSK | UART_ISR_TII_MSK)) + uart00_tx_chars(port); + if (status & UART_ISR_MI_MSK) + uart00_modem_status(port); + if (pass_counter++ > UART00_ISR_PASS_LIMIT) + break; + + status = UART_GET_INT_STATUS(port); + } while (status); +} + +static u_int uart00_tx_empty(struct uart_port *port) +{ + return UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK? 0 : TIOCSER_TEMT; +} + +static u_int uart00_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int status; + + status = UART_GET_MSR(port); + if (status & UART_MSR_DCD_MSK) + result |= TIOCM_CAR; + if (status & UART_MSR_DSR_MSK) + result |= TIOCM_DSR; + if (status & UART_MSR_CTS_MSK) + result |= TIOCM_CTS; + if (status & UART_MCR_RI_MSK) + result |= TIOCM_RNG; + + return result; +} + +static void uart00_set_mctrl(struct uart_port *port, u_int mctrl) +{ + unsigned char mcr = 0; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS_MSK; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR_MSK; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LB_MSK; + + UART_PUT_MCR(port, mcr); +} + +static void uart00_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int mcr; + + mcr = UART_GET_MCR(port); + if (break_state == -1) + mcr |= UART_MCR_BR_MSK; + else + mcr &= ~UART_MCR_BR_MSK; + UART_PUT_MCR(port, mcr); +} + +static inline u_int uart_calculate_quot(struct uart_port *port, u_int baud) +{ + u_int quot; + + /* Special case: B0 rate */ + if (!baud) + baud = 9600; + + quot = (port->uartclk / (16 * baud)-1) ; + + return quot; +} +static void uart00_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) +{ + u_int uart_mc=0, old_ies; + unsigned long flags; + +#ifdef DEBUG + printk("uart00_set_cflag(0x%x) called\n", cflag); +#endif + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: uart_mc = UART_MC_CLS_CHARLEN_5; break; + case CS6: uart_mc = UART_MC_CLS_CHARLEN_6; break; + case CS7: uart_mc = UART_MC_CLS_CHARLEN_7; break; + default: uart_mc = UART_MC_CLS_CHARLEN_8; break; // CS8 + } + if (cflag & CSTOPB) + uart_mc|= UART_MC_ST_TWO; + if (cflag & PARENB) { + uart_mc |= UART_MC_PE_MSK; + if (!(cflag & PARODD)) + uart_mc |= UART_MC_EP_MSK; + } + + port->read_status_mask = UART_RDS_OE_MSK; + if (iflag & INPCK) + port->read_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_RDS_BI_MSK; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK; + if (iflag & IGNBRK) { + port->ignore_status_mask |= UART_RDS_BI_MSK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (iflag & IGNPAR) + port->ignore_status_mask |= UART_RDS_OE_MSK; + } + + /* first, disable everything */ + save_flags(flags); cli(); + old_ies = UART_GET_IES(port); + + if ((port->flags & ASYNC_HARDPPS_CD) || + (cflag & CRTSCTS) || !(cflag & CLOCAL)) + old_ies |= UART_IES_ME_MSK; + + + /* Set baud rate */ + UART_PUT_DIV_LO(port, (quot & 0xff)); + UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8)); + + + UART_PUT_MC(port, uart_mc); + UART_PUT_IES(port, old_ies); + + restore_flags(flags); +} + +static int uart00_startup(struct uart_port *port) +{ + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, uart00_int, 0, "uart00", port); + if (retval) + return retval; + + /* + * Finally, enable interrupts. Use the TII interrupt to minimise + * the number of interrupts generated. If higher performance is + * needed, consider using the TI interrupt with a suitable FIFO + * threshold + */ + UART_PUT_IES(port, UART_IES_RE_MSK | UART_IES_TIE_MSK); + + return 0; +} + +static void uart00_shutdown(struct uart_port *port) +{ + /* + * disable all interrupts, disable the port + */ + UART_PUT_IEC(port, 0xff); + + /* disable break condition and fifos */ + UART_PUT_MCR(port, UART_GET_MCR(port) &~UART_MCR_BR_MSK); + + /* + * Free the interrupt + */ + free_irq(port->irq, port); +} + +static const char *uart00_type(struct uart_port *port) +{ + return port->type == PORT_UART00 ? "Altera UART00" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void uart00_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); + +#ifdef CONFIG_ARCH_CAMELOT + if(port->membase!=(void*)IO_ADDRESS(EXC_UART00_BASE)){ + iounmap(port->membase); + } +#endif +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int uart00_request_port(struct uart_port *port) +{ + int result; + + result = request_mem_region(port->mapbase, UART_PORT_SIZE, + "serial_uart00") != NULL ? 0 : -EBUSY; + if (result) + return result; + + port->membase = ioremap(port->mapbase, SZ_4K); + if (!port->membase) { + printk(KERN_ERR "serial00: cannot map io memory\n"); + release_mem_region(port->mapbase, UART_PORT_SIZE); + } + + return port->membase ? 0 : -ENOMEM; +} + +/* + * Configure/autoconfigure the port. + */ +static void uart00_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + if (uart00_request_port(port) == 0) + port->type = PORT_UART00; + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int uart00_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops uart00_pops = { + tx_empty: uart00_tx_empty, + set_mctrl: uart00_set_mctrl, + get_mctrl: uart00_get_mctrl, + stop_tx: uart00_stop_tx, + start_tx: uart00_start_tx, + stop_rx: uart00_stop_rx, + enable_ms: uart00_enable_ms, + break_ctl: uart00_break_ctl, + startup: uart00_startup, + shutdown: uart00_shutdown, + change_speed: uart00_change_speed, + type: uart00_type, + release_port: uart00_release_port, + request_port: uart00_request_port, + config_port: uart00_config_port, + verify_port: uart00_verify_port, +}; + +static struct uart_port uart00_ports[UART_NR] = { + +#ifdef CONFIG_ARCH_CAMELOT +{ + .membase = (void*)IO_ADDRESS(EXC_UART00_BASE), + .mapbase = EXC_UART00_BASE, + .iotype = SERIAL_IO_MEM, + .irq = IRQ_UART, + .uartclk = EXC_AHB2_CLK_FREQUENCY, + .fifosize = 16, + .ops = &uart00_pops, + .flags = ASYNC_BOOT_AUTOCONF, +} +#endif +}; + +#ifdef CONFIG_SERIAL_UART00_CONSOLE +static void uart00_console_write(struct console *co, const char *s, unsigned count) +{ +#ifdef CONFIG_ARCH_CAMELOT + struct uart_port *port = &uart00_ports[0]; + unsigned int status, old_ies; + int i; + + /* + * First save the CR then disable the interrupts + */ + old_ies = UART_GET_IES(port); + UART_PUT_IEC(port,0xff); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_TSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_TSR(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IES + */ + do { + status = UART_GET_TSR(port); + } while (status & UART_TSR_TX_LEVEL_MSK); + UART_PUT_IES(port, old_ies); +#endif +} + +static kdev_t uart00_console_device(struct console *co) +{ + return MKDEV(SERIAL_UART00_MAJOR, SERIAL_UART00_MINOR + co->index); +} + +static void /*__init*/ uart00_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + u_int uart_mc, quot; + uart_mc= UART_GET_MC(port); + + *parity = 'n'; + if (uart_mc & UART_MC_PE_MSK) { + if (uart_mc & UART_MC_EP_MSK) + *parity = 'e'; + else + *parity = 'o'; + } + + switch (uart_mc & UART_MC_CLS_MSK){ + + case UART_MC_CLS_CHARLEN_5: + *bits = 5; + break; + case UART_MC_CLS_CHARLEN_6: + *bits = 6; + break; + case UART_MC_CLS_CHARLEN_7: + *bits = 7; + break; + case UART_MC_CLS_CHARLEN_8: + *bits = 8; + break; + } + quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8); + *baud = port->uartclk / (16 *quot ); +} + +static int __init uart00_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow= 'n'; + +#ifdef CONFIG_ARCH_CAMELOT + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + port = &uart00_ports[0]; + co->index = 0; +#else + return -ENODEV; +#endif + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + uart00_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console uart00_console = { + .name = SERIAL_UART00_NAME, + .write = uart00_console_write, + .device = uart00_console_device, + .setup = uart00_console_setup, + .flags = CON_PRINTBUFFER, + .index = 0, +}; + +void __init uart00_console_init(void) +{ + register_console(&uart00_console); +} + +#define UART00_CONSOLE &uart00_console +#else +#define UART00_CONSOLE NULL +#endif + +static struct uart_driver uart00_reg = { + .owner = NULL, + .normal_major = SERIAL_UART00_MAJOR, + .normal_name = SERIAL_UART00_NAME, + .normal_driver = &normal, + .callout_major = CALLOUT_UART00_MAJOR, + .callout_name = CALLOUT_UART00_NAME, + .callout_driver = &callout, + .table = uart00_table, + .termios = uart00_termios, + .termios_locked = uart00_termios_locked, + .minor = SERIAL_UART00_MINOR, + .nr = UART_NR, + .state = NULL, + .cons = UART00_CONSOLE, +}; + +struct dev_port_entry{ + struct uart_port *port; +}; + +static struct dev_port_entry dev_port_map[UART_NR]; + +#ifdef CONFIG_PLD_HOTSWAP +/* + * Keep a mapping of dev_info addresses -> port lines to use when + * removing ports dev==NULL indicates unused entry + */ + +struct uart00_ps_data{ + unsigned int clk; + unsigned int fifosize; +}; + +int uart00_add_device(struct pldhs_dev_info* dev_info, void* dev_ps_data) +{ + struct uart00_ps_data* dev_ps=dev_ps_data; + struct uart_port * port; + int i,result; + + i=0; + while(dev_port_map[i].port) + i++; + + if(i==UART_NR){ + printk(KERN_WARNING "uart00: Maximum number of ports reached\n"); + return 0; + } + + port=&uart00_ports[i]; + + printk("clk=%d fifo=%d\n",dev_ps->clk,dev_ps->fifosize); + port->membase=0; + port->mapbase=dev_info->base_addr; + port->iotype=SERIAL_IO_MEM; + port->irq=dev_info->irq; + port->uartclk=dev_ps->clk; + port->fifosize=dev_ps->fifosize; + port->ops=&uart00_pops; + port->line=i; + port->flags=ASYNC_BOOT_AUTOCONF; + + result=uart_register_port(&uart00_reg, port); + if(result<0){ + printk("uart_register_port returned %d\n",result); + return result; + } + dev_port_map[i].port=port; + printk("uart00: added device at %lx as ttyUA%d\n",dev_port_map[i].port->mapbase,i); + return 0; + +} + +int uart00_remove_devices(void) +{ + int i,result; + + + result=0; + for(i=1;iuartclk,port->fifosize); + len+=PLDHS_READ_PROC_DATA(buf+len,"uart00",i, + port->mapbase,port->irq,ps_data); + + } + } + *eof=1; + return len; +} + + + +#endif +struct pld_hotswap_ops uart00_pldhs_ops={ + .name = "uart00", + .add_device = uart00_add_device, + .remove_devices = uart00_remove_devices, + .proc_read = uart00_proc_read +}; + +#endif + +static int __init uart00_init(void) +{ + int ret; + int i; + + ret = uart_register_driver(&uart00_reg); + if (ret) { + printk(KERN_ERR "uart00: Couldn't register driver\n"); + return ret; + } + + unregister_console(&uart00_console); + + for(i=0;i + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * This is the machine specific part of the Assabet/UDA1341 support. + * This driver makes use of the UDA1341 and the sa1100-audio modules. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial release. + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-17 Nicolas Pitre Supports 44100Hz and 22050Hz samplerate now. + * + * 2001-08-03 Russell King Fix left/right channel swap. + * Attempt to reduce power consumption when idle. + * + * 2001-09-23 Russell King Remove old L3 bus driver. + * + * Please note that fiddling too much with MDREFR results in oopses, so we don't + * touch MDREFR unnecessarily, which means we don't touch it on close. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" + +/* + * Define this to fix the power drain on early Assabets + */ +#define FIX_POWER_DRAIN + +/* + * Debugging? + */ +#undef DEBUG + + +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_RATE_DEFAULT 44100 + +/* + * Mixer (UDA1341) interface + */ + +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + +static struct file_operations assabet_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + + +/* + * Audio interface + */ +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +/* + * FIXME: what about SFRM going high when SSP is disabled? + */ +static void assabet_set_samplerate(long val) +{ + struct uda1341_cfg cfg; + u_int clk_ref, clk_div; + + /* We don't want to mess with clocks when frames are in flight */ + Ser4SSCR0 &= ~SSCR0_SSE; + /* wait for any frame to complete */ + udelay(125); + + /* + * Our clock source is derived from the CPLD on which we don't have + * much control unfortunately. This was intended for a fixed 48000 Hz + * samplerate assuming a core clock of 221.2 MHz. The CPLD appears + * to divide the memory clock so there is a ratio of 4608 between + * the core clock and the resulting samplerate (obtained by + * measurements, the CPLD equations should confirm that). + * + * Still we can play with the SA1110's clock divisor for the SSP port + * to get half the samplerate as well. + * + * Apparently the clock sent to the SA1110 for the SSP port is further + * more divided from the clock sent to the UDA1341 (some people tried + * to be too clever in their design, or simply failed to read the + * SA1110 manual). If it was the same clock we would have been able + * to support a third samplerate with the UDA1341's 384FS mode. + * + * At least it would have been a minimum acceptable solution to be + * able to set the CPLD divisor by software. The iPAQ design is + * certainly a better example to follow for a new design. + */ + clk_ref = cpufreq_get(0) * 1000 / 4608; + if (val > clk_ref*4/7) { + audio_samplerate = clk_ref; + cfg.fs = 256; + clk_div = SSCR0_SerClkDiv(2); + } else { + audio_samplerate = clk_ref/2; + cfg.fs = 512; + clk_div = SSCR0_SerClkDiv(4); + } + + cfg.format = FMT_LSB16; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + + Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; +} + +/* + * Initialise the Assabet audio driver. + * + * Note that we have to be careful with the order that we do things here; + * there is a D-type flip flop which is clocked from the SFRM line which + * indicates whether the same is for the left or right channel to the + * UDA1341. + * + * When you disable the SSP (by clearing SSCR0_SSE) it appears that the + * SFRM signal can float high. When you re-enable the SSP, you clock the + * flip flop once, and end up swapping the left and right channels. + * + * The ASSABET_BCR_CODEC_RST line will force this flip flop into a known + * state, but this line resets other devices as well! + * + * In addition to the above, it appears that powering down the UDA1341 on + * early Assabets leaves the UDA_WS actively driving a logic '1' into the + * chip, wasting power! (you can tell this by D11 being half-on). We + * attempt to correct this by kicking the flip flop on init/open/close. + * We should probably do this on PM resume as well. + * + * (Note the ordering of ASSABET_BCR_AUDIO_ON, SFRM and ASSABET_BCR_CODEC_RST + * is important). + */ +static void assabet_audio_init(void *dummy) +{ + unsigned long flags; + unsigned int mdrefr; + + local_irq_save(flags); + + /* + * Enable the power for the UDA1341 before driving any signals. + * We leave the audio amp (LM4880) disabled for now. + */ + ASSABET_BCR_set(ASSABET_BCR_AUDIO_ON); + +#ifdef FIX_POWER_DRAIN + GPSR = GPIO_SSP_SFRM; + GPCR = GPIO_SSP_SFRM; +#endif + + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + ASSABET_BCR_clear(ASSABET_BCR_STEREO_LB); + + /* + * Setup the SSP uart. + */ + PPAR |= PPAR_SPR; + Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(2); + Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; + GAFR |= GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_CLK; + GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + GPDR &= ~(GPIO_SSP_RXD | GPIO_SSP_CLK); + Ser4SSCR0 |= SSCR0_SSE; + + /* + * Only give SFRM to the SSP after it has been enabled. + */ + GAFR |= GPIO_SSP_SFRM; + + /* + * The assabet board uses the SDRAM clock as the source clock for + * audio. This is supplied to the SA11x0 from the CPLD on pin 19. + * At 206MHz we need to run the audio clock (SDRAM bank 2) + * at half speed. This clock will scale with core frequency so + * the audio sample rate will also scale. The CPLD on Assabet + * will need to be programmed to match the core frequency. + */ + mdrefr = MDREFR; + if ((mdrefr & (MDREFR_K2DB2 | MDREFR_K2RUN | MDREFR_EAPD | + MDREFR_KAPD)) != (MDREFR_K2DB2 | MDREFR_K2RUN)) { + mdrefr |= MDREFR_K2DB2 | MDREFR_K2RUN; + mdrefr &= ~(MDREFR_EAPD | MDREFR_KAPD); + MDREFR = mdrefr; + (void) MDREFR; + } + local_irq_restore(flags); + + /* Wait for the UDA1341 to wake up */ + mdelay(1); + + l3_open(&uda1341); + + assabet_set_samplerate(audio_samplerate); + + /* Enable the audio power */ + ASSABET_BCR_clear(ASSABET_BCR_QMUTE | ASSABET_BCR_SPK_OFF); +} + +/* + * Shutdown the Assabet audio driver. + * + * We have to be careful about the SFRM line here for the same reasons + * described in the initialisation comments above. This basically means + * that we must hand the SSP pins back to the GPIO module before disabling + * the SSP. + * + * In addition, to reduce power drain, we toggle the SFRM line once so + * that the UDA_WS line is at logic 0. + * + * We can't clear ASSABET_BCR_CODEC_RST without knowing if the UCB1300 or + * ADV7171 driver is still active. If it is, then we still need to play + * games, so we might as well leave ASSABET_BCR_CODEC_RST set. + */ +static void assabet_audio_shutdown(void *dummy) +{ + ASSABET_BCR_set(ASSABET_BCR_STEREO_LB | ASSABET_BCR_QMUTE | + ASSABET_BCR_SPK_OFF); + + l3_close(&uda1341); + + GAFR &= ~(GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM); + Ser4SSCR0 = 0; + +#ifdef FIX_POWER_DRAIN + GPSR = GPIO_SSP_SFRM; + GPCR = GPIO_SSP_SFRM; +#endif + + /* disable the audio power */ + ASSABET_BCR_clear(ASSABET_BCR_AUDIO_ON); +} + +static int assabet_audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) break; + assabet_set_samplerate(val); + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(audio_samplerate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do signed 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + +static audio_stream_t output_stream, input_stream; + +static audio_state_t audio_state = { + output_stream: &output_stream, + output_dma: DMA_Ser4SSPWr, + output_id: "Assabet UDA1341 out", + input_stream: &input_stream, + input_dma: DMA_Ser4SSPRd, + input_id: "Assabet UDA1341 in", + need_tx_for_rx: 1, + hw_init: assabet_audio_init, + hw_shutdown: assabet_audio_shutdown, + client_ioctl: assabet_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int assabet_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations assabet_audio_fops = { + open: assabet_audio_open, + owner: THIS_MODULE +}; + + +static int audio_dev_id, mixer_dev_id; + +static int __init assabet_uda1341_init(void) +{ + int ret; + + if (!machine_is_assabet()) + return -ENODEV; + + ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); + if (ret) + goto out; + + /* register devices */ + audio_dev_id = register_sound_dsp(&assabet_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&assabet_mixer_fops, -1); + +#ifdef FIX_POWER_DRAIN + { + unsigned long flags; + local_irq_save(flags); + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + GPSR = GPIO_SSP_SFRM; + GPDR |= GPIO_SSP_SFRM; + GPCR = GPIO_SSP_SFRM; + local_irq_restore(flags); + } +#endif + + printk(KERN_INFO "Sound: Assabet UDA1341: dsp id %d mixer id %d\n", + audio_dev_id, mixer_dev_id); + return 0; + +release_l3: + l3_detach_client(&uda1341); +out: + return ret; +} + +static void __exit assabet_uda1341_exit(void) +{ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + l3_detach_client(&uda1341); +} + +module_init(assabet_uda1341_init); +module_exit(assabet_uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Glue audio driver for the SA1110 Assabet board & Philips UDA1341 codec."); + +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/sound/h3600-uda1341.c @@ -0,0 +1,352 @@ +/* + * Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec. + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * This is the machine specific part of the Compaq iPAQ (aka Bitsy) support. + * This driver makes use of the UDA1341 and the sa1100-audio modules. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial UDA1341 driver release. + * + * 2000-07-?? George France Bitsy support. + * + * 2000-12-13 Deborah Wallach Fixed power handling for iPAQ/h3600 + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-13 Nicolas Pitre Fixes for all supported samplerates. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +//#include + +#include "sa1100-audio.h" + + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_NAME "Bitsy_UDA1341" + +#define AUDIO_RATE_DEFAULT 44100 + + +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + +static struct file_operations h3600_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + + +/* + * Audio interface + */ + +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +/* + * Stop-gap solution until rest of hh.org HAL stuff is merged. + */ +#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12) +#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13) +static void h3600_set_audio_clock(long val) +{ + switch (val) { + case 24000: case 32000: case 48000: /* 00: 12.288 MHz */ + GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; + break; + + case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */ + GPSR = GPIO_H3600_CLK_SET0; + GPCR = GPIO_H3600_CLK_SET1; + break; + + case 8000: case 10666: case 16000: /* 10: 4.096 MHz */ + GPCR = GPIO_H3600_CLK_SET0; + GPSR = GPIO_H3600_CLK_SET1; + break; + + case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */ + GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; + break; + } +} + +static void h3600_set_samplerate(long val) +{ + struct uda1341_cfg cfg; + int clk_div = 0; + + /* We don't want to mess with clocks when frames are in flight */ + Ser4SSCR0 &= ~SSCR0_SSE; + /* wait for any frame to complete */ + udelay(125); + + /* + * We have the following clock sources: + * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz + * Those can be divided either by 256, 384 or 512. + * This makes up 12 combinations for the following samplerates... + */ + if (val >= 48000) + val = 48000; + else if (val >= 44100) + val = 44100; + else if (val >= 32000) + val = 32000; + else if (val >= 29400) + val = 29400; + else if (val >= 24000) + val = 24000; + else if (val >= 22050) + val = 22050; + else if (val >= 21970) + val = 21970; + else if (val >= 16000) + val = 16000; + else if (val >= 14647) + val = 14647; + else if (val >= 10985) + val = 10985; + else if (val >= 10666) + val = 10666; + else + val = 8000; + + /* Set the external clock generator */ + h3600_set_audio_clock(val); + + /* Select the clock divisor */ + switch (val) { + case 8000: + case 10985: + case 22050: + case 24000: + cfg.fs = 512; + clk_div = SSCR0_SerClkDiv(16); + break; + case 16000: + case 21970: + case 44100: + case 48000: + cfg.fs = 256; + clk_div = SSCR0_SerClkDiv(8); + break; + case 10666: + case 14647: + case 29400: + case 32000: + cfg.fs = 384; + clk_div = SSCR0_SerClkDiv(12); + break; + } + + cfg.format = FMT_LSB16; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; + audio_samplerate = val; +} + +static void h3600_audio_init(void *dummy) +{ + unsigned long flags; + + /* Setup the uarts */ + local_irq_save(flags); + GAFR |= (GPIO_SSP_CLK); + GPDR &= ~(GPIO_SSP_CLK); + Ser4SSCR0 = 0; + Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8); + Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; + Ser4SSCR0 |= SSCR0_SSE; + + /* Enable the audio power */ + + clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); + set_h3600_egpio(IPAQ_EGPIO_AUDIO_ON); + set_h3600_egpio(IPAQ_EGPIO_QMUTE); + local_irq_restore(flags); + + /* external clock configuration */ + h3600_set_samplerate(audio_samplerate); + + /* Wait for the UDA1341 to wake up */ + set_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); + mdelay(1); + + /* make the left and right channels unswapped (flip the WS latch ) */ + Ser4SSDR = 0; + + /* Initialize the UDA1341 internal state */ + l3_open(&uda1341); + + clr_h3600_egpio(IPAQ_EGPIO_QMUTE); +} + +static void h3600_audio_shutdown(void *dummy) +{ + /* disable the audio power and all signals leading to the audio chip */ + l3_close(&uda1341); + Ser4SSCR0 = 0; + clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); + clr_h3600_egpio(IPAQ_EGPIO_AUDIO_ON); + clr_h3600_egpio(IPAQ_EGPIO_QMUTE); +} + +static int h3600_audio_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) break; + h3600_set_samplerate(val); + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(audio_samplerate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + +static audio_stream_t output_stream, input_stream; + +static audio_state_t audio_state = { + output_stream: &output_stream, + output_dma: DMA_Ser4SSPWr, + output_id: "UDA1341 out", + input_stream: &input_stream, + input_dma: DMA_Ser4SSPRd, + input_id: "UDA1341 in", + need_tx_for_rx: 1, + hw_init: h3600_audio_init, + hw_shutdown: h3600_audio_shutdown, + client_ioctl: h3600_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int h3600_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations h3600_audio_fops = { + open: h3600_audio_open, + owner: THIS_MODULE +}; + + +static int audio_dev_id, mixer_dev_id; + +static int __init h3600_uda1341_init(void) +{ + int ret; + + if (!machine_is_h3xxx()) + return -ENODEV; + + ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); + if (ret) + goto out; + + /* register devices */ + audio_dev_id = register_sound_dsp(&h3600_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&h3600_mixer_fops, -1); + + printk( KERN_INFO "iPAQ audio support initialized\n" ); + return 0; + +release_l3: + l3_detach_client(&uda1341); +out: + return ret; +} + +static void __exit h3600_uda1341_exit(void) +{ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + l3_detach_client(&uda1341); +} + +module_init(h3600_uda1341_init); +module_exit(h3600_uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre, George France"); +MODULE_DESCRIPTION("Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec."); + +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/sound/pangolin-uda1341.c @@ -0,0 +1,322 @@ +/* + * Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec. + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * This is the machine specific part of the Pangolin/UDA1341 support. + * This driver makes use of the UDA1341 and the sa1100-audio modules. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial release. + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-17 Nicolas Pitre Supports 44100Hz and 22050Hz samplerate now. + * + * 2001-08-06 Richard Fan Pangolin Support + * + * 2001-09-23 Russell King Update inline with Assabet driver + * Remove old L3 bus driver + * + * Note: this should probably be merged with the Assabet audio driver, + * and become the "SDRAM-clock driven" SA1100 audio driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "sa1100-audio.h" + +/* + * Debugging? + */ +#undef DEBUG + + +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_RATE_DEFAULT 44100 + +#define QmutePin GPIO_GPIO(4) +#define SpeakerOffPin GPIO_GPIO(5) + +/* + * Mixer (UDA1341) interface + */ + +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + +static struct file_operations pangolin_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + + +/* + * Audio interface + */ +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +static void pangolin_set_samplerate(long val) +{ + struct uda1341_cfg cfg; + int clk_div; + + /* We don't want to mess with clocks when frames are in flight */ + Ser4SSCR0 &= ~SSCR0_SSE; + /* wait for any frame to complete */ + udelay(125); + + /* + * Our clock source is derived from the CPLD on which we don't have + * much control unfortunately. This was intended for a fixed 44100Hz + * samplerate assuming a core clock of 206 MHz. Still we can play + * with the SA1110's clock divisor for the SSP port to get a 22050Hz + * samplerate. + * + * Apparently the clock sent to the SA1110 for the SSP port is + * divided from the clock sent to the UDA1341 (some people tried to + * be too clever in their design, or simply failed to read the SA1110 + * manual). If it was the same source we would have been able to + * support a third samplerate. + * + * At least it would have been a minimum acceptable solution to be + * able to set the CPLD divisor by software. The iPAQ design is + * certainly a better example to follow for a new design. + */ + if (val >= 44100) { + audio_samplerate = 44100; + cfg.fs = 256; + clk_div = SSCR0_SerClkDiv(2); + } else { + audio_samplerate = 22050; + cfg.fs = 512; + clk_div = SSCR0_SerClkDiv(4); + } + + cfg.format = FMT_LSB16; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + + Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; +} + +static void pangolin_audio_init(void *dummy) +{ + unsigned long flags; + unsigned int mdrefr; + + local_irq_save(flags); + + /* + * Setup the SSP uart. + */ + PPAR |= PPAR_SPR; + Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(2); + Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; + GAFR |= GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_CLK | + GPIO_SSP_SFRM; + GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + GPDR &= ~(GPIO_SSP_RXD | GPIO_SSP_CLK); + Ser4SSCR0 |= SSCR0_SSE; + + GAFR &= ~(SpeakerOffPin | QmutePin); + GPDR |= (SpeakerOffPin | QmutePin); + GPCR = SpeakerOffPin; + + /* + * The assabet board uses the SDRAM clock as the source clock for + * audio. This is supplied to the SA11x0 from the CPLD on pin 19. + * At 206MHz we need to run the audio clock (SDRAM bank 2) + * at half speed. This clock will scale with core frequency so + * the audio sample rate will also scale. The CPLD on Assabet + * will need to be programmed to match the core frequency. + */ + mdrefr = MDREFR; + if ((mdrefr & (MDREFR_K2DB2 | MDREFR_K2RUN | MDREFR_EAPD | + MDREFR_KAPD)) != (MDREFR_K2DB2 | MDREFR_K2RUN)) { + mdrefr |= MDREFR_K2DB2 | MDREFR_K2RUN; + mdrefr &= ~(MDREFR_EAPD | MDREFR_KAPD); + MDREFR = mdrefr; + (void) MDREFR; + } + local_irq_restore(flags); + + /* Wait for the UDA1341 to wake up */ + mdelay(100); + + l3_open(&uda1341); + + pangolin_set_samplerate(audio_samplerate); + + GPCR = QmutePin; +} + +static void pangolin_audio_shutdown(void *dummy) +{ + GPSR = QmutePin; + + l3_close(&uda1341); + + Ser4SSCR0 = 0; + MDREFR &= ~(MDREFR_K2DB2 | MDREFR_K2RUN); +} + +static int pangolin_audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) break; + pangolin_set_samplerate(val); + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(audio_samplerate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do signed 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + +static audio_stream_t output_stream, input_stream; + +static audio_state_t audio_state = { + output_stream: &output_stream, + output_dma: DMA_Ser4SSPWr, + output_id: "Pangolin UDA1341 out", + input_stream: &input_stream, + input_dma: DMA_Ser4SSPRd, + input_id: "Pangolin UDA1341 in", + need_tx_for_rx: 1, + hw_init: pangolin_audio_init, + hw_shutdown: pangolin_audio_shutdown, + client_ioctl: pangolin_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int pangolin_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations pangolin_audio_fops = { + open: pangolin_audio_open, + owner: THIS_MODULE +}; + + +static int audio_dev_id, mixer_dev_id; + +static int __init pangolin_uda1341_init(void) +{ + unsigned long flags; + int ret; + + if (!machine_is_pangolin()) + return -ENODEV; + + ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); + if (ret) + goto out; + + /* register devices */ + audio_dev_id = register_sound_dsp(&pangolin_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&pangolin_mixer_fops, -1); + + local_irq_save(flags); + GAFR &= ~(SpeakerOffPin | QmutePin); + GPDR |= (SpeakerOffPin | QmutePin); + local_irq_restore(flags); + + printk(KERN_INFO "Pangolin UDA1341 audio driver initialized\n"); + return 0; + +release_l3: + l3_detach_client(&uda1341); +out: + return ret; +} + +static void __exit pangolin_uda1341_exit(void) +{ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + l3_detach_client(&uda1341); +} + +module_init(pangolin_uda1341_init); +module_exit(pangolin_uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec."); + +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/sound/sa1100-audio.c @@ -0,0 +1,976 @@ +/* + * Common audio handling for the SA11x0 processor + * + * Copyright (C) 2000, 2001 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * + * This module handles the generic buffering/DMA/mmap audio interface for + * codecs connected to the SA1100 chip. All features depending on specific + * hardware implementations like supported audio formats or samplerates are + * relegated to separate specific modules. + * + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial release. + * + * 2000-06-10 Erik Bunce Add initial poll support. + * + * 2000-08-22 Nicolas Pitre Removed all DMA stuff. Now using the + * generic SA1100 DMA interface. + * + * 2000-11-30 Nicolas Pitre - Validation of opened instances; + * - Power handling at open/release time instead + * of driver load/unload; + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-22 Nicolas Pitre - added mmap() and realtime support + * - corrected many details to better comply + * with the OSS API + * + * 2001-10-19 Nicolas Pitre - brought DMA registration processing + * into this module for better ressource + * management. This also fixes a bug + * with the suspend/resume logic. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" + + +#undef DEBUG +/* #define DEBUG 1 */ +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_NAME "sa1100-audio" +#define AUDIO_NBFRAGS_DEFAULT 8 +#define AUDIO_FRAGSIZE_DEFAULT 8192 + +#define NEXT_BUF(_s_,_b_) { \ + (_s_)->_b_##_idx++; \ + (_s_)->_b_##_idx %= (_s_)->nbfrags; \ + (_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; } + +#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref) + +/* + * This function frees all buffers + */ + +static void audio_clear_buf(audio_stream_t * s) +{ + DPRINTK("audio_clear_buf\n"); + + /* ensure DMA won't run anymore */ + s->active = 0; + s->stopped = 0; + sa1100_dma_flush_all(s->dma_ch); + + if (s->buffers) { + int frag; + for (frag = 0; frag < s->nbfrags; frag++) { + if (!s->buffers[frag].master) + continue; + consistent_free(s->buffers[frag].start, + s->buffers[frag].master, + s->buffers[frag].dma_addr); + } + kfree(s->buffers); + s->buffers = NULL; + } + + s->buf_idx = 0; + s->buf = NULL; +} + + +/* + * This function allocates the buffer structure array and buffer data space + * according to the current number of fragments and fragment size. + */ + +static int audio_setup_buf(audio_stream_t * s) +{ + int frag; + int dmasize = 0; + char *dmabuf = NULL; + dma_addr_t dmaphys = 0; + + if (s->buffers) + return -EBUSY; + + s->buffers = (audio_buf_t *) + kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); + if (!s->buffers) + goto err; + memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags); + + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + + /* + * Let's allocate non-cached memory for DMA buffers. + * We try to allocate all memory at once. + * If this fails (a common reason is memory fragmentation), + * then we allocate more smaller buffers. + */ + if (!dmasize) { + dmasize = (s->nbfrags - frag) * s->fragsize; + do { + dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA, + dmasize, + &dmaphys); + if (!dmabuf) + dmasize -= s->fragsize; + } while (!dmabuf && dmasize); + if (!dmabuf) + goto err; + b->master = dmasize; + memzero(dmabuf, dmasize); + } + + b->start = dmabuf; + b->dma_addr = dmaphys; + b->stream = s; + sema_init(&b->sem, 1); + DPRINTK("buf %d: start %p dma %p\n", frag, b->start, + b->dma_addr); + + dmabuf += s->fragsize; + dmaphys += s->fragsize; + dmasize -= s->fragsize; + } + + s->buf_idx = 0; + s->buf = &s->buffers[0]; + s->bytecount = 0; + s->getptrCount = 0; + s->fragcount = 0; + + return 0; + +err: + printk(AUDIO_NAME ": unable to allocate audio memory\n "); + audio_clear_buf(s); + return -ENOMEM; +} + + +/* + * This function yanks all buffers from the DMA code's control and + * resets them ready to be used again. + */ + +static void audio_reset_buf(audio_stream_t * s) +{ + int frag; + + s->active = 0; + s->stopped = 0; + sa1100_dma_flush_all(s->dma_ch); + if (s->buffers) { + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + b->size = 0; + sema_init(&b->sem, 1); + } + } + s->bytecount = 0; + s->getptrCount = 0; + s->fragcount = 0; +} + + +/* + * DMA callback functions + */ + +static void audio_dmaout_done_callback(void *buf_id, int size) +{ + audio_buf_t *b = (audio_buf_t *) buf_id; + audio_stream_t *s = b->stream; + + /* Accounting */ + s->bytecount += size; + s->fragcount++; + + /* Recycle buffer */ + if (s->mapped) + sa1100_dma_queue_buffer(s->dma_ch, buf_id, + b->dma_addr, s->fragsize); + else + up(&b->sem); + + /* And any process polling on write. */ + wake_up(&s->wq); +} + +static void audio_dmain_done_callback(void *buf_id, int size) +{ + audio_buf_t *b = (audio_buf_t *) buf_id; + audio_stream_t *s = b->stream; + + /* Accounting */ + s->bytecount += size; + s->fragcount++; + + /* Recycle buffer */ + if (s->mapped) { + sa1100_dma_queue_buffer(s->dma_ch, buf_id, + b->dma_addr, s->fragsize); + } else { + b->size = size; + up(&b->sem); + } + + /* And any process polling on write. */ + wake_up(&s->wq); +} + +static int audio_sync(struct file *file) +{ + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *s = state->output_stream; + audio_buf_t *b; + + DPRINTK("audio_sync\n"); + + if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) + return 0; + + /* + * Send current buffer if it contains data. Be sure to send + * a full sample count. + */ + b = s->buf; + if (b->size &= ~3) { + down(&b->sem); + sa1100_dma_queue_buffer(s->dma_ch, (void *) b, + b->dma_addr, b->size); + b->size = 0; + NEXT_BUF(s, buf); + } + + /* + * Let's wait for the last buffer we sent i.e. the one before the + * current buf_idx. When we acquire the semaphore, this means either: + * - DMA on the buffer completed or + * - the buffer was already free thus nothing else to sync. + */ + b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags); + if (down_interruptible(&b->sem)) + return -EINTR; + up(&b->sem); + return 0; +} + + +static int audio_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + const char *buffer0 = buffer; + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *s = state->output_stream; + int chunksize, ret = 0; + + DPRINTK("audio_write: count=%d\n", count); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->mapped) + return -ENXIO; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + + while (count > 0) { + audio_buf_t *b = s->buf; + + /* Wait for a buffer to become free */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&b->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&b->sem)) + break; + } + + /* Feed the current buffer */ + chunksize = s->fragsize - b->size; + if (chunksize > count) + chunksize = count; + DPRINTK("write %d to %d\n", chunksize, s->buf_idx); + if (copy_from_user(b->start + b->size, buffer, chunksize)) { + up(&b->sem); + return -EFAULT; + } + b->size += chunksize; + buffer += chunksize; + count -= chunksize; + if (b->size < s->fragsize) { + up(&b->sem); + break; + } + + /* Send current buffer to dma */ + s->active = 1; + sa1100_dma_queue_buffer(s->dma_ch, (void *) b, + b->dma_addr, b->size); + b->size = 0; /* indicate that the buffer has been sent */ + NEXT_BUF(s, buf); + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + DPRINTK("audio_write: return=%d\n", ret); + return ret; +} + + +static inline void audio_check_tx_spin(audio_state_t *state) +{ + /* + * With some codecs like the Philips UDA1341 we must ensure + * there is an output stream at any time while recording since + * this is how the UDA1341 gets its clock from the SA1100. + * So while there is no playback data to send, the output DMA + * will spin with all zeroes. We use the cache flush special + * area for that. + */ + if (state->need_tx_for_rx && !state->tx_spinning) { + sa1100_dma_set_spin(state->output_stream->dma_ch, + (dma_addr_t)FLUSH_BASE_PHYS, 2048); + state->tx_spinning = 1; + } +} + + +static void audio_prime_dma(audio_stream_t *s) +{ + int i; + + s->active = 1; + for (i = 0; i < s->nbfrags; i++) { + audio_buf_t *b = s->buf; + down(&b->sem); + sa1100_dma_queue_buffer(s->dma_ch, (void *) b, + b->dma_addr, s->fragsize); + NEXT_BUF(s, buf); + } +} + + +static int audio_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + char *buffer0 = buffer; + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *s = state->input_stream; + int chunksize, ret = 0; + + DPRINTK("audio_read: count=%d\n", count); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->mapped) + return -ENXIO; + + if (!s->active) { + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + audio_check_tx_spin(state); + audio_prime_dma(s); + } + + while (count > 0) { + audio_buf_t *b = s->buf; + + /* Wait for a buffer to become full */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&b->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&b->sem)) + break; + } + + /* Grab data from the current buffer */ + chunksize = b->size; + if (chunksize > count) + chunksize = count; + DPRINTK("read %d from %d\n", chunksize, s->buf_idx); + if (copy_to_user(buffer, + b->start + s->fragsize - b->size, + chunksize)) { + up(&b->sem); + return -EFAULT; + } + b->size -= chunksize; + buffer += chunksize; + count -= chunksize; + if (b->size > 0) { + up(&b->sem); + break; + } + + /* Make current buffer available for DMA again */ + sa1100_dma_queue_buffer(s->dma_ch, (void *) b, + b->dma_addr, s->fragsize); + NEXT_BUF(s, buf); + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + DPRINTK("audio_read: return=%d\n", ret); + return ret; +} + + +static int audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *s; + unsigned long size, vma_addr; + int i, ret; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) { + if (!state->wr_ref) + return -EINVAL;; + s = state->output_stream; + } else if (vma->vm_flags & VM_READ) { + if (!state->rd_ref) + return -EINVAL; + s = state->input_stream; + } else return -EINVAL; + + if (s->mapped) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size != s->fragsize * s->nbfrags) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + vma_addr = vma->vm_start; + for (i = 0; i < s->nbfrags; i++) { + audio_buf_t *buf = &s->buffers[i]; + if (!buf->master) + continue; + ret = remap_page_range(vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot); + if (ret) + return ret; + vma_addr += buf->master; + } + s->mapped = 1; + + return 0; +} + + +static unsigned int audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + unsigned int mask = 0; + int i; + + DPRINTK("audio_poll(): mode=%s%s\n", + (file->f_mode & FMODE_READ) ? "r" : "", + (file->f_mode & FMODE_WRITE) ? "w" : ""); + + if (file->f_mode & FMODE_READ) { + /* Start audio input if not already active */ + if (!is->active) { + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + audio_check_tx_spin(state); + audio_prime_dma(is); + } + poll_wait(file, &is->wq, wait); + } + + if (file->f_mode & FMODE_WRITE) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + poll_wait(file, &os->wq, wait); + } + + if (file->f_mode & FMODE_READ) { + if (is->mapped) { +/* if the buffer is mapped assume we care that there are more bytes available than + when we last asked using SNDCTL_DSP_GETxPTR */ + if (is->bytecount != is->getptrCount) + mask |= POLLIN | POLLRDNORM; + } else { + for (i = 0; i < is->nbfrags; i++) { + if (atomic_read(&is->buffers[i].sem.count) > 0) { + mask |= POLLIN | POLLRDNORM; + break; + } + } + } + } + if (file->f_mode & FMODE_WRITE) { + if (os->mapped) { + if (os->bytecount != os->getptrCount) + mask |= POLLOUT | POLLWRNORM; + } else { + for (i = 0; i < os->nbfrags; i++) { + if (atomic_read(&os->buffers[i].sem.count) > 0) { + mask |= POLLOUT | POLLWRNORM; + break; + } + } + } + } + + DPRINTK("audio_poll() returned mask of %s%s\n", + (mask & POLLIN) ? "r" : "", + (mask & POLLOUT) ? "w" : ""); + + return mask; +} + + +static loff_t audio_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + +static int audio_set_fragments(audio_stream_t *s, int val) +{ + if (s->active) + return -EBUSY; + if (s->buffers) + audio_clear_buf(s); + s->nbfrags = (val >> 16) & 0x7FFF; + val &= 0xffff; + if (val < 4) + val = 4; + if (val > 15) + val = 15; + s->fragsize = 1 << val; + if (s->nbfrags < 2) + s->nbfrags = 2; + if (s->nbfrags * s->fragsize > 128 * 1024) + s->nbfrags = 128 * 1024 / s->fragsize; + if (audio_setup_buf(s)) + return -ENOMEM; + return val|(s->nbfrags << 16); +} + +static int audio_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + long val; + + /* dispatch based on command */ + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(os->fragsize, (int *)arg); + else + return put_user(is->fragsize, (int *)arg); + + case SNDCTL_DSP_GETCAPS: + val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; + if (is && os) + val |= DSP_CAP_DUPLEX; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (long *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + int ret = audio_set_fragments(is, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + int ret = audio_set_fragments(os, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + return 0; + + case SNDCTL_DSP_SYNC: + return audio_sync(file); + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && is->active && !is->stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && os->active && !os->stopped) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!is->active) { + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + audio_prime_dma(is); + } + audio_check_tx_spin(state); + if (is->stopped) { + is->stopped = 0; + sa1100_dma_resume(is->dma_ch); + } + } else { + sa1100_dma_stop(is->dma_ch); + is->stopped = 1; + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!os->active) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + if (os->mapped) + audio_prime_dma(os); + } + if (os->stopped) { + os->stopped = 0; + sa1100_dma_resume(os->dma_ch); + } + } else { + sa1100_dma_stop(os->dma_ch); + os->stopped = 1; + } + } + return 0; + + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_GETIPTR: + { + count_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; + audio_buf_t *b; + dma_addr_t ptr; + int bytecount, offset, flags; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + if (s->active) { + save_flags_cli(flags); + if (sa1100_dma_get_current(s->dma_ch, (void *)&b, &ptr) == 0) { + offset = ptr - b->dma_addr; + inf.ptr = (b - s->buffers) * s->fragsize + offset; + } else offset = 0; + bytecount = s->bytecount + offset; + s->getptrCount = s->bytecount; /* so poll can tell if it changes */ + inf.blocks = s->fragcount; + s->fragcount = 0; + restore_flags(flags); + if (bytecount < 0) + bytecount = 0; + inf.bytes = bytecount; + } + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_GETOSPACE: + { + audio_buf_info inf = { 0, }; + int i; + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + for (i = 0; i < os->nbfrags; i++) { + if (atomic_read(&os->buffers[i].sem.count) > 0) { + if (os->buffers[i].size == 0) + inf.fragments++; + inf.bytes += os->fragsize - os->buffers[i].size; + } + } + inf.fragstotal = os->nbfrags; + inf.fragsize = os->fragsize; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info inf = { 0, }; + int i; + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + for (i = 0; i < is->nbfrags; i++) { + if (atomic_read(&is->buffers[i].sem.count) > 0) { + if (is->buffers[i].size == is->fragsize) + inf.fragments++; + inf.bytes += is->buffers[i].size; + } + } + inf.fragstotal = is->nbfrags; + inf.fragsize = is->fragsize; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_READ) { + if (state->tx_spinning) { + sa1100_dma_set_spin(os->dma_ch, 0, 0); + state->tx_spinning = 0; + } + audio_reset_buf(is); + } + if (file->f_mode & FMODE_WRITE) { + audio_reset_buf(os); + } + return 0; + + default: + /* + * Let the client of this module handle the + * non generic ioctls + */ + return state->client_ioctl(inode, file, cmd, arg); + } + + return 0; +} + + +static int audio_release(struct inode *inode, struct file *file) +{ + audio_state_t *state = (audio_state_t *)file->private_data; + DPRINTK("audio_release\n"); + + down(&state->sem); + + if (file->f_mode & FMODE_READ) { + if (state->tx_spinning) { + sa1100_dma_set_spin(state->output_stream->dma_ch, 0, 0); + state->tx_spinning = 0; + } + audio_clear_buf(state->input_stream); + if (!state->skip_dma_init) { + sa1100_free_dma(state->input_stream->dma_ch); + if (state->need_tx_for_rx && !state->wr_ref) + sa1100_free_dma(state->output_stream->dma_ch); + } + state->rd_ref = 0; + } + + if (file->f_mode & FMODE_WRITE) { + audio_sync(file); + audio_clear_buf(state->output_stream); + if (!state->skip_dma_init) + if (!state->need_tx_for_rx || !state->rd_ref) + sa1100_free_dma(state->output_stream->dma_ch); + state->wr_ref = 0; + } + + if (!AUDIO_ACTIVE(state)) { + if (state->hw_shutdown) + state->hw_shutdown(state->data); +#ifdef CONFIG_PM + pm_unregister(state->pm_dev); +#endif + } + + up(&state->sem); + return 0; +} + + +#ifdef CONFIG_PM + +static int audio_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + audio_state_t *state = (audio_state_t *)pm_dev->data; + + switch (req) { + case PM_SUSPEND: /* enter D1-D3 */ + if (state->output_stream) + sa1100_dma_sleep(state->output_stream->dma_ch); + if (state->input_stream) + sa1100_dma_sleep(state->input_stream->dma_ch); + if (AUDIO_ACTIVE(state) && state->hw_shutdown) + state->hw_shutdown(state->data); + break; + case PM_RESUME: /* enter D0 */ + if (AUDIO_ACTIVE(state) && state->hw_init) + state->hw_init(state->data); + if (state->input_stream) + sa1100_dma_wakeup(state->input_stream->dma_ch); + if (state->output_stream) + sa1100_dma_wakeup(state->output_stream->dma_ch); + break; + } + return 0; +} + +#endif + + +int sa1100_audio_attach(struct inode *inode, struct file *file, + audio_state_t *state) +{ + int err, need_tx_dma; + + DPRINTK("audio_open\n"); + + down(&state->sem); + + /* access control */ + err = -ENODEV; + if ((file->f_mode & FMODE_WRITE) && !state->output_stream) + goto out; + if ((file->f_mode & FMODE_READ) && !state->input_stream) + goto out; + err = -EBUSY; + if ((file->f_mode & FMODE_WRITE) && state->wr_ref) + goto out; + if ((file->f_mode & FMODE_READ) && state->rd_ref) + goto out; + err = -EINVAL; + if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !state->output_stream) + goto out; + + /* request DMA channels */ + if (state->skip_dma_init) + goto skip_dma; + need_tx_dma = ((file->f_mode & FMODE_WRITE) || + ((file->f_mode & FMODE_READ) && state->need_tx_for_rx)); + if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx)) + need_tx_dma = 0; + if (need_tx_dma) { + err = sa1100_request_dma(&state->output_stream->dma_ch, + state->output_id, + state->output_dma); + if (err) + goto out; + } + if (file->f_mode & FMODE_READ) { + err = sa1100_request_dma(&state->input_stream->dma_ch, + state->input_id, + state->input_dma); + if (err) { + if (need_tx_dma) + sa1100_free_dma(state->output_stream->dma_ch); + goto out; + } + } +skip_dma: + + /* now complete initialisation */ + if (!AUDIO_ACTIVE(state)) { + if (state->hw_init) + state->hw_init(state->data); +#ifdef CONFIG_PM + state->pm_dev = pm_register(PM_SYS_DEV, 0, audio_pm_callback); + if (state->pm_dev) + state->pm_dev->data = state; +#endif + } + + if ((file->f_mode & FMODE_WRITE)) { + state->wr_ref = 1; + audio_clear_buf(state->output_stream); + state->output_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT; + state->output_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT; + state->output_stream->mapped = 0; + sa1100_dma_set_callback(state->output_stream->dma_ch, + audio_dmaout_done_callback); + init_waitqueue_head(&state->output_stream->wq); + } + if (file->f_mode & FMODE_READ) { + state->rd_ref = 1; + audio_clear_buf(state->input_stream); + state->input_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT; + state->input_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT; + state->input_stream->mapped = 0; + sa1100_dma_set_callback(state->input_stream->dma_ch, + audio_dmain_done_callback); + init_waitqueue_head(&state->input_stream->wq); + } + + file->private_data = state; + file->f_op->release = audio_release; + file->f_op->write = audio_write; + file->f_op->read = audio_read; + file->f_op->mmap = audio_mmap; + file->f_op->poll = audio_poll; + file->f_op->ioctl = audio_ioctl; + file->f_op->llseek = audio_llseek; + err = 0; + +out: + up(&state->sem); + return err; +} + +EXPORT_SYMBOL(sa1100_audio_attach); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Common audio handling for the SA11x0 processor"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.27/drivers/sound/sa1100-audio.h @@ -0,0 +1,68 @@ +/* + * Common audio handling for the SA11x0 + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + */ + + +/* + * Buffer Management + */ + +typedef struct { + int size; /* buffer size */ + char *start; /* points to actual buffer */ + dma_addr_t dma_addr; /* physical buffer address */ + struct semaphore sem; /* down before touching the buffer */ + int master; /* owner for buffer allocation, contain size when true */ + struct audio_stream_s *stream; /* owning stream */ +} audio_buf_t; + +typedef struct audio_stream_s { + audio_buf_t *buffers; /* pointer to audio buffer structures */ + audio_buf_t *buf; /* current buffer used by read/write */ + u_int buf_idx; /* index for the pointer above... */ + u_int fragsize; /* fragment i.e. buffer size */ + u_int nbfrags; /* nbr of fragments i.e. buffers */ + int bytecount; /* nbr of processed bytes */ + int getptrCount; /* value of bytecount last time anyone asked via GETxPTR */ + int fragcount; /* nbr of fragment transitions */ + dmach_t dma_ch; /* DMA channel ID */ + wait_queue_head_t wq; /* for poll */ + int mapped:1; /* mmap()'ed buffers */ + int active:1; /* actually in progress */ + int stopped:1; /* might be active but stopped */ +} audio_stream_t; + +/* + * State structure for one instance + */ + +typedef struct { + audio_stream_t *output_stream; + audio_stream_t *input_stream; + dma_device_t output_dma; + dma_device_t input_dma; + char *output_id; + char *input_id; + int rd_ref:1; /* open reference for recording */ + int wr_ref:1; /* open reference for playback */ + int need_tx_for_rx:1; /* if data must be sent while receiving */ + int tx_spinning:1; /* tx spinning active */ + int skip_dma_init:1; /* hack for the SA1111 */ + void *data; + void (*hw_init)(void *); + void (*hw_shutdown)(void *); + int (*client_ioctl)(struct inode *, struct file *, uint, ulong); + struct pm_dev *pm_dev; + struct semaphore sem; /* to protect against races in attach() */ +} audio_state_t; + +/* + * Functions exported by this module + */ +extern int sa1100_audio_attach( struct inode *inode, struct file *file, + audio_state_t *state); --- /dev/null +++ linux-2.4.27/drivers/sound/sa1100ssp.c @@ -0,0 +1,182 @@ +/* + * Glue audio driver for a simple DAC on the SA1100's SSP port + * + * Copyright (c) 2001 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2001-06-04 Nicolas Pitre Initial release. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "sa1100-audio.h" + + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_NAME "SA1100 SSP audio" + +#define AUDIO_FMT AFMT_S16_LE +#define AUDIO_CHANNELS 2 + +static int sample_rate = 44100; + + +static void ssp_audio_init(void) +{ + if (machine_is_lart()) { + unsigned long flags; + local_irq_save(flags); + + /* LART has the SSP port rewired to GPIO 10-13, 19 */ + /* alternate functions for the GPIOs */ + GAFR |= ( GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | + GPIO_SSP_SFRM | GPIO_SSP_CLK ); + + /* Set the direction: 10, 12, 13 output; 11, 19 input */ + GPDR |= ( GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM ); + GPDR &= ~( GPIO_SSP_RXD | GPIO_SSP_CLK ); + + /* enable SSP pin swap */ + PPAR |= PPAR_SPR; + + local_irq_restore(flags); + } + + /* turn on the SSP */ + Ser4SSCR0 = 0; + Ser4SSCR0 = (SSCR0_DataSize(16) | SSCR0_TI | SSCR0_SerClkDiv(2) | + SSCR0_SSE); + Ser4SSCR1 = (SSCR1_SClkIactL | SSCR1_SClk1P | SSCR1_ExtClk); +} + +static void ssp_audio_shutdown(void) +{ + Ser4SSCR0 = 0; +} + +static int ssp_audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* Simple standard DACs are stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* Simple standard DACs are stereo only */ + return put_user(AUDIO_CHANNELS, (long *) arg); + + case SNDCTL_DSP_SPEED: + case SOUND_PCM_READ_RATE: + /* We assume the clock doesn't change */ + return put_user(sample_rate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* Simple standard DACs are 16-bit only */ + return put_user(AUDIO_FMT, (long *) arg); + + default: + return -EINVAL; + } + + return ret; +} + +static audio_stream_t output_stream; + +static audio_state_t audio_state = { + output_stream: &output_stream, + output_dma: DMA_Ser4SSPWr, + output_id: "Generic SSP sound", + hw_init: ssp_audio_init, + hw_shutdown: ssp_audio_shutdown, + client_ioctl: ssp_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int ssp_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations ssp_audio_fops = { + open: ssp_audio_open, + owner: THIS_MODULE +}; + +static int audio_dev_id; + +static int __init sa1100ssp_audio_init(void) +{ + int ret; + + if (!machine_is_lart()) { + printk(KERN_ERR AUDIO_NAME ": no support for this SA-1100 design!\n"); + /* look at ssp_audio_init() for specific initialisations */ + return -ENODEV; + } + + /* register devices */ + audio_dev_id = register_sound_dsp(&ssp_audio_fops, -1); + + printk( KERN_INFO AUDIO_NAME " initialized\n" ); + return 0; +} + +static void __exit sa1100ssp_audio_exit(void) +{ + unregister_sound_dsp(audio_dev_id); +} + +module_init(sa1100ssp_audio_init); +module_exit(sa1100ssp_audio_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Glue audio driver for a simple DAC on the SA1100's SSP port"); + +MODULE_PARM(sample_rate, "i"); +MODULE_PARM_DESC(sample_rate, "Sample rate of the audio DAC, default is 44100"); + +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/sound/sa1111-ac97.c @@ -0,0 +1,518 @@ +/* + * Glue audio driver for the CS4205 and CS4201 AC'97 codecs. + * largely based on the framework provided by sa1111-uda1341.c. + * + * Copyright (c) 2002 Bertrik Sikken (bertrik.sikken@technolution.nl) + * Copyright (c) 2002 Robert Whaley (rwhaley@applieddata.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * This driver makes use of the ac97_codec module (for mixer registers) + * and the sa1100-audio module (for DMA). + * + * History: + * + * 2002-04-04 Initial version. + * 2002-04-10 Updated mtd_audio_init to improve choppy sound + * and hanging sound issue. + * 2002-05-16 Updated for ADS Bitsy+ Robert Whaley + * 2002-06-28 Cleanup and added retry for read register timeouts + * 2002-08-14 Updated for ADS AGC Robert Whaley + * 2002-12-26 Cleanup, remove CONFIG_PM (it's handled by sa1100-audio.c) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "sa1100-audio.h" + +/* SAC FIFO depth, low nibble is transmit fifo, high nibble is receive FIFO */ +#define SAC_FIFO_DEPTH 0x77 + +// #define DEBUG + +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + +/* + Our codec data +*/ +static struct ac97_codec ac97codec; +static int audio_dev_id, mixer_dev_id; +static audio_stream_t output_stream, input_stream; + +/* proc info */ + +struct proc_dir_entry *ac97_ps; + +static int sa1111_ac97_set_adc_rate(long rate); +static void sa1111_ac97_write_reg(struct ac97_codec *dev, u8 reg, u16 val); +static u16 sa1111_ac97_read_reg(struct ac97_codec *dev, u8 reg); + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') { + return -EINVAL; + } + + /* pass the ioctl to the ac97 mixer */ + return ac97codec.mixer_ioctl(&ac97codec, cmd, arg); +} + + +static struct file_operations sa1111_ac97_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + +static void sa1111_ac97_power_off(void *dummy) +{ +#ifdef CONFIG_SA1100_ADSBITSYPLUS + /* turn off audio and audio amp */ + ADS_CPLD_PCON |= (ADS_PCON_AUDIO_ON | ADS_PCON_AUDIOPA_ON); + + /* make GPIO11 high impeadence */ + GPDR &= ~GPIO_GPIO11; + + /* disable SACR0 so we can make these pins high impeadence */ + SACR0 &= ~SACR0_ENB; + + /* make BIT_CLK, SDATA_OUT, and SYNC high impeadence */ + PC_DDR |= (GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + +#endif + +#ifdef CONFIG_SA1100_ADSAGC + /* turn off audio and audio amp */ + ADS_CR1 &= ~(ADS_CR1_CODEC | ADS_CR1_AMP); + + /* disable SACR0 so we can make these pins high impeadence */ + SACR0 &= ~SACR0_ENB; + + /* make BIT_CLK, SDATA_OUT, and SYNC high impeadence */ + PC_DDR |= (GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); + +#endif +} + + +static void sa1111_ac97_power_on(void *dummy) +{ + int ret, i; + + /* disable L3 */ + SACR1 = 0; + + SKPCR |= (SKPCR_ACCLKEN); /* enable ac97 clock */ + udelay(50); + + /* BIT_CLK is input to SA1111, DMA thresholds 9 (both dirs) */ + SACR0 |= SACR0_BCKD | (SAC_FIFO_DEPTH << 8); + + /* reset SAC registers */ + SACR0 &= ~SACR0_RST; + udelay(50); + SACR0 |= SACR0_RST; + udelay(50); + SACR0 &= ~SACR0_RST; + + /* setup SA1111 to use AC'97 */ + SBI_SKCR |= SKCR_SELAC; /* select ac97 */ + udelay(50); + + /* issue a cold AC97 reset */ +#ifdef CONFIG_SA1100_ADSBITSYPLUS + + /* initialize reset line */ + GAFR &= ~GPIO_GPIO11; + GPDR |= GPIO_GPIO11; + GPSR = GPIO_GPIO11; + + /* turn on audio and audio amp */ + ADS_CPLD_PCON &= ~(ADS_PCON_AUDIO_ON | ADS_PCON_AUDIOPA_ON); + mdelay(5); + + /* reset by lowering the reset pin momentarily */ + DPRINTK("reseting codec via GPIO11\n"); + GPCR = GPIO_GPIO11; + udelay(5); + GPSR = GPIO_GPIO11; + udelay(10); + +#endif +#ifdef CONFIG_SA1100_ADSAGC + + /* turn on audio and audio amp */ + DPRINTK("before turning on power. ADS_CR1: %x\n", ADS_CR1); + ADS_CR1 |= (ADS_CR1_AMP | ADS_CR1_CODEC); + DPRINTK("after turnning on power. ADS_CR1: %x\n", ADS_CR1); + mdelay(5); + + /* reset by lowering the reset pin momentarily */ + DPRINTK("reseting codec via CPLD\n"); + ADS_CR1 |= ADS_CR1_AUDIO_RST; + DPRINTK("after reset1. ADS_CR1: %x\n", ADS_CR1); + udelay(5); + ADS_CR1 &= ~ADS_CR1_AUDIO_RST; + DPRINTK("after reset2. ADS_CR1: %x\n", ADS_CR1); + udelay(10); + +#endif + SACR2 = 0; + udelay(50); + + DPRINTK("before SW reset: SACR2: %x\n", SACR2); + SACR2 = SACR2_RESET; + DPRINTK("after SW reset: SACR2: %x\n", SACR2); + udelay(50); + + /* set AC97 slot 3 and 4 (PCM out) to valid */ + SACR2 = (SACR2_RESET | SACR2_TS3V | SACR2_TS4V); + + /* enable SAC */ + SACR0 |= SACR0_ENB; + + i = 100; + while (!(SASR1 & SASR1_CRDY)) { + if (!i--) { + printk("Didn't get CRDY. SASR1=%x SKID=%x\n", SASR1, SBI_SKID); + break; + } + udelay(50); + } + + if (!(ret = ac97_probe_codec(&ac97codec))) { + printk("ac97_probe_codec failed (%d)\n", ret); + return; + } + + /* mic ADC on, disable VRA, disable VRM */ + sa1111_ac97_write_reg(&ac97codec, AC97_EXTENDED_STATUS, 0x0200); +} + + +/* + * Audio interface + */ + + +static int sa1111_ac97_audio_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + DPRINTK("sa1111_ac97_audio_ioctl\n"); + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) { + return ret; + } + /* the cs42xx is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the cs42xx is stereo only */ + return put_user(2, (long *) arg); + +#define SA1100_AC97_IOCTL_EXTRAS + +#ifdef SA1100_AC97_IOCTL_EXTRAS + +#define SNDCTL_DSP_AC97_CMD _SIOWR('P', 99, int) +#define SNDCTL_DSP_INPUT_SPEED _SIOWR('P', 98, int) +#define SOUND_PCM_READ_INPUT_RATE _SIOWR('P', 97, int) + + case SNDCTL_DSP_AC97_CMD: + + ret = get_user(val, (long *) arg); + if (ret) { + break; + } + sa1111_ac97_write_reg(&ac97codec, (u8) ((val & 0xff000000) >> 24), (u16) (val & 0xffff)); + return 0; + + + case SNDCTL_DSP_INPUT_SPEED: + ret = get_user(val, (long *) arg); + // acc code here to set the speed + if (ret) { + break; + } + // note that this only changes the ADC rate, not the + // rate of the DAC. + ret = sa1111_ac97_set_adc_rate(val); + if (ret) + break; + return put_user(val, (long *) arg); + + case SOUND_PCM_READ_INPUT_RATE: + + return put_user((long) sa1111_ac97_read_reg(&ac97codec, 0x32), (long *) arg); + + +#endif + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) { + break; + } + + case SOUND_PCM_READ_RATE: + /* only 48 kHz playback is supported by the SA1111 */ + return put_user(48000L, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + + +static audio_state_t audio_state = { + output_stream: &output_stream, + input_stream: &input_stream, + skip_dma_init: 1, /* done locally */ + hw_init: sa1111_ac97_power_on, + hw_shutdown: sa1111_ac97_power_off, + client_ioctl: sa1111_ac97_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + + +static int sa1111_ac97_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations sa1111_ac97_audio_fops = { + open: sa1111_ac97_audio_open, + owner: THIS_MODULE +}; + + +static void sa1111_ac97_write_reg(struct ac97_codec *dev, u8 reg, u16 val) +{ + int i; + + /* reset status bits */ + SASCR = SASCR_DTS; + + /* write command and data registers */ + ACCAR = reg << 12; + ACCDR = val << 4; + + /* wait for data to be transmitted */ + i = 0; + while ((SASR1 & SASR1_CADT) == 0) { + udelay(50); + if (++i > 10) { + DPRINTK("sa1111_ac97_write_reg failed (data not transmitted. SASR1: %x)\n", SASR1); + break; + } + } + + DPRINTK("<%03d> sa1111_ac97_write_reg, [%02X]=%04X\n", i, reg, val); +} + + +static u16 sa1111_ac97_read_reg(struct ac97_codec *dev, u8 reg) +{ + u16 val; + int i; + int retry = 10; + + do { + /* reset status bits */ + SASCR = SASCR_RDD | SASCR_STO; + + /* write command register */ + ACCAR = (reg | 0x80) << 12; + ACCDR = 0; + + /* wait for SADR bit in SASR1 */ + i = 0; + while ((SASR1 & SASR1_SADR) == 0) { + udelay(50); + if (++i > 10) { + DPRINTK("<---> sa1111_ac97_read_reg failed\n"); + retry--; + break; + } + if ((SASR1 & SASR1_RSTO) != 0) { + DPRINTK("sa1111_ac97_read_reg *timeout*\n"); + retry--; + break; + } + } + + } while ((SASR1 & SASR1_SADR) == 0 && retry > 0); + + val = ACSDR >> 4; + + DPRINTK("<%03d> sa1111_ac97_read_reg, [%02X]=%04X\n", i, reg, val); + return val; +} + + +/* wait for codec ready */ +static void sa1111_ac97_ready(struct ac97_codec *dev) +{ + int i; + u16 val; + + i = 0; + while ((SASR1 & SASR1_CRDY) == 0) { + udelay(50); + if (++i > 10) { + DPRINTK("sa1111_ac97_ready failed\n"); + return; + } + } + DPRINTK("codec_ready bit took %d cycles\n", i); + + /* Wait for analog parts of codec to initialise */ + i = 0; + do { + val = sa1111_ac97_read_reg(&ac97codec, AC97_POWER_CONTROL); + if (++i > 100) { + break; + } + mdelay(10); + } while ((val & 0xF) != 0xF || val == 0xFFFF); + + /* the cs42xx typically takes 150 ms to initialise */ + + DPRINTK("analog init took %d cycles\n", i); +} + + +static int __init sa1111_ac97_init(void) +{ + int ret; + + // SBI_SKCR |= SKCR_RCLKEN; + + DPRINTK("sa1111_ac97_init\n"); + + /* install the ac97 mixer module */ + ac97codec.codec_read = sa1111_ac97_read_reg; + ac97codec.codec_write = sa1111_ac97_write_reg; + ac97codec.codec_wait = sa1111_ac97_ready; + + /* Acquire and initialize DMA */ + ret = sa1111_sac_request_dma(&output_stream.dma_ch, "SA1111 audio out", + SA1111_SAC_XMT_CHANNEL); + if (ret < 0) { + printk("DMA request for SAC output failed\n"); + return ret; + } + + ret = sa1111_sac_request_dma(&input_stream.dma_ch, "SA1111 audio in", + SA1111_SAC_RCV_CHANNEL); + if (ret < 0) { + printk("DMA request for SAC input failed\n"); + sa1100_free_dma(output_stream.dma_ch); + return ret; + } + /* register devices */ + audio_dev_id = register_sound_dsp(&sa1111_ac97_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&sa1111_ac97_mixer_fops, -1); + + + /* setup proc entry */ + ac97_ps = create_proc_read_entry ("driver/sa1111-ac97", 0, NULL, + ac97_read_proc, &ac97codec); + + return 0; +} + + +static void __exit sa1111_ac97_exit(void) +{ + SKPCR &= ~SKPCR_ACCLKEN; /* disable ac97 clock */ + SBI_SKCR &= ~SKCR_SELAC; /* deselect ac97 */ + + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + sa1100_free_dma(output_stream.dma_ch); + sa1100_free_dma(input_stream.dma_ch); +} + +static int sa1111_ac97_set_adc_rate(long rate) +{ + + // note this only changes the rate of the ADC, the DAC is fixed at 48K. + // this is due to limitations of the SA1111 chip + + u16 code = rate; + + switch (rate) { + case 8000: + case 11025: + case 16000: + case 22050: + case 32000: + case 44100: + case 48000: + break; + default: + return -1; + } + sa1111_ac97_write_reg(&ac97codec, 0x2A, 0x0001); + sa1111_ac97_write_reg(&ac97codec, 0x32, code); + return 0; +} + +module_init(sa1111_ac97_init); +module_exit(sa1111_ac97_exit); + +MODULE_AUTHOR("Bertrik Sikken, Technolution B.V., Netherlands"); +MODULE_DESCRIPTION("Glue audio driver for AC'97 codec"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/sound/sa1111-uda1341.c @@ -0,0 +1,282 @@ +/* + * Glue audio driver for the SA1111 compagnon chip & Philips UDA1341 codec. + * + * Copyright (c) 2000 John Dorsey + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2000-09-04 John Dorsey SA-1111 Serial Audio Controller support + * was initially added to the sa1100-uda1341.c + * driver. + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-09-23 Russell King Remove old L3 bus driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +/* + * Definitions + */ + +#define AUDIO_RATE_DEFAULT 22050 + +#define AUDIO_CLK_BASE 561600 + + + +/* + * Mixer interface + */ + +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + +static struct file_operations uda1341_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + + +/* + * Audio interface + */ + +static int audio_clk_div = (AUDIO_CLK_BASE + AUDIO_RATE_DEFAULT/2)/AUDIO_RATE_DEFAULT; + +static void sa1111_audio_init(void *dummy) +{ +#ifdef CONFIG_ASSABET_NEPONSET + if (machine_is_assabet()) { + /* Select I2S audio (instead of AC-Link) */ + AUD_CTL = AUD_SEL_1341; + } +#endif +#ifdef CONFIG_SA1100_JORNADA720 + if (machine_is_jornada720()) { + /* LDD4 is speaker, LDD3 is microphone */ + PPSR &= ~(PPC_LDD3 | PPC_LDD4); + PPDR |= PPC_LDD3 | PPC_LDD4; + PPSR |= PPC_LDD4; /* enable speaker */ + PPSR |= PPC_LDD3; /* enable microphone */ + } +#endif + + SBI_SKCR &= ~SKCR_SELAC; + + /* Enable the I2S clock and L3 bus clock: */ + SKPCR |= (SKPCR_I2SCLKEN | SKPCR_L3CLKEN); + + /* Activate and reset the Serial Audio Controller */ + SACR0 |= (SACR0_ENB | SACR0_RST); + mdelay(5); + SACR0 &= ~SACR0_RST; + + /* For I2S, BIT_CLK is supplied internally. The "SA-1111 + * Specification Update" mentions that the BCKD bit should + * be interpreted as "0 = output". Default clock divider + * is 22.05kHz. + * + * Select I2S, L3 bus. "Recording" and "Replaying" + * (receive and transmit) are enabled. + */ + SACR1 = SACR1_L3EN; + SKAUD = audio_clk_div - 1; + + /* Initialize the UDA1341 internal state */ + l3_open(&uda1341); +} + +static void sa1111_audio_shutdown(void *dummy) +{ + l3_close(&uda1341); + SACR0 &= ~SACR0_ENB; +} + +static int sa1111_audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) break; + if (val < 8000) val = 8000; + if (val > 48000) val = 48000; + audio_clk_div = (AUDIO_CLK_BASE + val/2)/val; + SKAUD = audio_clk_div - 1; + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(AUDIO_CLK_BASE/audio_clk_div, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (as per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + +static audio_stream_t output_stream, input_stream; + +static audio_state_t audio_state = { + output_stream: &output_stream, + input_stream: &input_stream, + skip_dma_init: 1, /* done locally */ + hw_init: sa1111_audio_init, + hw_shutdown: sa1111_audio_shutdown, + client_ioctl: sa1111_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int sa1111_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations sa1111_audio_fops = { + open: sa1111_audio_open, + owner: THIS_MODULE +}; + +static int audio_dev_id, mixer_dev_id; + +static int __init sa1111_uda1341_init(void) +{ + struct uda1341_cfg cfg; + int ret; + + if ( !( (machine_is_assabet() && machine_has_neponset()) || + machine_is_jornada720() || + machine_is_badge4() )) + return -ENODEV; + + if (!request_mem_region(_SACR0, 512, "sound")) + return -EBUSY; + + ret = l3_attach_client(&uda1341, "l3-sa1111", "uda1341"); + if (ret) + goto out; + + /* Acquire and initialize DMA */ + ret = sa1111_sac_request_dma(&output_stream.dma_ch, "SA1111 audio out", + SA1111_SAC_XMT_CHANNEL); + if (ret < 0) + goto release_l3; + + ret = sa1111_sac_request_dma(&input_stream.dma_ch, "SA1111 audio in", + SA1111_SAC_RCV_CHANNEL); + if (ret < 0) + goto release_dma; + + cfg.fs = 256; + cfg.format = FMT_I2S; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + + /* register devices */ + audio_dev_id = register_sound_dsp(&sa1111_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&uda1341_mixer_fops, -1); + + printk(KERN_INFO "Sound: SA1111 UDA1341: dsp id %d mixer id %d\n", + audio_dev_id, mixer_dev_id); + return 0; + +release_dma: + sa1100_free_dma(output_stream.dma_ch); +release_l3: + l3_detach_client(&uda1341); +out: + release_mem_region(_SACR0, 512); + return ret; +} + +static void __exit sa1111_uda1341_exit(void) +{ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + sa1100_free_dma(output_stream.dma_ch); + sa1100_free_dma(input_stream.dma_ch); + l3_detach_client(&uda1341); + + release_mem_region(_SACR0, 512); +} + +module_init(sa1111_uda1341_init); +module_exit(sa1111_uda1341_exit); + +MODULE_AUTHOR("John Dorsey, Nicolas Pitre"); +MODULE_DESCRIPTION("Glue audio driver for the SA1111 compagnon chip & Philips UDA1341 codec."); + +EXPORT_NO_SYMBOLS; --- /dev/null +++ linux-2.4.27/drivers/sound/uda1341.c @@ -0,0 +1,511 @@ +/* + * Philips UDA1341 mixer device driver + * + * Copyright (c) 2000 Nicolas Pitre + * + * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial release. + * + * 2000-08-19 Erik Bunce More inline w/ OSS API and UDA1341 docs + * including fixed AGC and audio source handling + * + * 2000-11-30 Nicolas Pitre - More mixer functionalities. + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-08-13 Russell King Re-written as part of the L3 interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEF_VOLUME 65 + +/* + * UDA1341 L3 address and command types + */ +#define UDA1341_L3ADDR 5 +#define UDA1341_DATA0 (UDA1341_L3ADDR << 2 | 0) +#define UDA1341_DATA1 (UDA1341_L3ADDR << 2 | 1) +#define UDA1341_STATUS (UDA1341_L3ADDR << 2 | 2) + +struct uda1341_regs { + unsigned char stat0; +#define STAT0 0x00 +#define STAT0_RST (1 << 6) +#define STAT0_SC_MASK (3 << 4) +#define STAT0_SC_512FS (0 << 4) +#define STAT0_SC_384FS (1 << 4) +#define STAT0_SC_256FS (2 << 4) +#define STAT0_IF_MASK (7 << 1) +#define STAT0_IF_I2S (0 << 1) +#define STAT0_IF_LSB16 (1 << 1) +#define STAT0_IF_LSB18 (2 << 1) +#define STAT0_IF_LSB20 (3 << 1) +#define STAT0_IF_MSB (4 << 1) +#define STAT0_IF_LSB16MSB (5 << 1) +#define STAT0_IF_LSB18MSB (6 << 1) +#define STAT0_IF_LSB20MSB (7 << 1) +#define STAT0_DC_FILTER (1 << 0) + + unsigned char stat1; +#define STAT1 0x80 +#define STAT1_DAC_GAIN (1 << 6) /* gain of DAC */ +#define STAT1_ADC_GAIN (1 << 5) /* gain of ADC */ +#define STAT1_ADC_POL (1 << 4) /* polarity of ADC */ +#define STAT1_DAC_POL (1 << 3) /* polarity of DAC */ +#define STAT1_DBL_SPD (1 << 2) /* double speed playback */ +#define STAT1_ADC_ON (1 << 1) /* ADC powered */ +#define STAT1_DAC_ON (1 << 0) /* DAC powered */ + + unsigned char data0_0; +#define DATA0 0x00 +#define DATA0_VOLUME_MASK 0x3f +#define DATA0_VOLUME(x) (x) + + unsigned char data0_1; +#define DATA1 0x40 +#define DATA1_BASS(x) ((x) << 2) +#define DATA1_BASS_MASK (15 << 2) +#define DATA1_TREBLE(x) ((x)) +#define DATA1_TREBLE_MASK (3) + + unsigned char data0_2; +#define DATA2 0x80 +#define DATA2_PEAKAFTER (1 << 5) +#define DATA2_DEEMP_NONE (0 << 3) +#define DATA2_DEEMP_32KHz (1 << 3) +#define DATA2_DEEMP_44KHz (2 << 3) +#define DATA2_DEEMP_48KHz (3 << 3) +#define DATA2_MUTE (1 << 2) +#define DATA2_FILTER_FLAT (0 << 0) +#define DATA2_FILTER_MIN (1 << 0) +#define DATA2_FILTER_MAX (3 << 0) + +#define EXTADDR(n) (0xc0 | (n)) +#define EXTDATA(d) (0xe0 | (d)) + + unsigned char ext0; +#define EXT0 0 +#define EXT0_CH1_GAIN(x) (x) + + unsigned char ext1; +#define EXT1 1 +#define EXT1_CH2_GAIN(x) (x) + + unsigned char ext2; +#define EXT2 2 +#define EXT2_MIC_GAIN_MASK (7 << 2) +#define EXT2_MIC_GAIN(x) ((x) << 2) +#define EXT2_MIXMODE_DOUBLEDIFF (0) +#define EXT2_MIXMODE_CH1 (1) +#define EXT2_MIXMODE_CH2 (2) +#define EXT2_MIXMODE_MIX (3) + + unsigned char ext4; +#define EXT4 4 +#define EXT4_AGC_ENABLE (1 << 4) +#define EXT4_INPUT_GAIN_MASK (3) +#define EXT4_INPUT_GAIN(x) ((x) & 3) + + unsigned char ext5; +#define EXT5 5 +#define EXT5_INPUT_GAIN(x) ((x) >> 2) + + unsigned char ext6; +#define EXT6 6 +#define EXT6_AGC_CONSTANT_MASK (7 << 2) +#define EXT6_AGC_CONSTANT(x) ((x) << 2) +#define EXT6_AGC_LEVEL_MASK (3) +#define EXT6_AGC_LEVEL(x) (x) +}; + +#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC) +#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE) + +struct uda1341 { + struct uda1341_regs regs; + int active; + unsigned short volume; + unsigned short bass; + unsigned short treble; + unsigned short line; + unsigned short mic; + int mod_cnt; +}; + +#define ADD_FIELD(reg,field) \ + *p++ = reg | uda->regs.field + +#define ADD_EXTFIELD(reg,field) \ + *p++ = EXTADDR(reg); \ + *p++ = EXTDATA(uda->regs.field); + +static void uda1341_sync(struct l3_client *clnt) +{ + struct uda1341 *uda = clnt->driver_data; + char buf[24], *p = buf; + + ADD_FIELD(STAT0, stat0); + ADD_FIELD(STAT1, stat1); + + if (p != buf) + l3_write(clnt, UDA1341_STATUS, buf, p - buf); + + p = buf; + ADD_FIELD(DATA0, data0_0); + ADD_FIELD(DATA1, data0_1); + ADD_FIELD(DATA2, data0_2); + ADD_EXTFIELD(EXT0, ext0); + ADD_EXTFIELD(EXT1, ext1); + ADD_EXTFIELD(EXT2, ext2); + ADD_EXTFIELD(EXT4, ext4); + ADD_EXTFIELD(EXT5, ext5); + ADD_EXTFIELD(EXT6, ext6); + + if (p != buf) + l3_write(clnt, UDA1341_DATA0, buf, p - buf); +} + +static void uda1341_cmd_init(struct l3_client *clnt) +{ + struct uda1341 *uda = clnt->driver_data; + char buf[2]; + + uda->active = 1; + + buf[0] = uda->regs.stat0 | STAT0_RST; + buf[1] = uda->regs.stat0; + + l3_write(clnt, UDA1341_STATUS, buf, 2); + + /* resend all parameters */ + uda1341_sync(clnt); +} + +static int uda1341_configure(struct l3_client *clnt, struct uda1341_cfg *conf) +{ + struct uda1341 *uda = clnt->driver_data; + int ret = 0; + + uda->regs.stat0 &= ~(STAT0_SC_MASK | STAT0_IF_MASK); + + switch (conf->fs) { + case 512: uda->regs.stat0 |= STAT0_SC_512FS; break; + case 384: uda->regs.stat0 |= STAT0_SC_384FS; break; + case 256: uda->regs.stat0 |= STAT0_SC_256FS; break; + default: ret = -EINVAL; break; + } + + switch (conf->format) { + case FMT_I2S: uda->regs.stat0 |= STAT0_IF_I2S; break; + case FMT_LSB16: uda->regs.stat0 |= STAT0_IF_LSB16; break; + case FMT_LSB18: uda->regs.stat0 |= STAT0_IF_LSB18; break; + case FMT_LSB20: uda->regs.stat0 |= STAT0_IF_LSB20; break; + case FMT_MSB: uda->regs.stat0 |= STAT0_IF_MSB; break; + case FMT_LSB16MSB: uda->regs.stat0 |= STAT0_IF_LSB16MSB; break; + case FMT_LSB18MSB: uda->regs.stat0 |= STAT0_IF_LSB18MSB; break; + case FMT_LSB20MSB: uda->regs.stat0 |= STAT0_IF_LSB20MSB; break; + } + + if (ret == 0 && uda->active) { + char buf = uda->regs.stat0 | STAT0; + l3_write(clnt, UDA1341_STATUS, &buf, 1); + } + return ret; +} + +static int uda1341_update_direct(struct l3_client *clnt, int cmd, void *arg) +{ + struct uda1341 *uda = clnt->driver_data; + struct l3_gain *v = arg; + char newreg; + int val; + + switch (cmd) { + case L3_SET_VOLUME: /* set volume. val = 0 to 100 => 62 to 1 */ + uda->regs.data0_0 = DATA0_VOLUME(62 - ((v->left * 61) / 100)); + newreg = uda->regs.data0_0 | DATA0; + break; + + case L3_SET_BASS: /* set bass. val = 50 to 100 => 0 to 12 */ + val = v->left - 50; + if (val < 0) + val = 0; + uda->regs.data0_1 &= ~DATA1_BASS_MASK; + uda->regs.data0_1 |= DATA1_BASS((val * 12) / 50); + newreg = uda->regs.data0_1 | DATA1; + break; + + case L3_SET_TREBLE: /* set treble. val = 50 to 100 => 0 to 3 */ + val = v->left - 50; + if (val < 0) + val = 0; + uda->regs.data0_1 &= ~DATA1_TREBLE_MASK; + uda->regs.data0_1 |= DATA1_TREBLE((val * 3) / 50); + newreg = uda->regs.data0_1 | DATA1; + break; + + default: + return -EINVAL; + } + + if (uda->active) + l3_write(clnt, UDA1341_DATA0, &newreg, 1); + return 0; +} + +static int uda1341_update_indirect(struct l3_client *clnt, int cmd, void *arg) +{ + struct uda1341 *uda = clnt->driver_data; + struct l3_gain *gain = arg; + struct l3_agc *agc = arg; + char buf[8], *p = buf; + int val, ret = 0; + + switch (cmd) { + case L3_SET_GAIN: + val = 31 - (gain->left * 31 / 100); + switch (gain->channel) { + case 1: + uda->regs.ext0 = EXT0_CH1_GAIN(val); + ADD_EXTFIELD(EXT0, ext0); + break; + + case 2: + uda->regs.ext1 = EXT1_CH2_GAIN(val); + ADD_EXTFIELD(EXT1, ext1); + break; + + default: + ret = -EINVAL; + } + break; + + case L3_INPUT_AGC: + if (agc->channel == 2) { + if (agc->enable) + uda->regs.ext4 |= EXT4_AGC_ENABLE; + else + uda->regs.ext4 &= ~EXT4_AGC_ENABLE; +#if 0 + agc->level + agc->attack + agc->decay +#endif + ADD_EXTFIELD(EXT4, ext4); + } else + ret = -EINVAL; + break; + + default: + ret = -EINVAL; + break; + } + + if (ret == 0 && uda->active) + l3_write(clnt, UDA1341_DATA0, buf, p - buf); + + return ret; +} + +static int uda1341_mixer_ioctl(struct l3_client *clnt, int cmd, void *arg) +{ + struct uda1341 *uda = clnt->driver_data; + struct l3_gain gain; + int val, nr = _IOC_NR(cmd), ret = 0; + + if (cmd == SOUND_MIXER_INFO) { + struct mixer_info mi; + + strncpy(mi.id, "UDA1341", sizeof(mi.id)); + strncpy(mi.name, "Philips UDA1341", sizeof(mi.name)); + mi.modify_counter = uda->mod_cnt; + return copy_to_user(arg, &mi, sizeof(mi)); + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = get_user(val, (int *)arg); + if (ret) + goto out; + + gain.left = val & 255; + gain.right = val >> 8; + gain.channel = 0; + + switch (nr) { + case SOUND_MIXER_VOLUME: + uda->volume = val; + uda->mod_cnt++; + uda1341_update_direct(clnt, L3_SET_VOLUME, &gain); + break; + + case SOUND_MIXER_BASS: + uda->bass = val; + uda->mod_cnt++; + uda1341_update_direct(clnt, L3_SET_BASS, &gain); + break; + + case SOUND_MIXER_TREBLE: + uda->treble = val; + uda->mod_cnt++; + uda1341_update_direct(clnt, L3_SET_TREBLE, &gain); + break; + + case SOUND_MIXER_LINE: + uda->line = val; + gain.channel = 1; + uda->mod_cnt++; + uda1341_update_indirect(clnt, L3_SET_GAIN, &gain); + break; + + case SOUND_MIXER_MIC: + uda->mic = val; + gain.channel = 2; + uda->mod_cnt++; + uda1341_update_indirect(clnt, L3_SET_GAIN, &gain); + break; + + case SOUND_MIXER_RECSRC: + break; + + default: + ret = -EINVAL; + } + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + int nr = _IOC_NR(cmd); + ret = 0; + + switch (nr) { + case SOUND_MIXER_VOLUME: val = uda->volume; break; + case SOUND_MIXER_BASS: val = uda->bass; break; + case SOUND_MIXER_TREBLE: val = uda->treble; break; + case SOUND_MIXER_LINE: val = uda->line; break; + case SOUND_MIXER_MIC: val = uda->mic; break; + case SOUND_MIXER_RECSRC: val = REC_MASK; break; + case SOUND_MIXER_RECMASK: val = REC_MASK; break; + case SOUND_MIXER_DEVMASK: val = DEV_MASK; break; + case SOUND_MIXER_CAPS: val = 0; break; + case SOUND_MIXER_STEREODEVS: val = 0; break; + default: val = 0; ret = -EINVAL; break; + } + + if (ret == 0) + ret = put_user(val, (int *)arg); + } +out: + return ret; +} + +static int uda1341_attach(struct l3_client *clnt) +{ + struct uda1341 *uda; + + uda = kmalloc(sizeof(*uda), GFP_KERNEL); + if (!uda) + return -ENOMEM; + + memset(uda, 0, sizeof(*uda)); + + uda->volume = DEF_VOLUME | DEF_VOLUME << 8; + uda->bass = 50 | 50 << 8; + uda->treble = 50 | 50 << 8; + uda->line = 88 | 88 << 8; + uda->mic = 88 | 88 << 8; + + uda->regs.stat0 = STAT0_SC_256FS | STAT0_IF_LSB16; + uda->regs.stat1 = STAT1_DAC_GAIN | STAT1_ADC_GAIN | + STAT1_ADC_ON | STAT1_DAC_ON; + uda->regs.data0_0 = DATA0_VOLUME(62 - ((DEF_VOLUME * 61) / 100)); + uda->regs.data0_1 = DATA1_BASS(0) | DATA1_TREBLE(0); + uda->regs.data0_2 = DATA2_PEAKAFTER | DATA2_DEEMP_NONE | + DATA2_FILTER_MAX; + uda->regs.ext0 = EXT0_CH1_GAIN(4); + uda->regs.ext1 = EXT1_CH2_GAIN(4); + uda->regs.ext2 = EXT2_MIXMODE_MIX | EXT2_MIC_GAIN(4); + uda->regs.ext4 = EXT4_AGC_ENABLE | EXT4_INPUT_GAIN(0); + uda->regs.ext5 = EXT5_INPUT_GAIN(0); + uda->regs.ext6 = EXT6_AGC_CONSTANT(3) | EXT6_AGC_LEVEL(0); + + clnt->driver_data = uda; + + return 0; +} + +static void uda1341_detach(struct l3_client *clnt) +{ + kfree(clnt->driver_data); +} + +static int +uda1341_command(struct l3_client *clnt, int cmd, void *arg) +{ + int ret = -EINVAL; + + if (_IOC_TYPE(cmd) == 'M') + ret = uda1341_mixer_ioctl(clnt, cmd, arg); + else if (cmd == L3_UDA1341_CONFIGURE) + ret = uda1341_configure(clnt, arg); + + return ret; +} + +static int uda1341_open(struct l3_client *clnt) +{ + uda1341_cmd_init(clnt); + return 0; +} + +static void uda1341_close(struct l3_client *clnt) +{ + struct uda1341 *uda = clnt->driver_data; + uda->active = 0; +} + +static struct l3_ops uda1341_ops = { + open: uda1341_open, + command: uda1341_command, + close: uda1341_close, +}; + +static struct l3_driver uda1341 = { + name: UDA1341_NAME, + attach_client: uda1341_attach, + detach_client: uda1341_detach, + ops: &uda1341_ops, + owner: THIS_MODULE, +}; + +static int __init uda1341_init(void) +{ + return l3_add_driver(&uda1341); +} + +static void __exit uda1341_exit(void) +{ + l3_del_driver(&uda1341); +} + +module_init(uda1341_init); +module_exit(uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Philips UDA1341 CODEC driver"); --- linux-2.4.27/drivers/sound/vidc.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/sound/vidc.c @@ -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; + hwctrl = 0x00000003; + + /* Using internal clock */ + hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; + if (hwrate < 3) hwrate = 3; - } else { - hwctrl = 0x00000003; + if (hwrate > 255) + hwrate = 255; - hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; - if (hwrate < 3) - hwrate = 3; - if (hwrate > 255) - hwrate = 255; + /* 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 = VIDC_SOUND_CLOCK / hwrate; + rate_int = VIDC_SOUND_CLOCK / hwrate; + rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext; + + /* Chose between external and internal clock */ + if (my_abs(rate_ext-rate) < my_abs(rate_int-rate)) { + /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/ + hwrate=hwrate_ext; + hwctrl=0x00000002; + rate=rate_ext; + } else { + /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/ + hwctrl=0x00000003; + rate=rate_int; } vidc_writel(0xb0000000 | (hwrate - 2)); @@ -225,13 +241,14 @@ if (newsize > 4096) newsize = 4096; for (new2size = 128; new2size < newsize; new2size <<= 1); - if (new2size - newsize > newsize - (new2size >> 1)) - new2size >>= 1; + if (new2size - newsize > newsize - (new2size >> 1)) + new2size >>= 1; if (new2size > 4096) { printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n", newsize, new2size); new2size = 4096; } + /*printk("VIDC: dma size %d\n", new2size);*/ dma_bufsize = new2size; vidc_audio_rate = rate; } --- linux-2.4.27/drivers/sound/waveartist.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/sound/waveartist.c @@ -247,17 +247,15 @@ printk("\n"); } - if (inb(io_base + STATR) & CMD_RF) { - int old_data; - - /* flush the port - */ + /* + * flush any stale command data from the port. + */ + while (inb(io_base + STATR) & CMD_RF) { + unsigned int old_data; old_data = inw(io_base + CMDR); - - if (debug_flg & DEBUG_CMD) - printk("flushed %04X...", old_data); - + printk("waveartist: flushing stale command data: 0x%04x pc=%p\n", + old_data, __builtin_return_address(0)); udelay(10); } @@ -287,16 +285,19 @@ resp[i] = inw(io_base + CMDR); } - if (debug_flg & DEBUG_CMD) { - if (!timed_out) { - printk("waveartist_cmd: resp="); + if (debug_flg & DEBUG_CMD && !timed_out) { + printk("waveartist_cmd: resp="); - for (i = 0; i < nr_resp; i++) - printk("%04X ", resp[i]); + for (i = 0; i < nr_resp; i++) + printk("%04X ", resp[i]); + printk("\n"); + } - printk("\n"); - } else - printk("waveartist_cmd: timed out\n"); + if (timed_out) { + printk(KERN_ERR "waveartist_cmd: command timed out:"); + for (i = 0; i < nr_cmd; i++) + printk(" %04x", cmd[i]); + printk("\n"); } return timed_out ? 1 : 0; @@ -495,7 +496,6 @@ audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && count == devc->xfer_count) { - devc->audio_mode |= PCM_ENABLE_INPUT; return; /* * Auto DMA mode on. No need to react */ @@ -571,9 +571,6 @@ wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; unsigned int speed, bits; - if (devc->audio_mode) - return 0; - speed = waveartist_get_speed(portc); bits = waveartist_get_bits(portc); --- /dev/null +++ linux-2.4.27/drivers/ssi/Config.in @@ -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 --- /dev/null +++ linux-2.4.27/drivers/ssi/Makefile @@ -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 --- /dev/null +++ linux-2.4.27/drivers/ssi/README @@ -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 --- /dev/null +++ linux-2.4.27/drivers/ssi/clps711x_ssi1.c @@ -0,0 +1,237 @@ +/* + * linux/drivers/ssi/clps711x_ssi1.c + * + * SSI bus driver for the CLPS711x SSI1 bus. We support EP7212 + * extensions as well. + * + * Frame sizes can be between 4 and 24 bits. + * Config sizes can be between 4 and 16 bits. + */ +#include +#include +#include + +#include +#include +#include + +#include + +#include "ssi_bus.h" +#include "ssi_dev.h" + +#define EP7212 + +/* + * Port E on the P720T is used for the SPI bus chip selects + * 0 - Keyboard + * 1 - Touch screen + * 2 - CS4224 ADC/DAC + * 7 - idle + */ + +#if 0 +/* + * we place in the transmit buffer: + * + * received data (binary): + * 0xxxxxxxxxxxx000 + * where 'x' is the value + */ +struct ssi_dev ads7846_dev = { + name: "ADS7846", + id: 1, + proto: SSI_SPI, + cfglen: 8, + framelen: 24, + clkpol: 0, + clkfreq: 2500000, +}; + +/* + * we place in the transmit buffer: + * write: <20> ... + * received data discarded + */ +struct ssi_dev cs4224_dev = { + name: "CS4224", + id: 2, + proto: SSI_SPI, + cfglen: 8, + framelen: 8, + clkpol: 0, + clkfreq: 6000000, +}; +#endif + +/* + * Supplement with whatever method your board requires + */ +static void ssi1_select_id(int id) +{ + if (machine_is_p720t()) { + clps_writel(7, PEDDR); + clps_writel(id, PEDR); + } +} + +/* + * Select the specified device. The upper layers will have already + * checked that the bus transmit queue is idle. We need to make sure + * that the interface itself is idle. + */ +static int ssi1_select(struct ssi_bus *bus, struct ssi_dev *dev) +{ + u_int id = dev ? dev->id : 7; + u_int val; + + /* + * Make sure that the interface is idle + */ + do { + val = clps_readl(SYSFLG1); + } while (val & SYSFLG1_SSIBUSY); + + ssi1_select_id(7); + + if (dev) { + /* + * Select clock frequency. This is very rough, + * and assumes that we're operating in PLL mode. + */ + val = clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK; +// if (dev->clkfreq <= 16000) /* <16kHz */ +// val |= SYSCON1_ADCKSEL(0); +// else if (dev->clkfreq < 64000) /* <64kHz */ +// val |= SYSCON1_ADCKSEL(1); +// else if (dev->clkfreq < 128000) /* <128kHz */ + val |= SYSCON1_ADCKSEL(2); +// else /* >= 128kHz */ +// val |= SYSCON1_ADCKSEL(3); + clps_writel(val, SYSCON1); + + bus->cfglen = dev->cfglen; + bus->framelen = dev->framelen; + bus->clkpol = dev->clkpol; + bus->proto = dev->proto; + +#ifdef EP7212 + /* + * Set the clock edge according to the device. + * (set clkpol if the device reads data on the + * falling edge of the clock signal). + */ + val = ep_readl(SYSCON3) & ~SYSCON3_ADCCKNSEN; + if (bus->clkpol && dev->proto != SSI_USAR) + val |= SYSCON3_ADCCKNSEN; + ep_writel(val, SYSCON3); +#endif + + /* + * Select the device + */ + ssi1_select_id(id); + +#ifdef EP7212 + /* + * If we are doing USAR, wait 30us, then set + * the clock line low. + */ + if (dev->proto == SSI_USAR) { + udelay(150); + + val |= SYSCON3_ADCCKNSEN; + ep_writel(val, SYSCON3); + } +#endif + } + + return 0; +} + +static void ssi1_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ssi_bus *bus = (struct ssi_bus *)dev_id; + + /* + * Read the data word and queue it. + */ + ssi_core_rcv(bus, clps_readl(SYNCIO)); +} + +/* + * Enable transmission and/or of some bytes + */ +static int ssi1_trans(struct ssi_bus *bus, u_int data) +{ + u_int syncio; + +#ifdef EP7212 + data <<= 32 - bus->cfglen; + syncio = bus->cfglen | bus->framelen << 7 | data; +#else + syncio = data | bus->framelen << 8; +#endif + + clps_writel(syncio, SYNCIO); + clps_writel(syncio | SYNCIO_TXFRMEN, SYNCIO); + return 0; +} + +/* + * Initialise the SSI bus. + */ +static int ssi1_bus_init(struct ssi_bus *bus) +{ + int retval, val; + + retval = request_irq(IRQ_SSEOTI, ssi1_int, 0, "ssi1", bus); + if (retval) + return retval; + +#ifdef EP7212 + /* + * EP7212 only! Set the configuration command length. + * On the CLPS711x chips, it is fixed at 8 bits. + */ + val = ep_readl(SYSCON3); + val |= SYSCON3_ADCCON; + ep_writel(val, SYSCON3); +#endif + + ssi1_select(bus, NULL); + + PLD_SPI |= PLD_SPI_EN; + + return 0; +} + +static void ssi1_bus_exit(struct ssi_bus *bus) +{ + ssi1_select(bus, NULL); + + PLD_SPI &= ~PLD_SPI_EN; + + free_irq(IRQ_SSEOTI, bus); +} + +struct ssi_bus clps711x_ssi1_bus = { + name: "clps711x_ssi1", + init: ssi1_bus_init, + exit: ssi1_bus_exit, + select: ssi1_select, + trans: ssi1_trans, +}; + +static int __init clps711x_ssi1_init(void) +{ + return ssi_register_bus(&clps711x_ssi1_bus); +} + +static void __exit clps711x_ssi1_exit(void) +{ + ssi_unregister_bus(&clps711x_ssi1_bus); +} + +module_init(clps711x_ssi1_init); +module_exit(clps711x_ssi1_exit); --- /dev/null +++ linux-2.4.27/drivers/ssi/juno.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "ssi_bus.h" +#include "ssi_dev.h" + +extern struct ssi_bus clps711x_ssi1_bus; + +static u_int recvbuf[16]; +static volatile u_int ptr, rxed; + +static inline void juno_enable_irq(void) +{ + enable_irq(IRQ_EINT1); +} + +static inline void juno_disable_irq(void) +{ + disable_irq(IRQ_EINT1); +} + +static void juno_rcv(struct ssi_dev *dev, u_int data) +{ + if (ptr < 16) { + recvbuf[ptr] = data; + ptr++; + } else + printk("juno_rcv: %04x\n", data); + rxed = 1; +} + +static void juno_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ssi_dev *dev = dev_id; + + printk("juno_irq\n"); + + ssi_select_device(dev->bus, dev); + + ptr = 0; + do { + rxed = 0; + ssi_transmit_data(dev, 0xff); + while (rxed == 0); + udelay(150); + } while (PLD_INT & PLD_INT_KBD_ATN); + + ssi_select_device(dev->bus, NULL); + + { int i; + printk("juno_rcv: "); + for (i = 0; i < ptr; i++) + printk("%04x ", recvbuf[i]); + printk("\n"); + } +} + +static void juno_command(struct ssi_dev *dev, int cmd, int data) +{ + ssi_transmit_data(dev, cmd); + mdelay(1); + ssi_transmit_data(dev, data); + mdelay(1); + ssi_transmit_data(dev, 0xa0 ^ 0xc0); + mdelay(1); +} + +static int juno_dev_init(struct ssi_dev *dev) +{ + int retval; + + PLD_KBD |= PLD_KBD_EN; + ptr = 16; + + mdelay(20); + + retval = request_irq(IRQ_EINT1, juno_irq, 0, dev->name, dev); + if (retval) + return retval; + + juno_disable_irq(); + + if (ssi_select_device(dev->bus, dev) != 0) { + printk("juno: ssi_select_dev failed\n"); + return -EBUSY; + } + + mdelay(1); + + juno_command(dev, 0x80, 0x20); + + ssi_select_device(dev->bus, NULL); + + juno_enable_irq(); + + return 0; +} + +static struct ssi_dev juno_dev = { + name: "Juno", + id: 0, + proto: SSI_USAR, + cfglen: 8, + framelen: 8, + clkpol: 1, + clkfreq: 250000, + rcv: juno_rcv, + init: juno_dev_init, +}; + +static int __init juno_init(void) +{ + return ssi_register_device(&clps711x_ssi1_bus, &juno_dev); +} + +static void __exit juno_exit(void) +{ + ssi_unregister_device(&juno_dev); +} + +module_init(juno_init); +module_exit(juno_exit); + --- /dev/null +++ linux-2.4.27/drivers/ssi/ssi_bus.h @@ -0,0 +1,21 @@ +#include + +struct ssi_dev; + +struct ssi_bus { + u_char cfglen; + u_char framelen; + u_char clkpol; + u_char proto; + struct ssi_dev *dev; /* current device */ + int (*select)(struct ssi_bus *, struct ssi_dev *); + int (*trans)(struct ssi_bus *, u_int data); + int (*init)(struct ssi_bus *); + void (*exit)(struct ssi_bus *); + char *name; + u_int devices; +}; + +extern int ssi_core_rcv(struct ssi_bus *bus, u_int data); +extern int ssi_register_bus(struct ssi_bus *bus); +extern int ssi_unregister_bus(struct ssi_bus *bus); --- /dev/null +++ linux-2.4.27/drivers/ssi/ssi_core.c @@ -0,0 +1,175 @@ +/* + * linux/drivers/ssi/ssi_core.c + * + * This file provides a common framework to allow multiple SSI devices + * to work together on a single SSI bus. + * + * You can use this in two ways: + * 1. select the device, queue up data, flush the data to the device, + * (optionally) purge the received data, deselect the device. + * 2. select the device, queue up one data word, flush to the device + * read data word, queue up next data word, flush to the device... + * deselect the device. + */ +#include +#include +#include +#include +#include + +#include + +#include "ssi_bus.h" +#include "ssi_dev.h" + +#define DEBUG + +/** + * ssi_core_rcv - pass received SSI data to the device + * @bus: the bus that the data was received from + * @data: the data word that was received + * + * This function is intended to be called by SSI bus device + * drivers to pass received data to the device driver. + */ +int ssi_core_rcv(struct ssi_bus *bus, u_int data) +{ + struct ssi_dev *dev = bus->dev; + + if (dev && dev->rcv) + dev->rcv(dev, data); + + return 0; +} + +/** + * ssi_transmit_data - queue SSI data for later transmission + * @dev: device requesting data to be transmitted + * @data: data word to be transmitted. + * + * Queue one data word of SSI data for later transmission. + */ +int ssi_transmit_data(struct ssi_dev *dev, u_int data) +{ + struct ssi_bus *bus = dev->bus; + + /* + * Make sure that we currently own the bus + */ + if (bus->dev != dev) + BUG(); + + bus->trans(bus, data); + return 0; +} + +/** + * ssi_select_device - select a SSI device for later transactions + * @dev: device to be selected + */ +int ssi_select_device(struct ssi_bus *bus, struct ssi_dev *dev) +{ + int retval; + +#ifdef DEBUG + printk("SSI: selecting device %s on bus %s\n", + dev ? dev->name : "", bus->name); +#endif + + /* + * Select the device if it wasn't already selected. + */ + retval = 0; + if (bus->dev != dev) { + retval = bus->select(bus, dev); + bus->dev = dev; + } + + return retval; +} + +/** + * ssi_register_device - register a SSI device with a SSI bus + * @bus: bus + * @dev: SSI device + */ +int ssi_register_device(struct ssi_bus *bus, struct ssi_dev *dev) +{ + int retval; + + dev->bus = bus; + bus->devices++; + retval = dev->init(dev); + if (retval != 0) { + dev->bus = NULL; + bus->devices--; + } else { +#ifdef DEBUG + printk("SSI: registered new device %s on bus %s\n", dev->name, bus->name); +#endif + } + return retval; +} + +/** + * ssi_unregister_device - unregister a SSI device from a SSI bus + * @dev: SSI device + */ +int ssi_unregister_device(struct ssi_dev *dev) +{ + struct ssi_bus *bus = dev->bus; + + if (bus->dev == dev) + bus->dev = NULL; + + dev->bus = NULL; + bus->devices--; +#ifdef DEBUG + printk("SSI: unregistered device %s on bus %s\n", dev->name, bus->name); +#endif + return 0; +} + +/** + * ssi_register_bus - register a SSI bus driver + * @bus: bus + */ +int ssi_register_bus(struct ssi_bus *bus) +{ + int retval; + + retval = bus->init(bus); + if (retval == 0) { + bus->devices = 0; +#ifdef DEBUG + printk("SSI: registered new bus %s\n", bus->name); +#endif + } + + return retval; +} + +/** + * ssi_unregister_bus - unregister a SSI bus driver + * @bus: bus + */ +int ssi_unregister_bus(struct ssi_bus *bus) +{ + int retval = -EBUSY; + if (bus->devices == 0) { + retval = 0; + } + return retval; +} + +static int __init ssi_init(void) +{ + return 0; +} + +static void __exit ssi_exit(void) +{ +} + +module_init(ssi_init); +module_exit(ssi_exit); --- /dev/null +++ linux-2.4.27/drivers/ssi/ssi_dev.h @@ -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; +}; + + --- linux-2.4.27/drivers/usb/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/usb/Config.in @@ -4,7 +4,13 @@ mainmenu_option next_comment comment 'USB support' -dep_tristate 'Support for USB' CONFIG_USB $CONFIG_PCI +# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. +if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" -o "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + tristate 'Support for USB' CONFIG_USB +else + define_bool CONFIG_USB n +fi + if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then bool ' USB verbose debug messages' CONFIG_USB_DEBUG --- linux-2.4.27/drivers/usb/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/usb/Makefile @@ -74,6 +74,14 @@ subdir-$(CONFIG_USB_OHCI) += host ifeq ($(CONFIG_USB_OHCI),y) + obj-y += host/usb-ohci.o host/usb-ohci-pci.o +endif +subdir-$(CONFIG_USB_OHCI_SA1111)+= host +ifeq ($(CONFIG_USB_OHCI),y) + obj-y += host/usb-ohci.o host/usb-ohci-sa1111.o +endif +subdir-$(CONFIG_USB_OHCI_AT91) += host +ifeq ($(CONFIG_USB_OHCI_AT91),y) obj-y += host/usb-ohci.o endif @@ -85,8 +93,6 @@ obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_AIPTEK) += aiptek.o obj-$(CONFIG_USB_WACOM) += wacom.o -obj-$(CONFIG_USB_KBTAB) += kbtab.o -obj-$(CONFIG_USB_POWERMATE) += powermate.o obj-$(CONFIG_USB_SCANNER) += scanner.o obj-$(CONFIG_USB_ACM) += acm.o --- linux-2.4.27/drivers/usb/hcd.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/usb/hcd.c @@ -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 --- linux-2.4.27/drivers/usb/host/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/usb/host/Config.in @@ -12,8 +12,13 @@ define_bool CONFIG_USB_UHCI_ALT n fi dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB +dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB if [ "$CONFIG_ARM" = "y" -o "$CONFIG_X86" = "y" -a "$CONFIG_X86_64" != "y" ]; then # Cypress embedded USB controller on StrongARM or on x86 in PC/104 dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $CONFIG_USB $CONFIG_EXPERIMENTAL fi +if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + dep_tristate ' AT91RM9200 OHCI-compatible host interface support' CONFIG_USB_OHCI_AT91 $CONFIG_USB +fi + --- linux-2.4.27/drivers/usb/host/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/usb/host/Makefile @@ -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. --- /dev/null +++ linux-2.4.27/drivers/usb/host/usb-ohci-pci.c @@ -0,0 +1,436 @@ +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt() */ +#undef DEBUG +#include + +#include "usb-ohci.h" + +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#include +#ifndef CONFIG_PM +#define CONFIG_PM +#endif +#endif + + +/*-------------------------------------------------------------------------*/ + +/* Increment the module usage count, start the control thread and + * return success. */ + +static struct pci_driver ohci_pci_driver; +extern spinlock_t usb_ed_lock; +int __devinit +hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags, + const char *name, const char *slot_name); +extern void hc_remove_ohci(ohci_t *ohci); + +static int __devinit +hc_found_ohci (struct pci_dev *dev, int irq, + void *mem_base, const struct pci_device_id *id) +{ + unsigned long flags = id->driver_data; + + printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); + + /* Check for NSC87560. We have to look at the bridge (fn1) to identify + the USB (fn2). This quirk might apply to more or even all NSC stuff + I don't know.. */ + + if(dev->vendor == PCI_VENDOR_ID_NS) + { + struct pci_dev *fn1 = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); + if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO) + flags |= OHCI_QUIRK_SUCKYIO; + + } + + if (flags & OHCI_QUIRK_SUCKYIO) + printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n"); + if (flags & OHCI_QUIRK_AMD756) + printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n"); + + return hc_add_ohci(dev, irq, mem_base, flags, + ohci_pci_driver.name, dev->slot_name); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +/* controller died; cleanup debris, then restart */ +/* must not be called from interrupt context */ + +static void hc_restart (ohci_t *ohci) +{ + int temp; + int i; + + if (ohci->pci_latency) + pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); + + ohci->disabled = 1; + ohci->sleeping = 0; + if (ohci->bus->root_hub) + usb_disconnect (&ohci->bus->root_hub); + + /* empty the interrupt branches */ + for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; + for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0; + + /* no EDs to remove */ + ohci->ed_rm_list [0] = NULL; + ohci->ed_rm_list [1] = NULL; + + /* empty control and bulk lists */ + ohci->ed_isotail = NULL; + ohci->ed_controltail = NULL; + ohci->ed_bulktail = NULL; + + if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { + err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp); + } else + dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name); +} + +#endif /* CONFIG_PM */ + +/*-------------------------------------------------------------------------*/ + +/* configured so that an OHCI device is always provided */ +/* always called with process context; sleeping is OK */ + +static int __devinit +ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) +{ + unsigned long mem_resource, mem_len; + void *mem_base; + int status; + + if (pci_enable_device(dev) < 0) + return -ENODEV; + + if (!dev->irq) { + err("found OHCI device with no IRQ assigned. check BIOS settings!"); + pci_disable_device (dev); + return -ENODEV; + } + + /* we read its hardware registers as memory */ + mem_resource = pci_resource_start(dev, 0); + mem_len = pci_resource_len(dev, 0); + if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) { + dbg ("controller already in use"); + pci_disable_device (dev); + return -EBUSY; + } + + mem_base = ioremap_nocache (mem_resource, mem_len); + if (!mem_base) { + err("Error mapping OHCI memory"); + release_mem_region (mem_resource, mem_len); + pci_disable_device (dev); + return -EFAULT; + } + + /* controller writes into our memory */ + pci_set_master (dev); + + status = hc_found_ohci (dev, dev->irq, mem_base, id); + if (status < 0) { + iounmap (mem_base); + release_mem_region (mem_resource, mem_len); + pci_disable_device (dev); + } + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* may be called from interrupt context [interface spec] */ +/* may be called without controller present */ +/* may be called with controller, bus, and devices active */ + +static void __devexit +ohci_pci_remove (struct pci_dev *dev) +{ + ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); + + dbg ("remove %s controller usb-%s%s%s", + hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), + dev->slot_name, + ohci->disabled ? " (disabled)" : "", + in_interrupt () ? " in interrupt" : "" + ); + + hc_remove_ohci(ohci); + + release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); + pci_disable_device (dev); +} + + +#ifdef CONFIG_PM + +/*-------------------------------------------------------------------------*/ + +static int +ohci_pci_suspend (struct pci_dev *dev, u32 state) +{ + ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); + unsigned long flags; + u16 cmd; + + if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { + dbg ("can't suspend usb-%s (state is %s)", dev->slot_name, + hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); + return -EIO; + } + + /* act as if usb suspend can always be used */ + info ("USB suspend: usb-%s", dev->slot_name); + ohci->sleeping = 1; + + /* First stop processing */ + spin_lock_irqsave (&usb_ed_lock, flags); + ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); + writel (ohci->hc_control, &ohci->regs->control); + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + (void) readl (&ohci->regs->intrstatus); + spin_unlock_irqrestore (&usb_ed_lock, flags); + + /* Wait a frame or two */ + mdelay(1); + if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) + mdelay (1); + +#ifdef CONFIG_PMAC_PBOOK + if (_machine == _MACH_Pmac) + disable_irq (ohci->irq); + /* else, 2.4 assumes shared irqs -- don't disable */ +#endif + /* Enable remote wakeup */ + writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable); + + /* Suspend chip and let things settle down a bit */ + ohci->hc_control = OHCI_USB_SUSPEND; + writel (ohci->hc_control, &ohci->regs->control); + (void) readl (&ohci->regs->control); + mdelay (500); /* No schedule here ! */ + switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { + case OHCI_USB_RESET: + dbg("Bus in reset phase ???"); + break; + case OHCI_USB_RESUME: + dbg("Bus in resume phase ???"); + break; + case OHCI_USB_OPER: + dbg("Bus in operational phase ???"); + break; + case OHCI_USB_SUSPEND: + dbg("Bus suspended"); + break; + } + /* In some rare situations, Apple's OHCI have happily trashed + * memory during sleep. We disable it's bus master bit during + * suspend + */ + pci_read_config_word (dev, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_MASTER; + pci_write_config_word (dev, PCI_COMMAND, cmd); +#ifdef CONFIG_PMAC_PBOOK + { + struct device_node *of_node; + + /* Disable USB PAD & cell clock */ + of_node = pci_device_to_OF_node (ohci->ohci_dev); + if (of_node) + pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); + } +#endif + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int +ohci_pci_resume (struct pci_dev *dev) +{ + ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); + int temp; + unsigned long flags; + + /* guard against multiple resumes */ + atomic_inc (&ohci->resume_count); + if (atomic_read (&ohci->resume_count) != 1) { + err ("concurrent PCI resumes for usb-%s", dev->slot_name); + atomic_dec (&ohci->resume_count); + return 0; + } + +#ifdef CONFIG_PMAC_PBOOK + { + struct device_node *of_node; + + /* Re-enable USB PAD & cell clock */ + of_node = pci_device_to_OF_node (ohci->ohci_dev); + if (of_node) + pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1); + } +#endif + + /* did we suspend, or were we powered off? */ + ohci->hc_control = readl (&ohci->regs->control); + temp = ohci->hc_control & OHCI_CTRL_HCFS; + +#ifdef DEBUG + /* the registers may look crazy here */ + ohci_dump_status (ohci); +#endif + + /* Re-enable bus mastering */ + pci_set_master(ohci->ohci_dev); + + switch (temp) { + + case OHCI_USB_RESET: // lost power + info ("USB restart: usb-%s", dev->slot_name); + hc_restart (ohci); + break; + + case OHCI_USB_SUSPEND: // host wakeup + case OHCI_USB_RESUME: // remote wakeup + info ("USB continue: usb-%s from %s wakeup", dev->slot_name, + (temp == OHCI_USB_SUSPEND) + ? "host" : "remote"); + ohci->hc_control = OHCI_USB_RESUME; + writel (ohci->hc_control, &ohci->regs->control); + (void) readl (&ohci->regs->control); + mdelay (20); /* no schedule here ! */ + /* Some controllers (lucent) need a longer delay here */ + mdelay (15); + temp = readl (&ohci->regs->control); + temp = ohci->hc_control & OHCI_CTRL_HCFS; + if (temp != OHCI_USB_RESUME) { + err ("controller usb-%s won't resume", dev->slot_name); + ohci->disabled = 1; + return -EIO; + } + + /* Some chips likes being resumed first */ + writel (OHCI_USB_OPER, &ohci->regs->control); + (void) readl (&ohci->regs->control); + mdelay (3); + + /* Then re-enable operations */ + spin_lock_irqsave (&usb_ed_lock, flags); + ohci->disabled = 0; + ohci->sleeping = 0; + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) { + if (ohci->ed_controltail) + ohci->hc_control |= OHCI_CTRL_CLE; + if (ohci->ed_bulktail) + ohci->hc_control |= OHCI_CTRL_BLE; + } + writel (ohci->hc_control, &ohci->regs->control); + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + writel (OHCI_INTR_SF, &ohci->regs->intrenable); + /* Check for a pending done list */ + writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); + (void) readl (&ohci->regs->intrdisable); + spin_unlock_irqrestore (&usb_ed_lock, flags); +#ifdef CONFIG_PMAC_PBOOK + if (_machine == _MACH_Pmac) + enable_irq (ohci->irq); +#endif + if (ohci->hcca->done_head) + dl_done_list (ohci, dl_reverse_done_list (ohci)); + writel (OHCI_INTR_WDH, &ohci->regs->intrenable); + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + + default: + warn ("odd PCI resume for usb-%s", dev->slot_name); + } + + /* controller is operational, extra resumes are harmless */ + atomic_dec (&ohci->resume_count); + + return 0; +} + +#endif /* CONFIG_PM */ + + +/*-------------------------------------------------------------------------*/ + +static const struct pci_device_id __devinitdata ohci_pci_ids [] = { { + + /* + * AMD-756 [Viper] USB has a serious erratum when used with + * lowspeed devices like mice. + */ + vendor: 0x1022, + device: 0x740c, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + + driver_data: OHCI_QUIRK_AMD756, + +} , { + + /* handle any USB OHCI controller */ + class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10), + class_mask: ~0, + + /* no matter who makes it */ + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + + }, { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE (pci, ohci_pci_ids); + +static struct pci_driver ohci_pci_driver = { + name: "usb-ohci", + id_table: &ohci_pci_ids [0], + + probe: ohci_pci_probe, + remove: __devexit_p(ohci_pci_remove), + +#ifdef CONFIG_PM + suspend: ohci_pci_suspend, + resume: ohci_pci_resume, +#endif /* PM */ +}; + + +/*-------------------------------------------------------------------------*/ + +static int __init ohci_hcd_init (void) +{ + return pci_module_init (&ohci_pci_driver); +} + +/*-------------------------------------------------------------------------*/ + +static void __exit ohci_hcd_cleanup (void) +{ + pci_unregister_driver (&ohci_pci_driver); +} + +module_init (ohci_hcd_init); +module_exit (ohci_hcd_cleanup); + --- /dev/null +++ linux-2.4.27/drivers/usb/host/usb-ohci-sa1111.c @@ -0,0 +1,118 @@ +/* + * linux/drivers/usb/usb-ohci-sa1111.c + * + * The outline of this code was taken from Brad Parkers + * original OHCI driver modifications, and reworked into a cleaner form + * by Russell King . + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "usb-ohci.h" + +int __devinit +hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags, + const char *name, const char *slot_name); +extern void hc_remove_ohci(ohci_t *ohci); + +static ohci_t *sa1111_ohci; + +static void __init sa1111_ohci_configure(void) +{ + unsigned int usb_rst = 0; + + if (machine_is_xp860() || + machine_has_neponset() || + machine_is_accelent_sa() || + machine_is_pfs168() || + machine_is_badge4()) + usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; + + /* + * Configure the power sense and control lines. Place the USB + * host controller in reset. + */ + USB_RESET = usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET; + + /* + * Now, carefully enable the USB clock, and take + * the USB host controller out of reset. + */ + SKPCR |= SKPCR_UCLKEN; + udelay(11); + USB_RESET = usb_rst; +} + +static int __init sa1111_ohci_init(void) +{ + int ret; + + /* + * Request memory resources. + */ +// if (!request_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT, "usb-ohci")) +// return -EBUSY; + + sa1111_ohci_configure(); + + /* + * Initialise the generic OHCI driver. + */ + ret = hc_add_ohci(SA1111_FAKE_PCIDEV, NIRQHCIM, + (void *)&USB_OHCI_OP_BASE, 0, &sa1111_ohci, + "usb-ohci", "sa1111"); + +// if (ret) +// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); + +#ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4() && (!ret)) { + /* found the controller, so now power the bus */ + badge4_set_5V(BADGE4_5V_USB, 1); + } +#endif + + return ret; +} + +static void __exit sa1111_ohci_exit(void) +{ + hc_remove_ohci(sa1111_ohci); + + /* + * Put the USB host controller into reset. + */ + USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET; + + /* + * Stop the USB clock. + */ + SKPCR &= ~SKPCR_UCLKEN; + + /* + * Release memory resources. + */ +// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); + +#ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { + badge4_set_5V(BADGE4_5V_USB, 0); + } +#endif +} + +module_init(sa1111_ohci_init); +module_exit(sa1111_ohci_exit); --- linux-2.4.27/drivers/usb/host/usb-ohci.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/usb/host/usb-ohci.c @@ -12,7 +12,6 @@ * * History: * - * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX ) * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on * load failure (Matthew Frederickson) * 2002/01/20 async unlink fixes: return -EINPROGRESS (per spec) and @@ -81,16 +80,6 @@ #include "../hcd.h" -#ifdef CONFIG_PMAC_PBOOK -#include -#include -#include -#ifndef CONFIG_PM -#define CONFIG_PM -#endif -#endif - - /* * Version Information */ @@ -98,12 +87,12 @@ #define DRIVER_AUTHOR "Roman Weissgaerber , David Brownell" #define DRIVER_DESC "USB OHCI Host Controller Driver" -/* For initializing controller (mask in an HCFS mode too) */ -#define OHCI_CONTROL_INIT \ - (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE - #define OHCI_UNLINK_TIMEOUT (HZ / 10) +static LIST_HEAD (ohci_hcd_list); +spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; + + /*-------------------------------------------------------------------------*/ /* AMD-756 (D2 rev) reports corrupt register contents in some cases. @@ -130,57 +119,6 @@ /*-------------------------------------------------------------------------* * URB support functions *-------------------------------------------------------------------------*/ - -static void ohci_complete_add(struct ohci *ohci, struct urb *urb) -{ - - if (urb->hcpriv != NULL) { - printk("completing with non-null priv!\n"); - return; - } - - if (ohci->complete_tail == NULL) { - ohci->complete_head = urb; - ohci->complete_tail = urb; - } else { - ohci->complete_head->hcpriv = urb; - ohci->complete_tail = urb; - } -} - -static inline struct urb *ohci_complete_get(struct ohci *ohci) -{ - struct urb *urb; - - if ((urb = ohci->complete_head) == NULL) - return NULL; - if (urb == ohci->complete_tail) { - ohci->complete_tail = NULL; - ohci->complete_head = NULL; - } else { - ohci->complete_head = urb->hcpriv; - } - urb->hcpriv = NULL; - return urb; -} - -static inline void ohci_complete(struct ohci *ohci) -{ - struct urb *urb; - - spin_lock(&ohci->ohci_lock); - while ((urb = ohci_complete_get(ohci)) != NULL) { - spin_unlock(&ohci->ohci_lock); - if (urb->dev) { - usb_dec_dev_use (urb->dev); - urb->dev = NULL; - } - if (urb->complete) - (*urb->complete)(urb); - spin_lock(&ohci->ohci_lock); - } - spin_unlock(&ohci->ohci_lock); -} /* free HCD-private data associated with this URB */ @@ -256,14 +194,20 @@ } urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv); - } else { - if (urb->dev != NULL) { - err ("Non-null dev at rm_priv time"); - // urb->dev = NULL; - } + usb_dec_dev_use (urb->dev); + urb->dev = NULL; } } +static void urb_rm_priv (struct urb * urb) +{ + unsigned long flags; + + spin_lock_irqsave (&usb_ed_lock, flags); + urb_rm_priv_locked (urb); + spin_unlock_irqrestore (&usb_ed_lock, flags); +} + /*-------------------------------------------------------------------------*/ #ifdef DEBUG @@ -484,7 +428,7 @@ static void ohci_dump (ohci_t *controller, int verbose) { - dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name); + dbg ("OHCI controller usb-%s state", controller->slot_name); // dumps some of the state we know about ohci_dump_status (controller); @@ -507,6 +451,7 @@ { urb_priv_t * urb_priv = urb->hcpriv; struct urb * urbt; + unsigned long flags; int i; if (!urb_priv) @@ -514,8 +459,7 @@ /* just to be sure */ if (!urb->complete) { - urb_rm_priv_locked (urb); - ohci_complete_add(hc, urb); /* Just usb_dec_dev_use */ + urb_rm_priv (urb); return -1; } @@ -531,18 +475,20 @@ usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + if (urb->interval) { urb->complete (urb); - + /* implicitly requeued */ urb->actual_length = 0; urb->status = -EINPROGRESS; td_submit_urb (urb); } else { - urb_rm_priv_locked (urb); - ohci_complete_add(hc, urb); + urb_rm_priv(urb); + urb->complete (urb); } - break; + break; + case PIPE_ISOCHRONOUS: for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); @@ -554,6 +500,7 @@ ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); urb->complete (urb); + spin_lock_irqsave (&usb_ed_lock, flags); urb->actual_length = 0; urb->status = USB_ST_URB_PENDING; urb->start_frame = urb_priv->ed->last_iso + 1; @@ -564,17 +511,18 @@ } td_submit_urb (urb); } - + spin_unlock_irqrestore (&usb_ed_lock, flags); + } else { /* unlink URB, call complete */ - urb_rm_priv_locked (urb); - ohci_complete_add(hc, urb); + urb_rm_priv (urb); + urb->complete (urb); } break; case PIPE_BULK: case PIPE_CONTROL: /* unlink URB, call complete */ - urb_rm_priv_locked (urb); - ohci_complete_add(hc, urb); + urb_rm_priv (urb); + urb->complete (urb); break; } return 0; @@ -594,7 +542,7 @@ int i, size = 0; unsigned long flags; int bustime = 0; - int mem_flags = GFP_ATOMIC; + int mem_flags = ALLOC_FLAGS; if (!urb->dev || !urb->dev->bus) return -ENODEV; @@ -611,24 +559,20 @@ #ifdef DEBUG urb_print (urb, "SUB", usb_pipein (pipe)); #endif - + /* handle a request to the virtual root hub */ if (usb_pipedevice (pipe) == ohci->rh.devnum) return rh_submit_urb (urb); - - spin_lock_irqsave(&ohci->ohci_lock, flags); - + /* when controller's hung, permit only roothub cleanup attempts * such as powering down ports */ if (ohci->disabled) { - spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ESHUTDOWN; } /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { - spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } @@ -650,7 +594,6 @@ case PIPE_ISOCHRONOUS: /* number of packets from URB */ size = urb->number_of_packets; if (size <= 0) { - spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -EINVAL; } @@ -670,9 +613,8 @@ /* allocate the private part of the URB */ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), - GFP_ATOMIC); + in_interrupt() ? GFP_ATOMIC : GFP_NOIO); if (!urb_priv) { - spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } @@ -683,12 +625,13 @@ urb_priv->ed = ed; /* allocate the TDs (updating hash chains) */ + spin_lock_irqsave (&usb_ed_lock, flags); for (i = 0; i < size; i++) { urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC); if (!urb_priv->td[i]) { urb_priv->length = i; urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + spin_unlock_irqrestore (&usb_ed_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } @@ -696,7 +639,7 @@ if (ed->state == ED_NEW || (ed->state & ED_DEL)) { urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + spin_unlock_irqrestore (&usb_ed_lock, flags); usb_dec_dev_use (urb->dev); return -EINVAL; } @@ -718,7 +661,7 @@ } if (bustime < 0) { urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + spin_unlock_irqrestore (&usb_ed_lock, flags); usb_dec_dev_use (urb->dev); return bustime; } @@ -744,6 +687,9 @@ if (urb->timeout) { struct list_head *entry; + // FIXME: usb-uhci uses relative timeouts (like this), + // while uhci uses absolute ones (probably better). + // Pick one solution and change the affected drivers. urb->timeout += jiffies; list_for_each (entry, &ohci->timeout_list) { @@ -757,11 +703,10 @@ /* drive timeouts by SF (messy, but works) */ writel (OHCI_INTR_SF, &ohci->regs->intrenable); - (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ } #endif - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + spin_unlock_irqrestore (&usb_ed_lock, flags); return 0; } @@ -792,7 +737,6 @@ if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) return rh_unlink_urb (urb); - spin_lock_irqsave(&ohci->ohci_lock, flags); if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { if (!ohci->disabled) { urb_priv_t * urb_priv; @@ -802,7 +746,6 @@ */ if (!(urb->transfer_flags & USB_ASYNC_UNLINK) && in_interrupt ()) { - spin_unlock_irqrestore(&ohci->ohci_lock, flags); err ("bug in call from %p; use async!", __builtin_return_address(0)); return -EWOULDBLOCK; @@ -811,10 +754,11 @@ /* flag the urb and its TDs for deletion in some * upcoming SF interrupt delete list processing */ + spin_lock_irqsave (&usb_ed_lock, flags); urb_priv = urb->hcpriv; if (!urb_priv || (urb_priv->state == URB_DEL)) { - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + spin_unlock_irqrestore (&usb_ed_lock, flags); return 0; } @@ -829,36 +773,26 @@ add_wait_queue (&unlink_wakeup, &wait); urb_priv->wait = &unlink_wakeup; - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + spin_unlock_irqrestore (&usb_ed_lock, flags); /* wait until all TDs are deleted */ set_current_state(TASK_UNINTERRUPTIBLE); - while (timeout && (urb->status == USB_ST_URB_PENDING)) { + while (timeout && (urb->status == USB_ST_URB_PENDING)) timeout = schedule_timeout (timeout); - set_current_state(TASK_UNINTERRUPTIBLE); - } set_current_state(TASK_RUNNING); remove_wait_queue (&unlink_wakeup, &wait); if (urb->status == USB_ST_URB_PENDING) { err ("unlink URB timeout"); return -ETIMEDOUT; } - - usb_dec_dev_use (urb->dev); - urb->dev = NULL; - if (urb->complete) - urb->complete (urb); } else { /* usb_dec_dev_use done in dl_del_list() */ urb->status = -EINPROGRESS; - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + spin_unlock_irqrestore (&usb_ed_lock, flags); return -EINPROGRESS; } } else { - urb_rm_priv_locked (urb); - spin_unlock_irqrestore(&ohci->ohci_lock, flags); - usb_dec_dev_use (urb->dev); - urb->dev = NULL; + urb_rm_priv (urb); if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; if (urb->complete) @@ -866,8 +800,6 @@ } else urb->status = -ENOENT; } - } else { - spin_unlock_irqrestore(&ohci->ohci_lock, flags); } return 0; } @@ -910,14 +842,14 @@ * (freeing all the TDs, unlinking EDs) but we need * to defend against bugs that prevent that. */ - spin_lock_irqsave(&ohci->ohci_lock, flags); + spin_lock_irqsave (&usb_ed_lock, flags); for(i = 0; i < NUM_EDS; i++) { ed = &(dev->ed[i]); if (ed->state != ED_NEW) { if (ed->state == ED_OPER) { /* driver on that interface didn't unlink an urb */ dbg ("driver usb-%s dev %d ed 0x%x unfreed URB", - ohci->ohci_dev->slot_name, usb_dev->devnum, i); + ohci->slot_name, usb_dev->devnum, i); ep_unlink (ohci, ed); } ep_rm_ed (usb_dev, ed); @@ -925,7 +857,7 @@ cnt++; } } - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + spin_unlock_irqrestore (&usb_ed_lock, flags); /* if the controller is running, tds for those unlinked * urbs get freed by dl_del_list at the next SF interrupt @@ -962,7 +894,7 @@ } else { /* likely some interface's driver has a refcount bug */ err ("bus %s devnum %d deletion in interrupt", - ohci->ohci_dev->slot_name, usb_dev->devnum); + ohci->slot_name, usb_dev->devnum); BUG (); } } @@ -1259,12 +1191,17 @@ td_t * td; ed_t * ed_ret; volatile ed_t * ed; + unsigned long flags; + + + spin_lock_irqsave (&usb_ed_lock, flags); ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]); if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { /* pending delete request */ + spin_unlock_irqrestore (&usb_ed_lock, flags); return NULL; } @@ -1277,6 +1214,7 @@ /* out of memory */ if (td) td_free(ohci, td); + spin_unlock_irqrestore (&usb_ed_lock, flags); return NULL; } ed->hwTailP = cpu_to_le32 (td->td_dma); @@ -1299,7 +1237,8 @@ ed->int_period = interval; ed->int_load = load; } - + + spin_unlock_irqrestore (&usb_ed_lock, flags); return ed_ret; } @@ -1340,7 +1279,6 @@ /* enable SOF interrupt */ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); writel (OHCI_INTR_SF, &ohci->regs->intrenable); - (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ } } @@ -1362,7 +1300,11 @@ err("internal OHCI error: TD index > length"); return; } - +#ifdef CONFIG_SA1111 + if (data & (1 << 20)) + panic("td_fill: A20 = 1: %08x\n", data); +#endif + /* use this td as the next dummy */ td_pt = urb_priv->td [index]; td_pt->hwNextTD = 0; @@ -1461,7 +1403,6 @@ if (!ohci->sleeping) { wmb(); writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ } break; @@ -1490,7 +1431,6 @@ if (!ohci->sleeping) { wmb(); writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ } break; @@ -1558,7 +1498,7 @@ /* handle an urb that is being unlinked */ -static void dl_del_urb (ohci_t *ohci, struct urb * urb) +static void dl_del_urb (struct urb * urb) { wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait; @@ -1566,9 +1506,12 @@ if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; - ohci_complete_add(ohci, urb); + if (urb->complete) + urb->complete (urb); } else { urb->status = -ENOENT; + if (urb->complete) + urb->complete (urb); /* unblock sohci_unlink_urb */ if (wait_head) @@ -1587,7 +1530,10 @@ td_t * td_rev = NULL; td_t * td_list = NULL; urb_priv_t * urb_priv = NULL; - + unsigned long flags; + + spin_lock_irqsave (&usb_ed_lock, flags); + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; ohci->hcca->done_head = 0; @@ -1613,6 +1559,7 @@ td_rev = td_list; td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; } + spin_unlock_irqrestore (&usb_ed_lock, flags); return td_list; } @@ -1624,6 +1571,7 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) { + unsigned long flags; ed_t * ed; __u32 edINFO; __u32 tdINFO; @@ -1631,6 +1579,8 @@ __u32 * td_p; int ctrl = 0, bulk = 0; + spin_lock_irqsave (&usb_ed_lock, flags); + for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) { tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0); @@ -1651,7 +1601,7 @@ /* URB is done; clean up */ if (++(urb_priv->td_cnt) == urb_priv->length) - dl_del_urb (ohci, urb); + dl_del_urb (urb); } else { td_p = &td->hwNextTD; } @@ -1708,6 +1658,7 @@ } ohci->ed_rm_list[frame] = NULL; + spin_unlock_irqrestore (&usb_ed_lock, flags); } @@ -1724,7 +1675,9 @@ struct urb * urb; urb_priv_t * urb_priv; __u32 tdINFO, edHeadP, edTailP; - + + unsigned long flags; + while (td_list) { td_list_next = td_list->next_dl_td; @@ -1753,10 +1706,13 @@ urb->status = cc_to_error[cc]; sohci_return_urb (ohci, urb); } else { - dl_del_urb (ohci, urb); + spin_lock_irqsave (&usb_ed_lock, flags); + dl_del_urb (urb); + spin_unlock_irqrestore (&usb_ed_lock, flags); } } + spin_lock_irqsave (&usb_ed_lock, flags); if (ed->state != ED_NEW) { edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; edTailP = le32_to_cpup (&ed->hwTailP); @@ -1765,6 +1721,7 @@ if ((edHeadP == edTailP) && (ed->state == ED_OPER)) ep_unlink (ohci, ed); } + spin_unlock_irqrestore (&usb_ed_lock, flags); td_list = td_list_next; } @@ -1855,7 +1812,7 @@ num_ports = roothub_a (ohci) & RH_A_NDP; if (num_ports > MAX_ROOT_PORTS) { err ("bogus NDP=%d for OHCI usb-%s", num_ports, - ohci->ohci_dev->slot_name); + ohci->slot_name); err ("rereads as NDP=%d", readl (&ohci->regs->roothub.a) & RH_A_NDP); /* retry later; "should not happen" */ @@ -1955,8 +1912,7 @@ int leni = urb->transfer_buffer_length; int len = 0; int status = TD_CC_NOERROR; - unsigned long flags; - + __u32 datab[4]; __u8 * data_buf = (__u8 *) datab; @@ -1965,16 +1921,13 @@ __u16 wIndex; __u16 wLength; - spin_lock_irqsave(&ohci->ohci_lock, flags); - if (usb_pipeint(pipe)) { ohci->rh.urb = urb; ohci->rh.send = 1; ohci->rh.interval = urb->interval; rh_init_int_timer(urb); urb->status = cc_to_error [TD_CC_NOERROR]; - - spin_unlock_irqrestore(&ohci->ohci_lock, flags); + return 0; } @@ -2146,7 +2099,6 @@ #endif urb->hcpriv = NULL; - spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (usb_dev); urb->dev = NULL; if (urb->complete) @@ -2159,16 +2111,13 @@ static int rh_unlink_urb (struct urb * urb) { ohci_t * ohci = urb->dev->bus->hcpriv; - unsigned int flags; - spin_lock_irqsave(&ohci->ohci_lock, flags); if (ohci->rh.urb == urb) { ohci->rh.send = 0; del_timer (&ohci->rh.rh_int_timer); ohci->rh.urb = NULL; urb->hcpriv = NULL; - spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use(urb->dev); urb->dev = NULL; if (urb->transfer_flags & USB_ASYNC_UNLINK) { @@ -2177,8 +2126,6 @@ urb->complete (urb); } else urb->status = -ENOENT; - } else { - spin_unlock_irqrestore(&ohci->ohci_lock, flags); } return 0; } @@ -2213,16 +2160,12 @@ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;", - ohci->ohci_dev->slot_name, + ohci->slot_name, readl (&ohci->regs->control)); /* Reset USB (needed by some controllers) */ writel (0, &ohci->regs->control); - - /* Force a state change from USBRESET to USBOPERATIONAL for ALi */ - (void) readl (&ohci->regs->control); /* PCI posting */ - writel (ohci->hc_control = OHCI_USB_OPER, &ohci->regs->control); - + /* HC Reset requires max 10 ms delay */ writel (OHCI_HCR, &ohci->regs->cmdstatus); while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { @@ -2291,8 +2234,6 @@ writel (RH_HS_LPSC, &ohci->regs->roothub.status); #endif /* OHCI_USE_NPS */ - (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ - // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((roothub_a (ohci) >> 23) & 0x1fe); @@ -2322,7 +2263,7 @@ static void check_timeouts (struct ohci *ohci) { - spin_lock (&ohci->ohci_lock); + spin_lock (&usb_ed_lock); while (!list_empty (&ohci->timeout_list)) { struct urb *urb; @@ -2335,15 +2276,15 @@ continue; urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; - spin_unlock (&ohci->ohci_lock); + spin_unlock (&usb_ed_lock); // outside the interrupt handler (in a timer...) // this reference would race interrupts sohci_unlink_urb (urb); - spin_lock (&ohci->ohci_lock); + spin_lock (&usb_ed_lock); } - spin_unlock (&ohci->ohci_lock); + spin_unlock (&usb_ed_lock); } @@ -2357,8 +2298,6 @@ struct ohci_regs * regs = ohci->regs; int ints; - spin_lock (&ohci->ohci_lock); - /* avoid (slow) readl if only WDH happened */ if ((ohci->hcca->done_head != 0) && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { @@ -2367,22 +2306,20 @@ /* cardbus/... hardware gone before remove() */ } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { ohci->disabled++; - spin_unlock (&ohci->ohci_lock); err ("%s device removed!", ohci->ohci_dev->slot_name); return; /* interrupt for some other device? */ } else if ((ints &= readl (®s->intrenable)) == 0) { - spin_unlock (&ohci->ohci_lock); return; - } + } // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); if (ints & OHCI_INTR_UE) { ohci->disabled++; err ("OHCI Unrecoverable Error, controller usb-%s disabled", - ohci->ohci_dev->slot_name); + ohci->slot_name); // e.g. due to PCI Master/Target Abort #ifdef DEBUG @@ -2398,39 +2335,26 @@ if (ints & OHCI_INTR_WDH) { writel (OHCI_INTR_WDH, ®s->intrdisable); - (void)readl (®s->intrdisable); /* PCI posting flush */ dl_done_list (ohci, dl_reverse_done_list (ohci)); writel (OHCI_INTR_WDH, ®s->intrenable); - (void)readl (®s->intrdisable); /* PCI posting flush */ } if (ints & OHCI_INTR_SO) { dbg("USB Schedule overrun"); writel (OHCI_INTR_SO, ®s->intrenable); - (void)readl (®s->intrdisable); /* PCI posting flush */ } // FIXME: this assumes SOF (1/ms) interrupts don't get lost... if (ints & OHCI_INTR_SF) { unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1; writel (OHCI_INTR_SF, ®s->intrdisable); - (void)readl (®s->intrdisable); /* PCI posting flush */ if (ohci->ed_rm_list[!frame] != NULL) { dl_del_list (ohci, !frame); } - if (ohci->ed_rm_list[frame] != NULL) { + if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, ®s->intrenable); - (void)readl (®s->intrdisable); /* PCI posting flush */ - } } - /* - * Finally, we are done with trashing about our hardware lists - * and other CPUs are allowed in. The festive flipping of the lock - * ensues as we struggle with the check_timeouts disaster. - */ - spin_unlock (&ohci->ohci_lock); - if (!list_empty (&ohci->timeout_list)) { check_timeouts (ohci); // FIXME: enable SF as needed in a timer; @@ -2442,9 +2366,6 @@ writel (ints, ®s->intrstatus); writel (OHCI_INTR_MIE, ®s->intrenable); - (void)readl (®s->intrdisable); /* PCI posting flush */ - - ohci_complete(ohci); } /*-------------------------------------------------------------------------*/ @@ -2475,10 +2396,14 @@ ohci->regs = mem_base; ohci->ohci_dev = dev; +#ifdef CONFIG_PCI pci_set_drvdata(dev, ohci); +#endif + INIT_LIST_HEAD (&ohci->ohci_hcd_list); + list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); + INIT_LIST_HEAD (&ohci->timeout_list); - spin_lock_init(&ohci->ohci_lock); ohci->bus = usb_alloc_bus (&sohci_device_operations); if (!ohci->bus) { @@ -2501,7 +2426,7 @@ static void hc_release_ohci (ohci_t * ohci) { - dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name); + dbg ("USB HC release ohci usb-%s", ohci->slot_name); /* disconnect all devices */ if (ohci->bus->root_hub) @@ -2514,7 +2439,9 @@ free_irq (ohci->irq, ohci); ohci->irq = -1; } - pci_set_drvdata(ohci->ohci_dev, NULL); +#ifdef CONFIG_PCI + pci_set_drvdata(ohci->ohci_dev, 0); +#endif if (ohci->bus) { if (ohci->bus->busnum != -1) usb_deregister_bus (ohci->bus); @@ -2522,6 +2449,9 @@ usb_free_bus (ohci->bus); } + list_del (&ohci->ohci_hcd_list); + INIT_LIST_HEAD (&ohci->ohci_hcd_list); + ohci_mem_cleanup (ohci); /* unmap the IO address space */ @@ -2534,17 +2464,15 @@ /*-------------------------------------------------------------------------*/ -/* Increment the module usage count, start the control thread and - * return success. */ - -static struct pci_driver ohci_pci_driver; - -static int __devinit -hc_found_ohci (struct pci_dev *dev, int irq, - void *mem_base, const struct pci_device_id *id) +/* + * Host bus independent add one OHCI host controller. + */ +int __devinit +hc_add_ohci(struct pci_dev *dev, int irq, void *mem_base, unsigned long flags, + const char *name, const char *slot_name) { - ohci_t * ohci; char buf[8], *bufp = buf; + ohci_t * ohci; int ret; #ifndef __sparc__ @@ -2554,34 +2482,17 @@ #endif printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n", (unsigned long) mem_base, bufp); - printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); - + ohci = hc_alloc_ohci (dev, mem_base); if (!ohci) { return -ENOMEM; } + ohci->slot_name = slot_name; if ((ret = ohci_mem_init (ohci)) < 0) { hc_release_ohci (ohci); return ret; } - ohci->flags = id->driver_data; - - /* Check for NSC87560. We have to look at the bridge (fn1) to identify - the USB (fn2). This quirk might apply to more or even all NSC stuff - I don't know.. */ - - if(dev->vendor == PCI_VENDOR_ID_NS) - { - struct pci_dev *fn1 = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); - if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO) - ohci->flags |= OHCI_QUIRK_SUCKYIO; - - } - - if (ohci->flags & OHCI_QUIRK_SUCKYIO) - printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n"); - if (ohci->flags & OHCI_QUIRK_AMD756) - printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n"); + ohci->flags = flags; if (hc_reset (ohci) < 0) { hc_release_ohci (ohci); @@ -2590,13 +2501,11 @@ /* FIXME this is a second HC reset; why?? */ writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); - (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ wait_ms (10); usb_register_bus (ohci->bus); - if (request_irq (irq, hc_interrupt, SA_SHIRQ, - ohci_pci_driver.name, ohci) != 0) { + if (request_irq (irq, hc_interrupt, SA_SHIRQ, name, ohci) != 0) { err ("request interrupt %s failed", bufp); hc_release_ohci (ohci); return -EBUSY; @@ -2604,7 +2513,7 @@ ohci->irq = irq; if (hc_start (ohci) < 0) { - err ("can't start usb-%s", dev->slot_name); + err ("can't start usb-%s", ohci->slot_name); hc_release_ohci (ohci); return -EBUSY; } @@ -2615,114 +2524,11 @@ return 0; } -/*-------------------------------------------------------------------------*/ - -#ifdef CONFIG_PM - -/* controller died; cleanup debris, then restart */ -/* must not be called from interrupt context */ - -static void hc_restart (ohci_t *ohci) -{ - int temp; - int i; - - if (ohci->pci_latency) - pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); - - ohci->disabled = 1; - ohci->sleeping = 0; - if (ohci->bus->root_hub) - usb_disconnect (&ohci->bus->root_hub); - - /* empty the interrupt branches */ - for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; - for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0; - - /* no EDs to remove */ - ohci->ed_rm_list [0] = NULL; - ohci->ed_rm_list [1] = NULL; - - /* empty control and bulk lists */ - ohci->ed_isotail = NULL; - ohci->ed_controltail = NULL; - ohci->ed_bulktail = NULL; - - if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { - err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp); - } else - dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name); -} - -#endif /* CONFIG_PM */ - -/*-------------------------------------------------------------------------*/ - -/* configured so that an OHCI device is always provided */ -/* always called with process context; sleeping is OK */ - -static int __devinit -ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) -{ - 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) +/* + * Host bus independent remove one OHCI host controller. + */ +void __devexit hc_remove_ohci(ohci_t *ohci) { - ohci_t *ohci = pci_get_drvdata(dev); - - dbg ("remove %s controller usb-%s%s%s", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - dev->slot_name, - ohci->disabled ? " (disabled)" : "", - in_interrupt () ? " in interrupt" : "" - ); #ifdef DEBUG ohci_dump (ohci, 1); #endif @@ -2739,270 +2545,8 @@ &ohci->regs->control); hc_release_ohci (ohci); - - release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); - pci_disable_device (dev); } - -#ifdef CONFIG_PM - -/*-------------------------------------------------------------------------*/ - -static int -ohci_pci_suspend (struct pci_dev *dev, u32 state) -{ - ohci_t *ohci = pci_get_drvdata(dev); - unsigned long flags; - u16 cmd; - - if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { - dbg ("can't suspend usb-%s (state is %s)", dev->slot_name, - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); - return -EIO; - } - - /* act as if usb suspend can always be used */ - info ("USB suspend: usb-%s", dev->slot_name); - ohci->sleeping = 1; - - /* First stop processing */ - spin_lock_irqsave (&ohci->ohci_lock, flags); - ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); - writel (ohci->hc_control, &ohci->regs->control); - writel (OHCI_INTR_SF, &ohci->regs->intrstatus); - (void) readl (&ohci->regs->intrstatus); - spin_unlock_irqrestore (&ohci->ohci_lock, flags); - - /* Wait a frame or two */ - mdelay(1); - if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) - mdelay (1); - -#ifdef CONFIG_PMAC_PBOOK - if (_machine == _MACH_Pmac) - disable_irq (ohci->irq); - /* else, 2.4 assumes shared irqs -- don't disable */ -#endif - /* Enable remote wakeup */ - writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable); - - /* Suspend chip and let things settle down a bit */ - ohci->hc_control = OHCI_USB_SUSPEND; - writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); - mdelay (500); /* No schedule here ! */ - switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { - case OHCI_USB_RESET: - dbg("Bus in reset phase ???"); - break; - case OHCI_USB_RESUME: - dbg("Bus in resume phase ???"); - break; - case OHCI_USB_OPER: - dbg("Bus in operational phase ???"); - break; - case OHCI_USB_SUSPEND: - dbg("Bus suspended"); - break; - } - /* In some rare situations, Apple's OHCI have happily trashed - * memory during sleep. We disable it's bus master bit during - * suspend - */ - pci_read_config_word (dev, PCI_COMMAND, &cmd); - cmd &= ~PCI_COMMAND_MASTER; - pci_write_config_word (dev, PCI_COMMAND, cmd); -#ifdef CONFIG_PMAC_PBOOK - { - struct device_node *of_node; - - /* Disable USB PAD & cell clock */ - of_node = pci_device_to_OF_node (ohci->ohci_dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); - } -#endif - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int -ohci_pci_resume (struct pci_dev *dev) -{ - ohci_t *ohci = pci_get_drvdata(dev); - int temp; - unsigned long flags; - - /* guard against multiple resumes */ - atomic_inc (&ohci->resume_count); - if (atomic_read (&ohci->resume_count) != 1) { - err ("concurrent PCI resumes for usb-%s", dev->slot_name); - atomic_dec (&ohci->resume_count); - return 0; - } - -#ifdef CONFIG_PMAC_PBOOK - { - struct device_node *of_node; - - /* Re-enable USB PAD & cell clock */ - of_node = pci_device_to_OF_node (ohci->ohci_dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1); - } -#endif - - /* did we suspend, or were we powered off? */ - ohci->hc_control = readl (&ohci->regs->control); - temp = ohci->hc_control & OHCI_CTRL_HCFS; - -#ifdef DEBUG - /* the registers may look crazy here */ - ohci_dump_status (ohci); -#endif - - /* Re-enable bus mastering */ - pci_set_master(ohci->ohci_dev); - - switch (temp) { - - case OHCI_USB_RESET: // lost power - info ("USB restart: usb-%s", dev->slot_name); - hc_restart (ohci); - break; - - case OHCI_USB_SUSPEND: // host wakeup - case OHCI_USB_RESUME: // remote wakeup - info ("USB continue: usb-%s from %s wakeup", dev->slot_name, - (temp == OHCI_USB_SUSPEND) - ? "host" : "remote"); - ohci->hc_control = OHCI_USB_RESUME; - writel (ohci->hc_control, &ohci->regs->control); - (void) readl (&ohci->regs->control); - mdelay (20); /* no schedule here ! */ - /* Some controllers (lucent) need a longer delay here */ - mdelay (15); - temp = readl (&ohci->regs->control); - temp = ohci->hc_control & OHCI_CTRL_HCFS; - if (temp != OHCI_USB_RESUME) { - err ("controller usb-%s won't resume", dev->slot_name); - ohci->disabled = 1; - return -EIO; - } - - /* Some chips likes being resumed first */ - writel (OHCI_USB_OPER, &ohci->regs->control); - (void) readl (&ohci->regs->control); - mdelay (3); - - /* Then re-enable operations */ - spin_lock_irqsave (&ohci->ohci_lock, flags); - ohci->disabled = 0; - ohci->sleeping = 0; - ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; - if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) { - if (ohci->ed_controltail) - ohci->hc_control |= OHCI_CTRL_CLE; - if (ohci->ed_bulktail) - ohci->hc_control |= OHCI_CTRL_BLE; - } - writel (ohci->hc_control, &ohci->regs->control); - writel (OHCI_INTR_SF, &ohci->regs->intrstatus); - writel (OHCI_INTR_SF, &ohci->regs->intrenable); - /* Check for a pending done list */ - writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); - (void) readl (&ohci->regs->intrdisable); -#ifdef CONFIG_PMAC_PBOOK - if (_machine == _MACH_Pmac) - enable_irq (ohci->irq); -#endif - if (ohci->hcca->done_head) - dl_done_list (ohci, dl_reverse_done_list (ohci)); - writel (OHCI_INTR_WDH, &ohci->regs->intrenable); - writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - spin_unlock_irqrestore (&ohci->ohci_lock, flags); - break; - - default: - warn ("odd PCI resume for usb-%s", dev->slot_name); - } - - /* controller is operational, extra resumes are harmless */ - atomic_dec (&ohci->resume_count); - - return 0; -} - -#endif /* CONFIG_PM */ - - -/*-------------------------------------------------------------------------*/ - -static const struct pci_device_id __devinitdata ohci_pci_ids [] = { { - - /* - * AMD-756 [Viper] USB has a serious erratum when used with - * lowspeed devices like mice. - */ - vendor: 0x1022, - device: 0x740c, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - - driver_data: OHCI_QUIRK_AMD756, - -} , { - - /* handle any USB OHCI controller */ - class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10), - class_mask: ~0, - - /* no matter who makes it */ - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - - }, { /* end: all zeroes */ } -}; - -MODULE_DEVICE_TABLE (pci, ohci_pci_ids); - -static struct pci_driver ohci_pci_driver = { - name: "usb-ohci", - id_table: &ohci_pci_ids [0], - - probe: ohci_pci_probe, - remove: __devexit_p(ohci_pci_remove), - -#ifdef CONFIG_PM - suspend: ohci_pci_suspend, - resume: ohci_pci_resume, -#endif /* PM */ -}; - - -/*-------------------------------------------------------------------------*/ - -static int __init ohci_hcd_init (void) -{ - return pci_module_init (&ohci_pci_driver); -} - -/*-------------------------------------------------------------------------*/ - -static void __exit ohci_hcd_cleanup (void) -{ - pci_unregister_driver (&ohci_pci_driver); -} - -module_init (ohci_hcd_init); -module_exit (ohci_hcd_cleanup); - - MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL"); --- linux-2.4.27/drivers/usb/host/usb-ohci.h~2.4.27-vrs1 +++ linux-2.4.27/drivers/usb/host/usb-ohci.h @@ -384,12 +384,13 @@ #define OHCI_QUIRK_SUCKYIO 0x02 /* NSC superio */ struct ohci_regs * regs; /* OHCI controller's memory */ + struct list_head ohci_hcd_list; /* list of all ohci_hcd */ + struct ohci * next; // chain of ohci device contexts struct list_head timeout_list; // struct list_head urb_list; // list of all pending urbs - spinlock_t ohci_lock; /* Covers all fields up & down */ - struct urb *complete_head, *complete_tail; - + // spinlock_t urb_list_lock; // lock to keep consistency + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */ ed_t * ed_bulktail; /* last endpoint of bulk list */ @@ -403,6 +404,7 @@ /* PCI device handle, settings, ... */ struct pci_dev *ohci_dev; + const char *slot_name; u8 pci_latency; struct pci_pool *td_cache; struct pci_pool *dev_cache; @@ -448,7 +450,7 @@ #endif #ifndef CONFIG_PCI -# error "usb-ohci currently requires PCI-based controllers" +//# error "usb-ohci currently requires PCI-based controllers" /* to support non-PCI OHCIs, you need custom bus/mem/... glue */ #endif @@ -641,3 +643,6 @@ pci_pool_free (hc->dev_cache, dev, dev->dma); } +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE --- linux-2.4.27/drivers/video/Config.in~2.4.27-vrs1 +++ linux-2.4.27/drivers/video/Config.in @@ -30,13 +30,28 @@ tristate ' Permedia3 support (EXPERIMENTAL)' CONFIG_FB_PM3 fi fi - if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - bool ' Acorn VIDC support' CONFIG_FB_ACORN - fi - dep_tristate ' Cyber2000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI - if [ "$CONFIG_ARCH_SA1100" = "y" ]; then - bool ' SA-1100 LCD support' CONFIG_FB_SA1100 + if [ "$CONFIG_ARM" = "y" ]; then + if [ "$CONFIG_ARCH_ACORN" -o "$CONFIG_ARCH_RISCSTATION" ]; then + bool ' Acorn VIDC support' CONFIG_FB_ACORN + else + define_bool CONFIG_FB_ACORN n + fi + dep_bool ' Anakin LCD support' CONFIG_FB_ANAKIN $CONFIG_ARCH_ANAKIN + dep_bool ' CLPS711X LCD support' CONFIG_FB_CLPS711X $CONFIG_ARCH_CLPS711X + dep_bool ' SA-1100 LCD support' CONFIG_FB_SA1100 $CONFIG_ARCH_SA1100 + dep_bool ' MX1ADS LCD support' CONFIG_FB_DBMX1 $CONFIG_ARCH_MX1ADS + if [ "$CONFIG_FB_SA1100" = "y" -a "$CONFIG_SA1100_CERF" = "y" ]; then + choice 'CerfBoard LCD Display Size' \ + "3.8_Color CONFIG_CERF_LCD_38_A \ + 3.8_Mono CONFIG_CERF_LCD_38_B \ + 5.7 CONFIG_CERF_LCD_57_A \ + 7.2 CONFIG_CERF_LCD_72_A" 5.7 + fi + if [ "$CONFIG_FB_SA1100" = "y" -a "$CONFIG_SA1100_CERF_CPLD" = "y" ]; then + bool 'Cerfboard Backlight (CerfPDA)' CONFIG_SA1100_CERF_LCD_BACKLIGHT + fi fi + dep_tristate ' CyberPro 2000/2010/5000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI if [ "$CONFIG_APOLLO" = "y" ]; then define_bool CONFIG_FB_APOLLO y fi @@ -265,7 +280,7 @@ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_RETINAZ3" = "y" -o \ "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ "$CONFIG_FB_BWTWO" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ - "$CONFIG_FB_TX3912" = "y" ]; then + "$CONFIG_FB_TX3912" = "y" -o "$CONFIG_FB_CLPS711X" = "y" ]; then define_tristate CONFIG_FBCON_MFB y else if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_AMIGA" = "m" -o \ @@ -273,19 +288,20 @@ "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_RETINAZ3" = "m" -o \ "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ "$CONFIG_FB_BWTWO" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ - "$CONFIG_FB_TX3912" = "m" ]; then + "$CONFIG_FB_TX3912" = "m" -o "$CONFIG_FB_CLPS711X" = "m" ]; then define_tristate CONFIG_FBCON_MFB m fi fi if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_MAC" = "y" -o \ "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ - "$CONFIG_FB_TX3912" = "y" ]; then + "$CONFIG_FB_TX3912" = "y" -o "$CONFIG_FB_CLPS711X" = "y" -o \ + "$CONFIG_FB_DBMX1" = "y" ]; then define_tristate CONFIG_FBCON_CFB2 y define_tristate CONFIG_FBCON_CFB4 y else if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_MAC" = "m" -o \ "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ - "$CONFIG_FB_TX3912" = "m" ]; then + "$CONFIG_FB_TX3912" = "m" -o "$CONFIG_FB_CLPS711X" = "m" ]; then define_tristate CONFIG_FBCON_CFB2 m define_tristate CONFIG_FBCON_CFB4 m fi @@ -312,7 +328,8 @@ "$CONFIG_FB_TX3912" = "y" -o \ "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_NEOMAGIC" = "y" -o \ "$CONFIG_FB_STI" = "y" -o "$CONFIG_FB_HP300" = "y" -o \ - "$CONFIG_FB_INTEL" = "y" ]; then + "$CONFIG_FB_INTEL" = "y" -o \ + "$CONFIG_FB_DBMX1" = "y" ]; then define_tristate CONFIG_FBCON_CFB8 y else if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \ @@ -354,7 +371,9 @@ "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_3DFX" = "y" -o \ "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_SA1100" = "y" -o \ "$CONFIG_FB_PVR2" = "y" -o "$CONFIG_FB_VOODOO1" = "y" -o \ - "$CONFIG_FB_NEOMAGIC" = "y" -o "$CONFIG_FB_INTEL" = "y" ]; then + "$CONFIG_FB_NEOMAGIC" = "y" -o "$CONFIG_FB_INTEL" = "y" -o \ + "$CONFIG_FB_ANAKIN" = "y" -o \ + "$CONFIG_FB_DBMX1" = "y" ]; then define_tristate CONFIG_FBCON_CFB16 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ @@ -509,6 +528,9 @@ if [ "$CONFIG_AMIGA" = "y" ]; then define_bool CONFIG_FONT_PEARL_8x8 y fi + if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_RISCSTATION" = "y" ]; then + define_bool CONFIG_FONT_ACORN_8x8 y + fi if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_ACORN" = "y" ]; then define_bool CONFIG_FONT_ACORN_8x8 y fi --- linux-2.4.27/drivers/video/Makefile~2.4.27-vrs1 +++ linux-2.4.27/drivers/video/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_FB_PLATINUM) += platinumfb.o obj-$(CONFIG_FB_VALKYRIE) += valkyriefb.o obj-$(CONFIG_FB_CT65550) += chipsfb.o +obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o obj-$(CONFIG_FB_CYBER) += cyberfb.o obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o obj-$(CONFIG_FB_SGIVW) += sgivwfb.o @@ -68,7 +69,7 @@ obj-$(CONFIG_FB_TRIDENT) += tridentfb.o fbgen.o obj-$(CONFIG_FB_S3TRIO) += S3triofb.o obj-$(CONFIG_FB_TGA) += tgafb.o fbgen.o -obj-$(CONFIG_FB_VESA) += vesafb.o +obj-$(CONFIG_FB_VESA) += vesafb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o fbcon-vga-planes.o obj-$(CONFIG_FB_VIRGE) += virgefb.o obj-$(CONFIG_FB_G364) += g364fb.o @@ -126,14 +127,16 @@ obj-$(CONFIG_FB_SUN3) += sun3fb.o obj-$(CONFIG_FB_BWTWO) += bwtwofb.o -obj-$(CONFIG_FB_HGA) += hgafb.o +obj-$(CONFIG_FB_HGA) += hgafb.o obj-$(CONFIG_FB_SA1100) += sa1100fb.o -obj-$(CONFIG_FB_VIRTUAL) += vfb.o +obj-$(CONFIG_FB_DBMX1) += dbmx1fb.o +obj-$(CONFIG_FB_VIRTUAL) += vfb.o obj-$(CONFIG_FB_HIT) += hitfb.o fbgen.o obj-$(CONFIG_FB_E1355) += epson1355fb.o fbgen.o obj-$(CONFIG_FB_E1356) += epson1356fb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o +obj-$(CONFIG_FB_ANAKIN) += anakinfb.o # Generic Low Level Drivers @@ -169,4 +172,3 @@ -e 's/dfont\(_uni.*\]\)/promfont\1 __initdata/' > promcon_tbl.c promcon_tbl.o: promcon_tbl.c $(TOPDIR)/include/linux/types.h - --- linux-2.4.27/drivers/video/acornfb.c~2.4.27-vrs1 +++ linux-2.4.27/drivers/video/acornfb.c @@ -752,11 +752,12 @@ } #endif #ifdef FBCON_HAS_CFB16 - if (bpp == 16 && regno < 16) { + if (bpp == 16) { int i; - current_par.cmap.cfb16[regno] = - regno | regno << 5 | regno << 10; + if (regno < 16) + current_par.cmap.cfb16[regno] = + regno | regno << 5 | regno << 10; pal.p = 0; vidc_writel(0x10000000); @@ -1215,7 +1216,7 @@ p.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green; p.vidc20.blue = current_par.palette[(i >> 2) & 31].vidc20.blue; } - acornfb_palette_write(i, current_par.palette[i]); + acornfb_palette_write(i, p); } } else #endif --- /dev/null +++ linux-2.4.27/drivers/video/anakinfb.c @@ -0,0 +1,221 @@ +/* + * linux/drivers/video/anakinfb.c + * + * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 23-Apr-2001 TTC Created + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include