diff -Naur linux-2.4.32.orig/Makefile linux-2.4.32/Makefile --- linux-2.4.32.orig/Makefile 2005-11-16 19:12:54.000000000 +0000 +++ linux-2.4.32/Makefile 2006-03-19 22:47:02.000000000 +0000 @@ -1,11 +1,12 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 32 -EXTRAVERSION = +EXTRAVERSION = -5mx KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) +ARCH := arm KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g") CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ @@ -19,7 +20,7 @@ HOSTCC = gcc HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -CROSS_COMPILE = +CROSS_COMPILE = arm-linux- # # Include the make variables (CC, etc...) @@ -161,6 +162,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 diff -Naur linux-2.4.32.orig/arch/arm/Makefile linux-2.4.32/arch/arm/Makefile --- linux-2.4.32.orig/arch/arm/Makefile 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/Makefile 2006-03-19 22:09:12.000000000 +0000 @@ -52,7 +52,7 @@ CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm -AFLAGS +=$(apcs-y) $(arch-y) -mno-fpu -msoft-float +AFLAGS +=$(apcs-y) $(arch-y) -msoft-float ifeq ($(CONFIG_CPU_26),y) PROCESSOR := armo @@ -156,6 +156,11 @@ TEXTADDR = 0xc0208000 endif +ifeq ($(CONFIG_ARCH_PSIONW),y) +TEXTADDR = 0xc0048000 +MACHINE = psionw +endif + ifeq ($(CONFIG_ARCH_ANAKIN),y) MACHINE = anakin endif diff -Naur linux-2.4.32.orig/arch/arm/config.in linux-2.4.32/arch/arm/config.in --- linux-2.4.32.orig/arch/arm/config.in 2004-11-17 11:54:21.000000000 +0000 +++ linux-2.4.32/arch/arm/config.in 2006-03-19 21:11:28.000000000 +0000 @@ -45,6 +45,7 @@ Omaha CONFIG_ARCH_OMAHA \ LinkUp-L7200 CONFIG_ARCH_L7200 \ Motorola-MX1ADS CONFIG_ARCH_MX1ADS \ + Psion-Windermere CONFIG_ARCH_PSIONW \ RiscPC CONFIG_ARCH_RPC \ RiscStation CONFIG_ARCH_RISCSTATION \ SA1100-based CONFIG_ARCH_SA1100 \ @@ -181,6 +182,18 @@ endmenu +if [ "$CONFIG_ARCH_PSIONW" = "y" ]; then +mainmenu_option next_comment +comment 'Psion Windermere Implementations' +choice 'Psion-Windermere Implementations' \ + "Psion-5MX-16MB-and-Ericsson-MC218 CONFIG_PSIONW_5MX \ + Psion-5MX-Pro-24MB CONFIG_PSIONW_5MXPRO24MB \ + Psion-5MX-Pro-32MB CONFIG_PSIONW_5MXPRO32MB \ + Psion-Revo-8MB CONFIG_PSIONW_REVO \ + Psion-Revo-Plus-16MB-and-Diamond-Mako CONFIG_PSIONW_REVOPLUS" CONFIG_PSIONW_5MX +endmenu +fi + # Definitions to make life easier if [ "$CONFIG_ARCH_ARCA5K" = "y" -o \ "$CONFIG_ARCH_RPC" = "y" ]; then @@ -287,6 +300,7 @@ # ARM720T if [ "$CONFIG_ARCH_CLPS711X" = "y" -o \ + "$CONFIG_ARCH_PSIONW" = "y" -o \ "$CONFIG_ARCH_L7200" = "y" -o \ "$CONFIG_ARCH_CDB89712" = "y" ]; then define_bool CONFIG_CPU_ARM720T y @@ -413,6 +427,7 @@ # Select various configuration options depending on the machine type if [ "$CONFIG_ARCH_EDB7211" = "y" -o \ + "$CONFIG_ARCH_PSIONW" = "y" -o \ "$CONFIG_ARCH_SA1100" = "y" -o \ "$CONFIG_ARCH_RISCSTATION" = "y" ]; then define_bool CONFIG_DISCONTIGMEM y @@ -511,6 +526,7 @@ "$CONFIG_ARCH_SHARK" = "y" -o \ "$CONFIG_ARCH_CO285" = "y" -o \ "$CONFIG_ARCH_SA1100" = "y" -o \ + "$CONFIG_ARCH_PSIONW" = "y" -o \ "$CONFIG_ARCH_INTEGRATOR" = "y" -o \ "$CONFIG_ARCH_CDB89712" = "y" -o \ "$CONFIG_ARCH_P720T" = "y" -o \ @@ -522,6 +538,7 @@ "$CONFIG_ARCH_SHARK" = "y" -o \ "$CONFIG_ARCH_CO285" = "y" -o \ "$CONFIG_ARCH_SA1100" = "y" -o \ + "$CONFIG_ARCH_PSIONW" = "y" -o \ "$CONFIG_ARCH_INTEGRATOR" = "y" -o \ "$CONFIG_ARCH_P720T" = "y" -o \ "$CONFIG_ARCH_OMAHA" = "y" ]; then @@ -599,7 +616,8 @@ fi endmenu -if [ "$CONFIG_ARCH_CLPS711X" = "y" ]; then +if [ "$CONFIG_ARCH_CLPS711X" = "y" -o \ + "$CONFIG_ARCH_PSIONW" = "y" ]; then # This is _meant_ to be ssi _not_ scsi. It is not a spelling error. source drivers/ssi/Config.in fi @@ -678,6 +696,7 @@ "$CONFIG_ARCH_TBOX" = "y" -o \ "$CONFIG_ARCH_SHARK" = "y" -o \ "$CONFIG_ARCH_SA1100" = "y" -o \ + "$CONFIG_ARCH_PSIONW" = "y" -o \ "$CONFIG_PCI" = "y" ]; then mainmenu_option next_comment comment 'Sound' diff -Naur linux-2.4.32.orig/arch/arm/kernel/Makefile linux-2.4.32/arch/arm/kernel/Makefile --- linux-2.4.32.orig/arch/arm/kernel/Makefile 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/Makefile 2006-03-19 22:32:51.000000000 +0000 @@ -45,7 +45,7 @@ $(CONFIG_FOOTBRIDGE) $(CONFIG_ARCH_EBSA110) \ $(CONFIG_ARCH_SA1100) $(CONFIG_ARCH_CAMELOT) \ $(CONFIG_ARCH_MX1ADS) $(CONFIG_ARCH_OMAHA) \ - $(CONFIG_ARCH_AT91RM9200) + $(CONFIG_ARCH_AT91RM9200) $(CONFIG_ARCH_PSIONW) ifneq ($(findstring y,$(no-irq-arch)),y) obj-y += irq-arch.o @@ -54,6 +54,7 @@ obj-$(CONFIG_ARCH_ACORN) += ecard.o fiq.o time-acorn.o obj-$(CONFIG_ARCH_CLPS7500) += time-acorn.o obj-$(CONFIG_ARCH_RISCSTATION) += time-acorn.o +obj-$(CONFIG_ARCH_PSIONW) += fiq.o psionw_pm.o obj-$(CONFIG_DEBUG_LL) += debug-$(PROCESSOR).o obj-$(CONFIG_MODULES) += armksyms.o obj-$(CONFIG_ARTHUR) += arthur.o diff -Naur linux-2.4.32.orig/arch/arm/kernel/debug-armv.S linux-2.4.32/arch/arm/kernel/debug-armv.S --- linux-2.4.32.orig/arch/arm/kernel/debug-armv.S 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/debug-armv.S 2006-03-19 21:09:04.000000000 +0000 @@ -470,6 +470,37 @@ +#elif defined(CONFIG_ARCH_PSIONW) + +#include + + .macro addruart,rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + moveq \rx, #PSIONW_PHYS_BASE + movne \rx, #PSIONW_VIRT_BASE + orr \rx, \rx, #0x0700 @ UART2 + .endm + + .macro senduart,rd,rx + str \rd, [\rx] @ UARTDR + .endm + + .macro waituart,rd,rx +1001: ldr \rd, [\rx, #0x0010] @ SYSFLGx + tst \rd, #1 << 3 @ UBUSYx + bne 1001b + .endm + + .macro busyuart,rd,rx + tst \rx, #0x0000 @ UART2 does not have CTS here + bne 1002f +1001: ldr \rd, [\rx, #0x0010] @ SYSFLGx + tst \rd, #1 << 0 @ CTS + bne 1001b +1002: + .endm + #else #error Unknown architecture #endif diff -Naur linux-2.4.32.orig/arch/arm/kernel/dma.c linux-2.4.32/arch/arm/kernel/dma.c --- linux-2.4.32.orig/arch/arm/kernel/dma.c 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/dma.c 2006-03-19 22:42:32.000000000 +0000 @@ -22,9 +22,11 @@ #include +#if 0 spinlock_t dma_spin_lock = SPIN_LOCK_UNLOCKED; +#endif -#if MAX_DMA_CHANNELS > 0 +#if MAX_DMA_CHANNELS static dma_t dma_chan[MAX_DMA_CHANNELS]; @@ -262,10 +264,12 @@ #else +#if 0 int request_dma(dmach_t channel, const char *device_id) { return -EINVAL; } +#endif int get_dma_residue(dmach_t channel) { @@ -275,8 +279,10 @@ #define GLOBAL_ALIAS(_a,_b) asm (".set " #_a "," #_b "; .globl " #_a) GLOBAL_ALIAS(disable_dma, get_dma_residue); GLOBAL_ALIAS(enable_dma, get_dma_residue); +#if 0 GLOBAL_ALIAS(free_dma, get_dma_residue); GLOBAL_ALIAS(get_dma_list, get_dma_residue); +#endif GLOBAL_ALIAS(set_dma_mode, get_dma_residue); GLOBAL_ALIAS(set_dma_page, get_dma_residue); GLOBAL_ALIAS(set_dma_count, get_dma_residue); @@ -301,4 +307,6 @@ EXPORT_SYMBOL(set_dma_speed); EXPORT_SYMBOL(dma_channel_active); +#if 0 EXPORT_SYMBOL(dma_spin_lock); +#endif diff -Naur linux-2.4.32.orig/arch/arm/kernel/entry-armv.S linux-2.4.32/arch/arm/kernel/entry-armv.S --- linux-2.4.32.orig/arch/arm/kernel/entry-armv.S 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/entry-armv.S 2006-03-19 21:09:04.000000000 +0000 @@ -566,7 +566,42 @@ .macro irq_prio_table .endm - + +#elif defined(CONFIG_ARCH_PSIONW) + +#include + + .macro disable_fiq + .endm + + .macro get_irqnr_and_base, irqnr, stat, base, mask + mov \base, #PSIONW_BASE + ldr \stat, [\base, #INTRSR] + ldr \mask, [\base, #INTENS] + mov \irqnr, #4 + mov \mask, \mask, lsl #16 + and \stat, \stat, \mask, lsr #16 + movs \stat, \stat, lsr #4 + bne 1001f + +1001: tst \stat, #255 + addeq \irqnr, \irqnr, #8 + moveq \stat, \stat, lsr #8 + tst \stat, #15 + addeq \irqnr, \irqnr, #4 + moveq \stat, \stat, lsr #4 + tst \stat, #3 + addeq \irqnr, \irqnr, #2 + moveq \stat, \stat, lsr #2 + tst \stat, #1 + addeq \irqnr, \irqnr, #1 + moveq \stat, \stat, lsr #1 + tst \stat, #1 @ bit 0 should be set + .endm + + .macro irq_prio_table + .endm + #elif defined (CONFIG_ARCH_CAMELOT) #include #undef IRQ_MODE /* same name defined in asm/proc/ptrace.h */ diff -Naur linux-2.4.32.orig/arch/arm/kernel/fiq.c linux-2.4.32/arch/arm/kernel/fiq.c --- linux-2.4.32.orig/arch/arm/kernel/fiq.c 2003-06-13 15:51:29.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/fiq.c 2006-03-19 22:12:56.000000000 +0000 @@ -122,22 +122,22 @@ 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 %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" #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 + "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" #endif : "=&r" (tmp), "=&r" (tmp2) @@ -154,22 +154,22 @@ 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 %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" #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 + "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" #endif : "=&r" (tmp), "=&r" (tmp2) diff -Naur linux-2.4.32.orig/arch/arm/kernel/head-armv.S linux-2.4.32/arch/arm/kernel/head-armv.S --- linux-2.4.32.orig/arch/arm/kernel/head-armv.S 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/head-armv.S 2006-03-19 21:09:04.000000000 +0000 @@ -130,6 +130,22 @@ */ mov r1, #MACH_TYPE_L7200 #endif +#if defined(CONFIG_ARCH_PSIONW) /* PSIONW_NONSTANDARD */ + /* + * FIXME5MX Interrupts should be disabled in the boot loader. + * This will cause the kernel not to boot after early 2.4 + * kernels, as the save_and_disable_irqs assumes that fiqs + * are on. If the interrupts are not turned off, this will + * cause the system to hang. Hard to track down too, btw. + * For development, you may want to temporarily use + * mov \temp, #I_BIT | F_BIT | MODE_SVC in function + * save_and_disable_irqs if you are having trouble booting. + */ + mov r8, #0x80000000 @ physical base + mov r9, #0xff00 @ create 0xffff + add r9, r9, #0x00ff @ with two steps + str r9, [r8, #0x050c] @ write to INTENC +#endif mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode msr cpsr_c, r0 @ and all irqs disabled diff -Naur linux-2.4.32.orig/arch/arm/kernel/irq.c linux-2.4.32/arch/arm/kernel/irq.c --- linux-2.4.32.orig/arch/arm/kernel/irq.c 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/irq.c 2006-03-19 21:09:04.000000000 +0000 @@ -145,7 +145,7 @@ *p++ = '\n'; } -#ifdef CONFIG_ARCH_ACORN +#if defined(CONFIG_ARCH_ACORN) || defined(CONFIG_ARCH_PSIONW) p += get_fiq_list(p); #endif p += sprintf(p, "Err: %10lu\n", irq_err_count); diff -Naur linux-2.4.32.orig/arch/arm/kernel/psionw_pm.c linux-2.4.32/arch/arm/kernel/psionw_pm.c --- linux-2.4.32.orig/arch/arm/kernel/psionw_pm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/psionw_pm.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,116 @@ +/* + * Psion battery FIQ handler. Copyright 2002 Tony Lindgren + * + * Modeled after Russell King's dma-rpc.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static struct fiq_handler extfiq_fh = { + name: "extfiq" +}; + +static struct fiq_handler blint_fh = { + name: "blint" +}; + +/* + * Sets up a handler for the external fiq + * NOTE: Not currently in use as Linux does not + * currently support sharing the fiq vector. + */ +static void extfiq_handler_setup(void) +{ + void *fiqhandler_start; + unsigned int fiqhandler_length; + struct pt_regs regs; + extern unsigned char extfiq_handler_start, extfiq_handler_end; + + printk("FIQ: Setting up handler for external fast interrupt\n"); + + fiqhandler_start = &extfiq_handler_start; + fiqhandler_length = &extfiq_handler_end - &extfiq_handler_start; + + if (claim_fiq(&extfiq_fh)) { + printk("FIQ: Could not claim fast interrupt\n"); + return; + } + + set_fiq_handler(fiqhandler_start, fiqhandler_length); + set_fiq_regs(®s); + + enable_fiq(IRQ_EXTFIQ); +} + +/* + * Sets up a handler for the battery low fiq + */ +static void blint_fiq_handler_setup(void) +{ + void *fiqhandler_start; + unsigned int fiqhandler_length; + struct pt_regs regs; + extern unsigned char blint_fiq_handler_start, blint_fiq_handler_end; + + printk("FIQ: Setting up handler for battery low fast interrupt\n"); + + fiqhandler_start = &blint_fiq_handler_start; + fiqhandler_length = &blint_fiq_handler_end - &blint_fiq_handler_start; + + if (claim_fiq(&blint_fh)) { + printk("FIQ: Could not claim fast interrupt\n"); + return; + } + + set_fiq_handler(fiqhandler_start, fiqhandler_length); + set_fiq_regs(®s); + psionw_writel(1, BLEOI); + enable_fiq(IRQ_BLINT); +} + +static int __init psionw_pm_init(void) +{ + printk("FIQ: Initializing Psion fast interrupts\n"); + //extfiq_handler_setup(); + blint_fiq_handler_setup(); +} + +static void __exit psionw_pm_exit(void) +{ + printk("FIQ: Disabling Psion fast interrupts\n"); + + //disable_fiq(IRQ_EXTFIQ); + //release_fiq(&extfiq_fh); + + disable_fiq(IRQ_BLINT); + release_fiq(&blint_fh); +} + + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); + +module_init(psionw_pm_init); +module_exit(psionw_pm_exit); diff -Naur linux-2.4.32.orig/arch/arm/kernel/semaphore.c linux-2.4.32/arch/arm/kernel/semaphore.c --- linux-2.4.32.orig/arch/arm/kernel/semaphore.c 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/kernel/semaphore.c 2006-03-19 22:11:14.000000000 +0000 @@ -193,7 +193,7 @@ bl __down_interruptible \n\ mov ip, r0 \n\ ldmfd sp!, {r0 - r3, pc}^ \n\ - + \n\ .align 5 \n\ .globl __down_trylock_failed \n\ __down_trylock_failed: \n\ diff -Naur linux-2.4.32.orig/arch/arm/lib/Makefile linux-2.4.32/arch/arm/lib/Makefile --- linux-2.4.32.orig/arch/arm/lib/Makefile 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/lib/Makefile 2006-03-19 21:09:04.000000000 +0000 @@ -26,6 +26,7 @@ obj-clps7500 := io-acorn.o obj-l7200 := io-acorn.o obj-shark := io-shark.o +obj-psionw := psionwfiqs.o obj-edb7211 := io-acorn.o obj-riscstation := io-acorn.o floppydma.o diff -Naur linux-2.4.32.orig/arch/arm/lib/getuser.S linux-2.4.32/arch/arm/lib/getuser.S --- linux-2.4.32.orig/arch/arm/lib/getuser.S 2002-08-03 01:39:42.000000000 +0100 +++ linux-2.4.32/arch/arm/lib/getuser.S 2006-03-19 21:45:43.000000000 +0000 @@ -17,7 +17,7 @@ * * Inputs: r0 contains the address * Outputs: r0 is the error code - * r1, r2 contains the zero-extended value + * r1, ip contains the zero-extended value * lr corrupted * * No other registers must be altered. (see include/asm-arm/uaccess.h @@ -42,14 +42,14 @@ .global __get_user_2 __get_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: ldrlsbt r1, [r0], #1 -3: ldrlsbt r2, [r0] - orrls r1, r1, r2, lsl #8 +3: ldrlsbt ip, [r0] + orrls r1, r1, ip, lsl #8 movls r0, #0 movls pc, lr b __get_user_bad @@ -68,20 +68,20 @@ .global __get_user_8 __get_user_8: - bic r2, sp, #0x1f00 - bic r2, r2, #0x00ff - ldr r2, [r2, #TSK_ADDR_LIMIT] - sub r2, r2, #8 - cmp r0, r2 + bic ip, sp, #0x1f00 + bic ip, ip, #0x00ff + ldr ip, [ip, #TSK_ADDR_LIMIT] + sub ip, ip, #8 + cmp r0, ip 5: ldrlst r1, [r0], #4 -6: ldrlst r2, [r0] +6: ldrlst ip, [r0] movls r0, #0 movls pc, lr /* fall through */ __get_user_bad_8: - mov r2, #0 + mov ip, #0 __get_user_bad: mov r1, #0 mov r0, #-14 diff -Naur linux-2.4.32.orig/arch/arm/lib/psionwfiqs.S linux-2.4.32/arch/arm/lib/psionwfiqs.S --- linux-2.4.32.orig/arch/arm/lib/psionwfiqs.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/lib/psionwfiqs.S 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * linux/arch/arm/lib/psionwblint.S + * + * Copyright (C) 2002 Tony Lindgren + * + * Modeled after Russel King's floppydma.S + * + * 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 + .text + + .global SYMBOL_NAME(extfiq_handler_end) +ENTRY(extfiq_handler_start) + mov r11, #0xdf000000 @ VBASE + mov r12, #'E' + strb r12, [r11, #0x700] @ UART2 + subs pc, lr, #4 @ return +SYMBOL_NAME(extfiq_handler_end): + + .global SYMBOL_NAME(blint_fiq_handler_end) +ENTRY(blint_fiq_handler_start) + mov r11, #0xdf000000 @ VBASE + str r11, [r11, #0x0410] @ write to BLEOI + + ldr r12, [r11, #0x0e08] @ load PCDR + bic r12, r12, #0x10 @ clear PCDR_LIGHT + str r12, [r11, #0x0e08] @ write PCDR + + mov r12, #'B' + strb r12, [r11, #0x700] @ UART2 + subs pc, lr, #4 @ return +SYMBOL_NAME(blint_fiq_handler_end): diff -Naur linux-2.4.32.orig/arch/arm/lib/putuser.S linux-2.4.32/arch/arm/lib/putuser.S --- linux-2.4.32.orig/arch/arm/lib/putuser.S 2001-10-11 17:04:57.000000000 +0100 +++ linux-2.4.32/arch/arm/lib/putuser.S 2006-03-19 21:45:43.000000000 +0000 @@ -16,7 +16,7 @@ * __put_user_X * * Inputs: r0 contains the address - * r1, r2 contains the value + * r1, ip contains the value * Outputs: r0 is the error code * lr corrupted * @@ -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 @@ -74,7 +74,7 @@ sub ip, ip, #8 cmp r0, ip 5: strlst r1, [r0], #4 -6: strlst r2, [r0] +6: strlst ip, [r0] movls r0, #0 movls pc, lr diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/Makefile linux-2.4.32/arch/arm/mach-psionw/Makefile --- linux-2.4.32.orig/arch/arm/mach-psionw/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/Makefile 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,27 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). + +USE_STANDARD_AS_RULE := true + +O_TARGET := psionw.o + +# Object file lists. + +obj-y := irq.o mm.o psionw-arch.o psionw-hardware.o +obj-m := +obj-n := +obj- := + +export-objs := psionw-time.o psionw-leds.o psionw-power.o serial-debug.o + +obj-$(CONFIG_ARCH_PSIONW) += psionw-time.o psionw-leds.o psionw-power.o serial-debug.o + +leds-$(CONFIG_ARCH_PSIONW) += psionw-leds.o +obj-$(CONFIG_LEDS) += $(leds-y) + +include $(TOPDIR)/Rules.make + diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/irq.c linux-2.4.32/arch/arm/mach-psionw/irq.c --- linux-2.4.32.orig/arch/arm/mach-psionw/irq.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/irq.c 2006-03-19 22:06:31.000000000 +0000 @@ -0,0 +1,217 @@ +/* + * linux/arch/arm/mach-psionw/irq.c + * + * Copyright (C) 2001 Yuji Shinokawa + * Copyright (C) 2001 Tony Lindgren + * + * 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 + +static int debug_on; +static struct irqaction *etna_action = NULL; + +#if CONFIG_PCMCIA_ETNA +extern void etna_interrupt(void); + +static struct tq_struct etna_int_task = { + routine:etna_interrupt +}; +#endif + +static void +mask_irq_int(unsigned int irq) +{ + u32 intmr; + intmr = (1 << irq); + psionw_writel(intmr, INTENC); +} + +#ifdef CONFIG_PCMCIA_ETNA +static void +mask_etna_irq(unsigned int irq) +{ + int etna_int, count = 0; + mask_irq_int(irq); + + etna_int = etna_readb(ETNA_INT_STATUS); + etna_action = irq_desc[irq].action; + + //printk("i0x%x ", etna_int); + + switch (etna_int) { + case (0x00): /* Card removed interrupt */ + irq_desc[irq].action = NULL; + printk("ETNA: Card removed\n"); + schedule_task(&etna_int_task); + break; + case (0x01): /* Card got hosed */ + irq_desc[irq].action = NULL; + while (etna_int == 0x01) { + count++; + schedule_task(&etna_int_task); + mdelay(500); + etna_int = etna_readb(ETNA_INT_STATUS); + if (count >= 5) { + printk("ETNA: Socket stuck, reinsert the card\n"); + break; + } + } + break; + case (0x03): + break; /* Network after nfs mount */ + case (0x05): /* Normal IDE interrupt */ + break; + case (0x07): /* Normal network interrupt */ + break; + case (0x3f): /* First write after sleep mode */ + case (0x35): /* First write after sleep mode */ + //irq_desc[IRQ_EINT1].action = NULL; + break; + default: + printk("ETNA: Unknown interrupt status: 0x%x\n", etna_int); + schedule_task(&etna_int_task); + } + + /* + * You would assume that we need to disable Etna interrupt + * until we're done? Nope, that causes delays in waking up + * the card for 16-bit writes in etna_outsw_ide. + */ + //etna_writeb(0, ETNA_INT_MASK); + + /* Clear the Etna interrupt */ + etna_writeb(ETNA_CLEAR_MASK, ETNA_INT_CLEAR); +} + +static void +unmask_etna_irq(unsigned int irq) +{ + int etna_int_mask; + + etna_int_mask = etna_readb(ETNA_INT_MASK); + while (etna_int_mask & (1 << 2)) { + printk("ETNA: Bad ETNA_INT_MASK: 0x%02x\n", + etna_readb(ETNA_INT_STATUS)); + etna_writeb(0, ETNA_INT_MASK); + etna_writeb(ETNA_CF_IRQ, ETNA_INT_CLEAR); + etna_int_mask = etna_readb(ETNA_INT_MASK); + } + + if (irq_desc[irq].action == NULL) + irq_desc[irq].action = etna_action; + + /* Let's enable the Etna interrupt again */ + etna_writeb(ETNA_CF_IRQ, ETNA_INT_MASK); + unmask_irq_int(irq); +} +#endif /* CONFIG_PCMCIA_ETNA */ + +static void +mask_ack_irq_int(unsigned int irq) +{ + switch (irq) { + case IRQ_CSINT: + psionw_writel(1, COEOI); + printk("Received and cleared CSINT irq %d\n", irq); + break; + case IRQ_EINT2: + psionw_writel(1, E2EOI); + printk("Received and cleared EINT2 irq %d\n", irq); + break; + case IRQ_TC1OI: + psionw_writel(1, TC1EOI); /* Cleared here, handled in rtc */ + break; + case IRQ_TC2OI: + psionw_writel(1, TC2EOI); /* Cleared here, handled in timer */ + break; + case IRQ_RTCMI: + psionw_writel(1, RTCEOI); /* Cleared here, handled in rtc*/ + break; + case IRQ_TINT: + psionw_writel(1, TEOI); + printk("Received and cleared TINT irq %d\n", irq); + break; + } +} + +static void +unmask_irq_int(unsigned int irq) +{ + u32 intmr; + intmr = (1 << irq); + psionw_writel(intmr, INTENS); +} + +void __init +psionw_init_irq(void) +{ + unsigned int i; + + for (i = 0; i < NR_IRQS; i++) { + if (INT1_IRQS & (1 << i)) { + irq_desc[i].valid = 1; + irq_desc[i].probe_ok = 1; + irq_desc[i].mask_ack = (INT1_ACK_IRQS & (1 << i)) ? + mask_ack_irq_int : mask_irq_int; + irq_desc[i].mask = mask_irq_int; + irq_desc[i].unmask = unmask_irq_int; + } + } + + /* + * Disable interrupts + */ + psionw_writel(~0, INTENC); + + /* + * Clear down any pending interrupts + */ + psionw_writel(0, MCEOI); + psionw_writel(0, BLEOI); + psionw_writel(0, COEOI); + psionw_writel(0, TC1EOI); + psionw_writel(0, TC2EOI); + psionw_writel(0, RTCEOI); + psionw_writel(0, TEOI); + psionw_writel(0, UMSEOI); + psionw_writel(0, SSCR0); + +#ifdef CONFIG_PCMCIA_ETNA + irq_desc[IRQ_EINT1].mask_ack = mask_etna_irq; + irq_desc[IRQ_EINT1].unmask = unmask_etna_irq; + + /* Enable the ETNA interrupts */ + __raw_writeb(ETNA_CF_IRQ, ETNA_V_BASE + ETNA_INT_MASK); +#endif + + init_FIQ(); +} diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/mm.c linux-2.4.32/arch/arm/mach-psionw/mm.c --- linux-2.4.32.orig/arch/arm/mach-psionw/mm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/mm.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * linux/arch/arm/mach-psionw/mm.c + * + * Copyright (C) 2000 Tony Lindgren + * + * 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 + +#define HOLE 0x000 + +/* + * Logical Physical + */ +static struct map_desc psionw_io_desc[] __initdata = { + {PSIONW_VIRT_BASE, PSIONW_PHYS_BASE, 0x10000, DOMAIN_IO, 0, 1}, + {ETNA_V_BASE, ETNA_P_BASE, ETNA_SIZE, DOMAIN_IO, 0, 1}, + {CF1_V_BASE, CF1_P_BASE, CF_SIZE, DOMAIN_IO, 0, 1}, + {PSION_V_BR, PSION_P_BR, PSION_BR_SIZE, DOMAIN_IO, 0, 1}, + {PSION_V_BF, PSION_P_BF, PSION_BF_SIZE, DOMAIN_IO, 0, 1}, + LAST_DESC +}; + +void __init +psionw_map_io(void) +{ + iotable_init(psionw_io_desc); +} diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/psionw-arch.c linux-2.4.32/arch/arm/mach-psionw/psionw-arch.c --- linux-2.4.32.orig/arch/arm/mach-psionw/psionw-arch.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/psionw-arch.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,109 @@ +/* + * linux/arch/arm/mach-psionw/psionw.c + * + * Copyright (C) 2001 Tony Lindgren + * + * Based on the mach-sa1100 code, some portions of the code + * 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 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 + +extern void psionw_init_irq(void); +extern void psionw_map_io(void); + +#define SET_BANK(__nr,__start,__size) \ + mi->bank[__nr].start = (__start), \ + mi->bank[__nr].size = (__size), \ + mi->bank[__nr].node = (((unsigned)(__start) - PHYS_OFFSET) >> 27) + +static void __init +fixup_psionw(struct machine_desc *desc, struct param_struct *params, + char **cmdline, struct meminfo *mi) +{ + +#ifdef CONFIG_PSIONW_5MX + SET_BANK(0, 0xc0000000, 8 * 1024 * 1024); + SET_BANK(1, 0xc1000000, 8 * 1024 * 1024); + mi->nr_banks = 2; +#elif CONFIG_PSIONW_5MXPRO24MB + /* Chris Halls */ + SET_BANK(0, 0xc0000000, 8 * 1024 * 1024); + SET_BANK(1, 0xc1000000, 8 * 1024 * 1024); + SET_BANK(2, 0xd0000000, 4 * 1024 * 1024); + SET_BANK(3, 0xd0800000, 4 * 1024 * 1024); + mi->nr_banks = 4; +#elif CONFIG_PSIONW_5MXPRO32MB + /* Provided by Thilo Hille */ + SET_BANK(0, 0xc0000000, 8 * 1024 * 1024); + SET_BANK(1, 0xc1000000, 8 * 1024 * 1024); + SET_BANK(2, 0xd0000000, 8 * 1024 * 1024); + SET_BANK(3, 0xd1000000, 8 * 1024 * 1024); + mi->nr_banks = 4; +#elif CONFIG_PSIONW_REVO + /* This seems to be correct */ + SET_BANK(0, 0xc0000000, 4 * 1024 * 1024); + SET_BANK(1, 0xc0800000, 4 * 1024 * 1024); + mi->nr_banks = 2; +#elif CONFIG_PSIONW_REVOPLUS + /* Provided by Ilmar Kotte */ + SET_BANK(0, 0xc0000000, 4 * 1024 * 1024); + SET_BANK(1, 0xc0800000, 4 * 1024 * 1024); + SET_BANK(2, 0xd0000000, 4 * 1024 * 1024); + SET_BANK(3, 0xd0800000, 4 * 1024 * 1024); + mi->nr_banks = 4; +#else +#error Unknown machine type! +#endif + + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + + /* Merge the command line options from kernel and boot loader */ + strcat(*cmdline, " "); + strcat(*cmdline, params->commandline); + strcpy(params->commandline, *cmdline); +} + +MACHINE_START(PSIONW, "Psion Windermere NEC ARM-710T") +MAINTAINER("Tony Lindgren") +BOOT_MEM(0xc0000000, 0x80000000, 0xdf000000) + +/* + * VIDEO() does not poke a hole to bootmem. + * See arch/arm/arm/mm/init.c. + */ +VIDEO(0xc000d000, 0xc000d000 + (PSION_LCD_W * PSION_LCD_H * 4 / 8)) + +/* Must sync with TEXTADDR */ +BOOT_PARAMS(0xc0048000 - (128 * 1024)) + +FIXUP(fixup_psionw) +MAPIO(psionw_map_io) +INITIRQ(psionw_init_irq) +MACHINE_END diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/psionw-hardware.c linux-2.4.32/arch/arm/mach-psionw/psionw-hardware.c --- linux-2.4.32.orig/arch/arm/mach-psionw/psionw-hardware.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/psionw-hardware.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * linux/arch/arm/mach-psionw/hardware.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 + +/* + * Shut down all unnecessary hardware + */ +static int +psionw_hw_init(void) +{ + if (psionw_readb(PCDR) & PCDR_UART1) { + psionw_writeb(psionw_readb(PCDR) & ~PCDR_UART1, PCDR); + } + + if (psionw_readb(PCDR) & PCDR_UART2) { + //psionw_writeb(psionw_readb(PCDR) & ~PCDR_UART2, PCDR); + } + + return 0; +} + +__initcall(psionw_hw_init); diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/psionw-leds.c linux-2.4.32/arch/arm/mach-psionw/psionw-leds.c --- linux-2.4.32.orig/arch/arm/mach-psionw/psionw-leds.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/psionw-leds.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * linux/arch/arm/mach-psionw/leds.c + * + * Psionw LED control routines + * + * 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 + +static void psionw_leds_event(led_event_t ledevt) +{ + unsigned long flags; + u32 port; + + local_irq_save(flags); + switch(ledevt) { + case led_idle_start: + /* + * Turn off red front led when entering idle. + * The led then shows the current CPU load. + */ + port = psionw_readb(PDDR); + psionw_writeb(port & ~PDDR_SLED, PDDR); + break; + + case led_idle_end: + /* Turn on red front led when not idle*/ + port = psionw_readb(PDDR); + psionw_writeb(port | PDDR_SLED, PDDR); + break; + + case led_timer: + /* FIXME5MX Blink the green top led, does not work */ + port = psionw_readb(PCDR); + //psionw_writeb(port ^ PCDR_PLED, PCDR); + break; + + default: + break; + } + + local_irq_restore(flags); +} + +static int __init leds_init(void) +{ + leds_event = psionw_leds_event; + return 0; +} + +__initcall(leds_init); diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/psionw-power.c linux-2.4.32/arch/arm/mach-psionw/psionw-power.c --- linux-2.4.32.orig/arch/arm/mach-psionw/psionw-power.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/psionw-power.c 2006-03-19 22:07:54.000000000 +0000 @@ -0,0 +1,249 @@ +/* + * arch/arm/mach-psionw/psionw-power.c - Psion power managemenent + * + * Copyright (C) 2002 Tony Lindgren + * + * Contrast restore after sleep fix (C) 2002 by Simon Howard + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern void disable_irq(unsigned int irq); +extern void enable_irq(unsigned int irq); +extern void psionw_lcd_powerdown(int lock); +extern void psionw_lcd_powerup(int lock); +extern void etna_powerdown(int lock); +extern void etna_powerup(int lock); +extern void ssi5mx_powerdown(int lock); +extern void ssi5mx_powerup(int lock); + +static int psion_rtc_off; + +/* + * Stops the DC to DC converter + */ +void stop_pump(int lock) +{ + long flags = 0; + + if (lock) + save_flags_cli(flags); + + psionw_writel(PUMP_STOP_VAL,PUMPCON); + psionw_writeb(psionw_readb(PDDR) & ~PDDR_PUMP_PWR1, PDDR); + + if (lock) + restore_flags(flags); +} + +void stop_timer(int lock) +{ + unsigned int cfg; + unsigned long goto_sleep; + long flags = 0; + + if (lock) + save_flags_cli(flags); + + psion_rtc_off = xtime.tv_sec - RTCTIME; + + goto_sleep = jiffies; + do { + disable_irq(IRQ_TC2OI); + } while (time_after(jiffies, goto_sleep + HZ/50)); + cfg = psionw_readl(TC2CTRL); + cfg &= ~TC_ENABLE; + psionw_writel(cfg, TC2CTRL); + + if (lock) + restore_flags(flags); +} + +void start_timer(int lock) +{ + unsigned int cfg; + long flags = 0; + + if (lock) + save_flags_cli(flags); + + cfg = psionw_readl(TC2CTRL); + + /* These bits need to initialized to 0 */ + cfg &= ~TC_BIT2; + cfg &= ~TC_BIT4; + cfg &= ~TC_BIT5; + cfg |= TC_CLKSEL; // 512kHz mode + cfg |= TC_MODE; + cfg |= TC_ENABLE; + + psionw_writel(cfg, TC2CTRL); + psionw_writel(5119, TC2LOAD); /* 512kHz / 100Hz - 1 */ + + psionw_writel(1, TC2EOI); + enable_irq(IRQ_TC2OI); + + if (lock) + restore_flags(flags); + + /* Set the correct time from RTC */ + xtime.tv_sec = RTCTIME; + xtime.tv_sec += psion_rtc_off; +} + +/* + * Puts the Psion into sleep mode until a keyboard, serial, + * touch panel, or RTC interrupt is received. + */ +void psion_off(void) +{ + int adc_clock = 0, rfdiv = 0, rtcdiv1 = 0, rtcdiv2 = 0; + int uart1 = 0, uart2 = 0; + long flags; + + if (psionw_readl(PDDR) & PDDR_PUMP_PWR1) { + + save_flags_cli(flags); + if (psionw_readb(PCDR) & PCDR_UART1) { + uart1 = 1; + //printk("Looks like UART1 is on\n"); + psionw_writeb(psionw_readb(PCDR) & ~PCDR_UART1, PCDR); + } + + if (psionw_readb(PCDR) & PCDR_UART2) { + uart2 = 1; + //printk("Looks like UART2 is on\n"); + psionw_writeb(psionw_readb(PCDR) & ~PCDR_UART2, PCDR); + } + restore_flags(flags); + + /* First we must stop the timer */ + stop_timer(1); + + /* + * We establish a long lock for the whole sleep-wake procedure + */ + save_flags_cli(flags); + + psionw_writeb(psionw_readb(PDDR) & ~PDDR_SLED, PDDR); + psionw_writel(0, KSCAN); + ssi5mx_powerdown(0); /* SSI, saves only about 0.1mA */ + +#ifdef CONFIG_PCMCIA_ETNA + etna_powerdown(0); +#endif + + psionw_lcd_powerdown(0); + stop_pump(0); /* DC to DC, saves about 0.5mA */ + + /* Lower the DRAM refresh to 1 KHz, no real savings */ + rfdiv = psionw_readl(DRAM_CFG) & 0x7f; + psionw_writel(psionw_readl(DRAM_CFG) | 0x7f, DRAM_CFG); + + /* Disable the ADC clock, no real savings */ + adc_clock = (psionw_readl(PWRCNT) >> 8) & 0xff; + psionw_writel(psionw_readl(PWRCNT) | (0xff << 8), PWRCNT); + + cpu_cache_clean_invalidate_all(); + + rtcdiv1 = psionw_readb(PWRSR) & 0x3f; + while(rtcdiv1 == rtcdiv2) { + rtcdiv2 = psionw_readb(PWRSR) & 0x3f; + } + while(rtcdiv2 == rtcdiv1) { + rtcdiv1 = psionw_readb(PWRSR) & 0x3f; + } + + /* Now go to sleep */ + psionw_writel(1, STBY); + __asm__ __volatile__(" \n\ + mov r0, r0 \n\ + mov r0, r0 \n\ + mov r0, r0 \n\ + "); + + /* We're back from sleep */ + + /* Restore the ADC clock */ + psionw_writel(psionw_readl(PWRCNT) & ~(0xff << 8), PWRCNT); + psionw_writel(psionw_readl(PWRCNT) | (adc_clock << 8), PWRCNT); + + /* Restore DRAM refresh rate */ + psionw_writel(psionw_readl(DRAM_CFG) & ~0x7f, DRAM_CFG); + psionw_writel(psionw_readl(DRAM_CFG) | rfdiv, DRAM_CFG); + + start_pump(0); + psionw_lcd_powerup(0); + + /* Restore contrast */ + psion_set_contrast(psion_get_contrast(), 1); + +#ifdef CONFIG_PCMCIA_ETNA + etna_powerup(0); +#endif + + ssi5mx_powerup(0); + + /* + * And finally we release the long lock + */ + restore_flags(flags); + start_timer(1); + + save_flags_cli(flags); + if (uart1) + psionw_writeb(psionw_readb(PCDR) | PCDR_UART1, PCDR); + + if (uart2) + psionw_writeb(psionw_readb(PCDR) | PCDR_UART2, PCDR); + restore_flags(flags); + + } else { + /* Just in case the LCD is off */ + psionw_lcd_powerup(1); + } +} + +/* + * Returns the CPU speed + */ +unsigned int psionw_get_cpu_speed(int cpu) +{ + int speed; + speed = (psionw_readl(PWRCNT) & 0x7) >> 2; + if (speed) { + return 36864000; + } else { + return 11432000; + } +} + +/* + * Changes the CPU speed + */ +void psionw_set_cpu_speed(int speed) +{ + int pwrcnt; + long flags = 0; + + save_flags_cli(flags); + cpu_cache_clean_invalidate_all(); + pwrcnt = psionw_readl(PWRCNT); + if (speed) { + pwrcnt |= (1 << 2); + printk("Setting the CPU speed to 36Mhz\n"); + psionw_writel(pwrcnt, PWRCNT); + } else { + pwrcnt &= ~(1 << 2); + printk("Setting the CPU speed to 18Mhz\n"); + psionw_writel(pwrcnt, PWRCNT); + } + restore_flags(flags); +} diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/psionw-time.c linux-2.4.32/arch/arm/mach-psionw/psionw-time.c --- linux-2.4.32.orig/arch/arm/mach-psionw/psionw-time.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/psionw-time.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,171 @@ +/* + * linux/arch/arm/mach-psionw/time.c + * + * RTC handler for Psion 5mx. + * + * Copyright (C) 2001 Yuji Shinokawa + * + * 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 + +#undef DEBUG_RTC + +/* + * Offset to compatible with EPOC time handling. + * In EPOC, it seems a time for rtc = 0 is "00:00:00 1 Jan 2000 UTC" + * and rtc has signed value. + * + * RTC Time & date + * 0x00000000 = 00:00:00 1 Jan 2000 (London Inner, United Kingdom) + * + * You can get this offset by following perl script; + * #!/usr/bin/perl + * require 'timelocal.pl'; + * $time = timegm (0, 0, 0, 1, 0, 2000); + * print "$time\n"; + */ +#define EPOC_RTC_OFFSET 946684800 + +extern int (*set_rtc) (void); +extern unsigned long (*gettimeoffset) (void); + +unsigned int psionw_read_rtc(void) +{ + int try_count, rtc1, rtc2; + u_int xrtc; + + try_count = 3; + do { + rtc1 = + (psionw_readl(RTCDRU) << 16) | (psionw_readl(RTCDRL) & + 0xffff); + rtc2 = + (psionw_readl(RTCDRU) << 16) | (psionw_readl(RTCDRL) & + 0xffff); + } while (rtc1 != rtc2 && --try_count); + +#if DEBUG_RTC + printk("DEBUG: (psionw_get_rtc) read rtc = %08x\n", rtc1); +#endif + + xrtc = rtc1 + EPOC_RTC_OFFSET; + + return xrtc; +} + +void psionw_write_rtc(u_int xrtc) +{ + int rtc; + + rtc = xrtc - EPOC_RTC_OFFSET; + +#if DEBUG_RTC + printk("DEBUG: (psionw_set_rtc) write rtc = %08x\n", rtc); +#endif + + /* Writing order is important. */ + psionw_writel((rtc >> 16), RTCDRU); + psionw_writel((rtc & 0xffff), RTCDRL); +} + +unsigned int psionw_read_rtc_alarm(void) +{ + int try_count, rtc1, rtc2; + u_int xrtc; + + try_count = 3; + do { + rtc1 = + (psionw_readl(RTCMRU) << 16) | (psionw_readl(RTCMRL) & + 0xffff); + rtc2 = + (psionw_readl(RTCMRU) << 16) | (psionw_readl(RTCMRL) & + 0xffff); + } while (rtc1 != rtc2 && --try_count); + +#if DEBUG_RTC + printk("DEBUG: (psionw_get_rtc_alarm) read rtc alarm = %08x\n", rtc1); +#endif + + xrtc = rtc1 + EPOC_RTC_OFFSET; + + return xrtc; +} + +void psionw_write_rtc_alarm(u_int xrtc) +{ + int rtc; + + rtc = xrtc - EPOC_RTC_OFFSET; + +#if DEBUG_RTC + printk("DEBUG: (psionw_set_rtc_alarm) write rtc alarm = %08x\n", rtc); +#endif + + /* Writing order is important. */ + psionw_writel((rtc >> 16), RTCMRU); + psionw_writel((rtc & 0xffff), RTCMRL); +} + +EXPORT_SYMBOL(psionw_read_rtc); +EXPORT_SYMBOL(psionw_write_rtc); +EXPORT_SYMBOL(psionw_read_rtc_alarm); +EXPORT_SYMBOL(psionw_write_rtc_alarm); + +static int +psionw_set_rtc(void) +{ + u_int xrtc, rtc; + + xrtc = (u_int) xtime.tv_sec; + + psionw_write_rtc(xrtc); + + rtc = psionw_read_rtc(); + if (xrtc > rtc || rtc - xrtc > 1) + return 1; /* Write failed. */ + + return 0; +} + +static unsigned long +psionw_gettimeoffset(void) +{ + return 0; +} + +static int +psionw_rtc_init(void) +{ + /* Set the initial RTC value. */ + xtime.tv_sec = (time_t) psionw_read_rtc(); + + /* Setup hook. */ + set_rtc = psionw_set_rtc; + gettimeoffset = psionw_gettimeoffset; + + return 0; +} + +__initcall(psionw_rtc_init); diff -Naur linux-2.4.32.orig/arch/arm/mach-psionw/serial-debug.c linux-2.4.32/arch/arm/mach-psionw/serial-debug.c --- linux-2.4.32.orig/arch/arm/mach-psionw/serial-debug.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/arch/arm/mach-psionw/serial-debug.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,35 @@ +#include +#include + +#define DEBUG 1 + +/* + * Allows printing debug information over the serial + * line even before the console is initialized. Uses + * the low level debug functions. + * To use in the code, just add: + * + * extern void serial_printf(char *fmt, ...); + * + * For debugging only, do not leave serial_printf + * statements in the code. + * + * Copied from the drivers/video/cyber2000fb.h + */ +#if defined(DEBUG) && defined(CONFIG_DEBUG_LL) +void +serial_printf(char *fmt, ...) +{ + extern void printascii(const char *); + char buffer[128]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + printascii(buffer); +} +#else +#define serial_printf(x...) do { } while (0) +#endif diff -Naur linux-2.4.32.orig/arch/arm/mm/init.c linux-2.4.32/arch/arm/mm/init.c --- linux-2.4.32.orig/arch/arm/mm/init.c 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/mm/init.c 2006-03-19 21:09:04.000000000 +0000 @@ -385,6 +385,13 @@ reserve_bootmem_node(pgdat, 0xc0000000, 0x00020000); if (machine_is_p720t()) reserve_bootmem_node(pgdat, PHYS_OFFSET, 0x00014000); + +#ifdef CONFIG_ARCH_PSIONW +#include + /* Reserve the video memory area */ + reserve_bootmem_node(pgdat, LCD_MEM_START, LCD_MEM_SIZE); +#endif + #ifdef CONFIG_SA1111 /* * Because of the SA1111 DMA bug, we want to preserve diff -Naur linux-2.4.32.orig/arch/arm/mm/proc-arm720.S linux-2.4.32/arch/arm/mm/proc-arm720.S --- linux-2.4.32.orig/arch/arm/mm/proc-arm720.S 2003-08-25 12:44:39.000000000 +0100 +++ linux-2.4.32/arch/arm/mm/proc-arm720.S 2006-03-19 21:09:04.000000000 +0000 @@ -447,7 +447,7 @@ cpu_arm720_name: - .asciz "ARM720T" + .asciz "ARM720T/710T" .align .section ".text.init", #alloc, #execinstr @@ -521,14 +521,16 @@ /* * See linux/include/asm-arm/procinfo.h for a definition of this structure. + * Supports both ARM720T and 710T processors. The cpu_val can be something + * like: 0x41807200 for 720T or 0x41807101 for 710T. What about 740T? */ .section ".proc.info", #alloc, #execinstr .type __arm720_proc_info, #object __arm720_proc_info: - .long 0x41807200 @ cpu_val - .long 0xffffff00 @ cpu_mask + .long 0x41807000 @ cpu_val 720T or 710T + .long 0xfffff000 @ cpu_mask .long 0x00000c1e @ section_mmu_flags b __arm720_setup @ cpu_flush .long cpu_arch_name @ arch_name diff -Naur linux-2.4.32.orig/drivers/Makefile linux-2.4.32/drivers/Makefile --- linux-2.4.32.orig/drivers/Makefile 2003-11-28 18:26:19.000000000 +0000 +++ linux-2.4.32/drivers/Makefile 2006-03-19 22:16:17.000000000 +0000 @@ -48,5 +48,6 @@ subdir-$(CONFIG_ACPI_BOOT) += acpi subdir-$(CONFIG_BLUEZ) += bluetooth +subdir-$(CONFIG_SSI) += ssi include $(TOPDIR)/Rules.make diff -Naur linux-2.4.32.orig/drivers/char/Config.in linux-2.4.32/drivers/char/Config.in --- linux-2.4.32.orig/drivers/char/Config.in 2004-08-08 00:26:04.000000000 +0100 +++ linux-2.4.32/drivers/char/Config.in 2006-03-19 21:13:23.000000000 +0000 @@ -168,6 +168,17 @@ if [ "$CONFIG_CPU_VR41XX" = "y" ]; then bool 'NEC VR4100 series Keyboard Interface Unit Support ' CONFIG_VR41XX_KIU fi +if [ "$CONFIG_ARCH_PSIONW" = "y" ]; then + mainmenu_option next_comment + comment 'Psion Windermere Keyboard Locale' + choice 'Psion-Windermere Keyboard Locale' \ + "Psion-UK-Keyboard CONFIG_PSION_KBD_UK \ + Psion-US-Keyboard CONFIG_PSION_KBD_US \ + Psion-DE-Keyboard CONFIG_PSION_KBD_DE \ + Psion-FR-Keyboard CONFIG_PSION_KBD_FR" CONFIG_PSION_KBD_UK + endmenu + bool '/proc/psionw support' CONFIG_PROCFS_PSION +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 @@ -227,6 +238,9 @@ comment ' metalab.unc.edu or ftp://titus.cfw.com/pub/Linux/util/' fi fi +if [ "$CONFIG_ARCH_PSIONW" = "y" ]; then + tristate 'Psion Windermere Real Time Clock' CONFIG_PSIONW_RTC +fi tristate 'IPMI top-level message handler' CONFIG_IPMI_HANDLER dep_mbool ' Generate a panic event to all BMCs on a panic' CONFIG_IPMI_PANIC_EVENT $CONFIG_IPMI_HANDLER diff -Naur linux-2.4.32.orig/drivers/char/Makefile linux-2.4.32/drivers/char/Makefile --- linux-2.4.32.orig/drivers/char/Makefile 2004-08-08 00:26:04.000000000 +0100 +++ linux-2.4.32/drivers/char/Makefile 2006-03-19 21:16:36.000000000 +0000 @@ -254,6 +254,7 @@ obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_MIPS_RTC) += mips_rtc.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o +obj-$(CONFIG_PSIONW_RTC) += psionw-rtc.o ifeq ($(CONFIG_PPC),) obj-$(CONFIG_NVRAM) += nvram.o endif @@ -334,6 +335,31 @@ obj-y += ipmi/ipmi.o endif +ifeq ($(CONFIG_ARCH_PSIONW),y) + KEYBD =keyboard_psion.o + CONSOLE =console.o + obj-y += keyboard_psion.o + ifeq ($(CONFIG_PSION_KBD_UK),y) + KEYMAP =keymap_psion.o + obj-y += keymap_psion.o + endif + ifeq ($(CONFIG_PSION_KBD_US),y) + KEYMAP =keymap_psion_us.o + obj-y += keymap_psion_us.o + endif + ifeq ($(CONFIG_PSION_KBD_DE),y) + KEYMAP =keymap_psion_de.o + obj-y += keymap_psion_de.o + endif + ifeq ($(CONFIG_PSION_KBD_FR),y) + KEYMAP =keymap_psion_fr.o + obj-y += keymap_psion_fr.o + endif + ifeq ($(CONFIG_PROCFS_PSION),y) + obj-y += psionw_procfs.o + endif +endif + include $(TOPDIR)/Rules.make fastdep: @@ -353,3 +379,16 @@ qtronixmap.c: qtronixmap.map set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ + +keymap_psion.c: keymap_psion.map + set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ + +keymap_psion_us.c: keymap_psion_us.map + set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ + +keymap_psion_de.c: keymap_psion_de.map + set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ + +keymap_psion_fr.c: keymap_psion_fr.map + set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ + diff -Naur linux-2.4.32.orig/drivers/char/console.c linux-2.4.32/drivers/char/console.c --- linux-2.4.32.orig/drivers/char/console.c 2005-01-19 14:09:44.000000000 +0000 +++ linux-2.4.32/drivers/char/console.c 2006-03-19 21:09:04.000000000 +0000 @@ -2442,7 +2442,14 @@ vc_cons[currcons].d->vc_palette[k++] = default_grn[j] ; vc_cons[currcons].d->vc_palette[k++] = default_blu[j] ; } + +#ifdef CONFIG_ARCH_PSIONW /* PSIONW_NONSTANDARD */ + /* setterm -foreground 9 should do, but doesn't */ + def_color = 0x0f; /* really white */ +#else def_color = 0x07; /* white */ +#endif + ulcolor = 0x0f; /* bold white */ halfcolor = 0x08; /* grey */ init_waitqueue_head(&vt_cons[currcons]->paste_wait); diff -Naur linux-2.4.32.orig/drivers/char/keyboard.c linux-2.4.32/drivers/char/keyboard.c --- linux-2.4.32.orig/drivers/char/keyboard.c 2003-11-28 18:26:20.000000000 +0000 +++ linux-2.4.32/drivers/char/keyboard.c 2006-03-19 21:09:04.000000000 +0000 @@ -105,17 +105,17 @@ static k_handfn do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_dead2, - do_ignore; + do_ignore, do_arch; /* PSIONW_NONSTANDARD */ static k_hand key_handler[16] = { do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_dead2, - do_ignore, do_ignore + do_ignore, do_arch }; /* Key types processed even in raw modes */ -#define TYPES_ALLOWED_IN_RAW_MODE ((1 << KT_SPEC) | (1 << KT_SHIFT)) +#define TYPES_ALLOWED_IN_RAW_MODE ((1 << KT_SPEC) | (1 << KT_CONS) | (1 << KT_SLOCK) | (1 << KT_SHIFT) | (1 << KT_ARCH1) | (1 << KT_ARCH2) | (1 << KT_ARCH3)) typedef void (*void_fnp)(void); typedef void (void_fn)(void); @@ -152,7 +152,7 @@ struct pt_regs * kbd_pt_regs; #ifdef CONFIG_MAGIC_SYSRQ -static int sysrq_pressed; +int sysrq_pressed; #endif static struct pm_dev *pm_kbd; @@ -311,8 +311,15 @@ if (type >= 0xf0) { type -= 0xf0; - if (raw_mode && ! (TYPES_ALLOWED_IN_RAW_MODE & (1 << type))) - goto out; + +#if 0 + printk("keysym=0x%x type=0x%x 0x%x && 0x%x\n", + keysym, type, raw_mode, !(TYPES_ALLOWED_IN_RAW_MODE & (1<slockstate = 0; } else { /* maybe only if (kbd->kbdmode == VC_UNICODE) ? */ @@ -536,6 +545,13 @@ compute_shiftstate(); } +static void do_arch(unsigned char value, char up_flag) +{ +#ifdef kbd_arch_handler + kbd_arch_handler(value,up_flag); +#endif +} + static void do_spec(unsigned char value, char up_flag) { if (up_flag) diff -Naur linux-2.4.32.orig/drivers/char/keyboard_psion.c linux-2.4.32/drivers/char/keyboard_psion.c --- linux-2.4.32.orig/drivers/char/keyboard_psion.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/keyboard_psion.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,337 @@ +/* + * arch/arm/drivers/char/keyboard_psion.c - Keyboard Driver for Psion + * + * Copyright (C) 1998 Roger Gammans + * Copyright (C) 2001 Tony Lindgren + * + * 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 "keyboard_psion.h" + +/* callback function for auto sleep */ +void (*psion_sleep_set_callback)(void); + +extern void psionw_lcd_powerdown(int lock); +extern void psionw_lcd_powerup(int lock); + +unsigned int kbd_delay[] = KB_DELAY; +static int keysdown = 0; +static struct tq_struct kbdhw_task; + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char kbdpsion_sysrq_xlate[] = + "\000" "6" "5" "4" "3" "2" "1" "\000" + "\000" "'" "\000" "0" "9" "8" "7" "\000" + "\000" "y" "t" "r" "e" "w" "q" "\000" + "\000" "\012" "l" "p" "o" "i" "u" "\000" + "\000" "g" "f" "d" "s" "a" "\011" "\000" + "\000" "\000" "." "m" "k" "j" "h" "\000" + "\000" "n" "b" "v" "c" "x" "z" "\000" + "\000" "\000" "\000" "," "\000" " " "\000"; +#endif + +void psion_cleartable(void) +{ + int key; + unsigned int t; + + for (key = 0; key < NR_KEYCODES; key++) { + if (!kbdstate[key].in) + continue; + + /* + * dont worry these ops are commutative and + * assoicative + */ + t = (jiffies - kbdstate[key].jif) & KB_JIFMASK; + if (t>KB_DEB_JIFFY) { + + /* Keboard could have changed meanwhile */ + kbdstate[key].in = 0; + keysdown--; + /* printk("Releasing key:%x\n",key); */ + handle_scancode(key, 0); + } + } +} + +static void kbd_press(int keycode) +{ + unsigned char newpress = 0; + + if (!kbdstate[keycode].in) { + keysdown++; + newpress = 1; + /* printk("Keycode: %d (0x%x)\n", keycode, keycode); */ + } + + /* + * We approximate a retriggable monostable + * action. + */ + kbdstate[keycode].in=1; + + /* We only need to ensure keysdown consistent */ + kbdstate[keycode].jif = jiffies & KB_JIFMASK; + if (newpress) { + handle_scancode(keycode, 1); + if (psion_sleep_set_callback != 0) + (*psion_sleep_set_callback)(); + } +} + +static void psion_tick(void* dummy) +{ + int col,row; + int rowd; + int count=0; + unsigned char state; + + /* + * Check if any keys are depressed + */ + for (col = 0; col < 8; col++) { + udelay(kbd_delay[8]); + state = psionw_readl(KSCAN); + state &= ~KBDSCAN; + state |= (KBSC_COL0 + col); + psionw_writel(state, KSCAN); + udelay(kbd_delay[col]); + rowd = psionw_readl(PADR) & 0xff; + if (!rowd) + continue; + for (row=0; row < KB_LASTROW; row++) { + if (rowd & KB_ROWMASK(row)) { + kbd_press(KEYCODE(row,col)); + count++; + } + } + }; + + if (count != keysdown) { + psion_cleartable(); + } + + /* + * Re-queue ourselves + */ + queue_task(&kbdhw_task,&tq_timer); +} + +void kbdpsion_hw_init(void) +{ + kbdhw_task.routine = psion_tick; + kbdhw_task.sync = 0; + queue_task(&kbdhw_task,&tq_timer); +} + +int kbdpsion_translate(unsigned char scancode, unsigned char *keycode_p) +{ + *keycode_p = scancode & ~(KBDOWN | KBUP); + return 1; +} + +#if defined(CONFIG_VT) && defined(CONFIG_MAGIC_SYSRQ) +extern int sysrq_pressed; +#endif + +void psion_arch_handler(unsigned char value, char up_flag) +{ +#if defined(CONFIG_VT) && defined(CONFIG_MAGIC_SYSRQ) + if (value == 1) { + sysrq_pressed = !up_flag; + return; + } +#endif + if (up_flag) return; + switch (value) { + +#ifdef KBD_ARCHKEY_2 + case 2: + KBD_ARCHKEY_2; + break; +#endif +#ifdef KBD_ARCHKEY_3 + case 3: + KBD_ARCHKEY_3; + break; +#endif +#ifdef KBD_ARCHKEY_4 + case 4: + KBD_ARCHKEY_4; + break; +#endif +#ifdef KBD_ARCHKEY_5 + case 5: + KBD_ARCHKEY_5; + break; +#endif +#ifdef KBD_ARCHKEY_6 + case 6: + KBD_ARCHKEY_6; + break; +#endif +#ifdef KBD_ARCHKEY_7 + case 7: + KBD_ARCHKEY_7; + break; +#endif +#ifdef KBD_ARCHKEY_8 + case 8: + KBD_ARCHKEY_8; + break; +#endif +#ifdef KBD_ARCHKEY_9 + case 9: + KBD_ARCHKEY_9; + break; +#endif +#ifdef KBD_ARCHKEY_10 + case 10: + KBD_ARCHKEY_10; + break; +#endif +#ifdef KBD_ARCHKEY_11 + case 11: + KBD_ARCHKEY_11; + break; +#endif + + default: + return; + } +} + +void psion_toggle_backlight() +{ + int pcdr; + long flags; + + if ((psionw_readl(LCDCTL) & LCDCTL_EN) == 0) + psionw_lcd_powerup(1); + + save_flags_cli(flags); + pcdr = psionw_readl(PCDR); + pcdr ^= PCDR_LIGHT; + psionw_writel(pcdr, PCDR); + restore_flags(flags); +} + +/* + * Contrast table. Upper four bits: output value, lower four bits: direction + * (0 = in = Z) + */ +#define V0 0 +#define V1 1 +#define VZ 0 +#define D0 1 +#define D1 1 +#define DZ 0 + +#define C(a,b,c,d) \ + ((V##a << 7) | (V##b << 6) | (V##c << 5) | (V##d << 4) | \ + (D##a << 3) | (D##b << 2) | (D##c << 1) | D##d) + +static __u8 contrast_table[] = { + C(1,1,1,1), C(1,1,1,Z), C(1,1,1,0), C(1,1,Z,1), /* 1- 4 */ + C(1,1,Z,Z), C(1,1,0,1), C(1,1,Z,0), C(1,Z,1,1), /* 5- 8 */ + C(1,1,0,0), C(1,Z,1,Z), C(1,Z,1,0), C(1,Z,Z,1), /* 9-12 */ + C(1,0,1,1), C(1,Z,Z,Z), C(1,Z,0,1), C(1,0,1,Z), /* 13-16 */ + C(1,0,1,0), C(1,0,Z,1), C(1,Z,0,0), C(Z,1,1,Z), /* 17-20 */ + C(1,0,Z,Z), C(1,0,0,1), C(1,0,Z,0), C(1,0,0,Z), /* 21-24 */ + C(Z,1,Z,Z), C(1,0,0,0), C(Z,1,Z,0), C(Z,1,0,Z), /* 25-28 */ + C(Z,1,0,0), C(Z,Z,1,Z), C(Z,Z,1,0), C(Z,Z,Z,1), /* 29-32 */ + C(0,1,1,0), C(0,1,Z,1), C(Z,Z,0,1), C(Z,0,1,Z), /* 33-36 */ + C(0,1,Z,Z), C(Z,Z,0,Z), C(0,1,Z,0), C(Z,Z,0,0), /* 37-40 */ + C(0,1,0,Z), C(Z,0,Z,Z), C(0,1,0,0), C(Z,0,Z,0), /* 41-44 */ + C(0,Z,1,0), C(0,Z,Z,1), C(Z,0,0,0), C(0,0,1,1), /* 45-48 */ + C(0,Z,Z,Z), C(0,Z,0,1), C(0,Z,Z,0), C(0,0,1,0), /* 49-52 */ + C(0,0,Z,1), C(0,Z,0,0), C(0,0,Z,Z), C(0,0,0,1), /* 53-56 */ + C(0,0,Z,0), C(0,0,0,Z), C(0,0,0,0) /* 57-60 */ +}; + +static int contrast = 40; + +int psion_get_contrast(void) +{ + return contrast; +} + +int psion_set_contrast(int new_contrast, int lock) +{ + long flags = 0; + + if ((new_contrast >= 0) && (new_contrast < sizeof(contrast_table))) { + contrast = new_contrast; + } + else { + printk("Bad contrast value: %i, should be between 0 - %d\n", + new_contrast, sizeof(contrast_table) - 1); + return -EINVAL; + } + + if (lock) + save_flags_cli(flags); + + psionw_writeb((psionw_readb(PBDR) & ~PBDR_VLD_MASK) | + ((contrast_table[contrast] >> 4) << PBDR_VLD_SHIFT), + PBDR); + psionw_writeb((psionw_readb(PBDDR) & ~PBDR_VLD_MASK) | + ((contrast_table[contrast] & 0xf) << PBDR_VLD_SHIFT), + PBDDR); + + if (lock) + restore_flags(flags); + + return 0; +} + +void psion_contrast(int increase) +{ + + if (increase) + if (contrast == sizeof(contrast_table)-1) return; + else contrast++; + else if (!contrast) return; + else contrast--; + + psion_set_contrast(contrast, 1); +} + +void debug_gpio(void) +{ + printk("\nPADR=0x%02x PBDR=0x%02x PCDR=0x%02x PDDR=0x%02x PEDR=0x%02x ", + psionw_readb(PADR), + psionw_readb(PBDR), + psionw_readb(PCDR), + psionw_readb(PDDR), + psionw_readb(PEDR)); + + printk("PADDR=0x%02x PBDDR=0x%02x\n", + psionw_readb(PADDR), + psionw_readb(PBDDR)); +} diff -Naur linux-2.4.32.orig/drivers/char/keyboard_psion.h linux-2.4.32/drivers/char/keyboard_psion.h --- linux-2.4.32.orig/drivers/char/keyboard_psion.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/keyboard_psion.h 2006-03-19 21:46:57.000000000 +0000 @@ -0,0 +1,127 @@ +/* + * arch/arm/drivers/char/keyboard_psion.h + * + * Psion keyboard definitions. + * + * Copyright (C) 1998 Roger Gammans + * Copyright (C) 2000 Tony Lindgren + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_ARCH_PSION_H +#define __ASM_ARCH_PSION_H + +#include + +#define KBDSCAN 0x0000000f /* Keyboard scan */ +#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 /* Col 0 high, others high impedance */ +#define KBSC_COL1 0x9 /* Col 1 high, others high impedance */ +#define KBSC_COL2 0xa /* Col 2 high, others high impedance */ +#define KBSC_COL3 0xb /* Col 3 high, others high impedance */ +#define KBSC_COL4 0xc /* Col 4 high, others high impedance */ +#define KBSC_COL5 0xd /* Col 5 high, others high impedance */ +#define KBSC_COL6 0xe /* Col 6 high, others high impedance */ +#define KBSC_COL7 0xf /* Col 7 high, others high impedance */ + +#define KB_JIFMASK (127) + +/* + * Arm's keyboard is active high row select... + * (must try to remember) + */ +#define KB_ALLCOLS KBSC_HI +#define KB_DISCHARGE KBSC_LO +#define KB_LASTROW 7 + +#define KBUP (0x80) +#define KBDOWN (0) + +/* + * Later on we'll try a struct of + * two arrays then the existing keyboard.c can + * test our array directly but until then.... + */ +typedef struct { + int in:1; /* If the key down */ + int jif:7; /* how long has key been down for (approx) */ +} kbd_keyinfo; + +static kbd_keyinfo kbdstate[NR_KEYCODES]; + +#ifdef KB_DELAY +extern unsigned int kbd_delay[]; +#endif + +/* + * Be sure to change the if you increase the + * number of kbd rows... + */ +#define KEYCODE(r,c) ( ((c)<<3) + (r)+1) +#define KB_ROWMASK(r) (1 << (r)) + +/* + * KB_DELAY is used to allow the matrix + * to stabilize.., value is determined via + * experimentation. + */ + +#define KB_DELAY {16 ,16 ,16 ,16 ,16 ,16 ,16 ,16 ,16} + +/* + * Not sure whether this trick works yet! + * + */ +#undef KBD_SUPPORTS_WIREOR + +/* + * treat debounce monostable as 30ms + */ + +#define KB_DEB_JIFFY (30/HZ) + +extern void psion_off(void); +void psion_toggle_backlight(void); +void psion_contrast(int increase); +void psion_debug_cf(void); + +/* + * Architecture-specific keys. Value 0 is invalid. 1 is SysRq. + * Ctrl-Alt = Ctrl-Menu on Psion, For example: SysRq = Ctrl-Menu-, + */ +#define KBD_ARCHKEY_2 psion_off() /* Fn-Off */ +#define KBD_ARCHKEY_3 psion_toggle_backlight() /* Fn-Space */ +#define KBD_ARCHKEY_4 psion_contrast(0) /* Fn-- */ +#define KBD_ARCHKEY_5 psion_contrast(1) /* Fn-+ */ + +/* KBD_ARCHKEY_6 to KBD_ARCHKEY_11 reserved for debugging */ +#define KBD_ARCHKEY_6 0 /* Ctrl-Menu-z */ + +#ifdef CONFIG_PCMCIA_ETNA +extern void psion_debug_etna(void); +#define KBD_ARCHKEY_7 psion_debug_etna() /* Ctrl-Menu-x */ +#else +#define KBD_ARCHKEY_7 debug_gpio() /* Ctrl-Menu-x */ +#endif + +#define KBD_ARCHKEY_8 0 /* Ctrl-Menu-c */ +#define KBD_ARCHKEY_9 0 /* Ctrl-Menu-v */ +#define KBD_ARCHKEY_8 0 +#define KBD_ARCHKEY_9 0 + +#endif diff -Naur linux-2.4.32.orig/drivers/char/keymap_psion.map linux-2.4.32/drivers/char/keymap_psion.map --- linux-2.4.32.orig/drivers/char/keymap_psion.map 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/keymap_psion.map 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,234 @@ +# arch/arm/drivers/char/keymap_psion.map - UK (default) keyboard map +# +# Written 1998,1999 by Werner Almesberger +# +# based on keymap_geo.map, string and compose def's taken from +# drivers/char/defkeymap.map +# +# Special keys: +# F13 REC F14 STOP F15 PLAY +# F16 Contrast-- F17 Contrast++ F18 Backlight + +# Default kernel keymap. This uses 7 modifier combinations. +keymaps 0-2,4-5,8,12 + + keycode 1 = six asciicircum +alt keycode 1 = Console_6 +altgr keycode 1 = greater +control keycode 1 = Control_asciicircum + keycode 2 = five percent +alt keycode 2 = Console_5 +altgr keycode 2 = less + keycode 3 = four dollar +alt keycode 3 = Console_4 +altgr keycode 3 = at + keycode 4 = three sterling +alt keycode 4 = Console_3 +altgr keycode 4 = backslash +control keycode 4 = Control_backslash + keycode 5 = two quotedbl +alt keycode 5 = Console_2 +altgr keycode 5 = numbersign + keycode 6 = one exclam +alt keycode 6 = Console_1 +altgr keycode 6 = underscore +control keycode 6 = Control_underscore + keycode 7 = F13 # REC + + keycode 8 = VoidSymbol + keycode 9 = apostrophe asciitilde +altgr keycode 9 = colon + keycode 10 = Delete +alt keycode 10 = Remove +altgr keycode 10 = grave +control alt keycode 10 = Boot + keycode 11 = zero parenright +alt keycode 11 = Console_10 +altgr keycode 11 = braceright + keycode 12 = nine parenleft +alt keycode 12 = Console_9 +altgr keycode 12 = braceleft + keycode 13 = eight asterisk +alt keycode 13 = Console_8 +altgr keycode 13 = bracketright +control keycode 13 = Control_bracketright + keycode 14 = seven ampersand +alt keycode 14 = Console_7 +altgr keycode 14 = bracketleft +control keycode 14 = Escape + keycode 15 = F15 # PLAY + + keycode 16 = VoidSymbol + keycode 17 = y +altgr keycode 17 = KP_Multiply + keycode 18 = t +altgr keycode 18 = bar + keycode 19 = r + keycode 20 = e + keycode 21 = w + keycode 22 = q + keycode 23 = Escape Escape +alt keycode 23 = Meta_Escape +altgr keycode 23 = 0xf02 # OFF + + keycode 24 = VoidSymbol + keycode 25 = Return + keycode 26 = l +altgr keycode 26 = semicolon + keycode 27 = p +altgr keycode 27 = equal + keycode 28 = o +altgr keycode 28 = minus + keycode 29 = i +altgr keycode 29 = plus + keycode 30 = u +altgr keycode 30 = KP_Divide + keycode 31 = SAlt # Menu + + keycode 32 = VoidSymbol + keycode 33 = g + keycode 34 = f + keycode 35 = d + keycode 36 = s + keycode 37 = a + keycode 38 = Tab +altgr keycode 38 = Caps_Lock + keycode 39 = SControl + + keycode 40 = VoidSymbol + keycode 41 = Down Scroll_Backward +altgr keycode 41 = Prior + keycode 42 = period question +altgr keycode 42 = 0xf05 # Contrast++ + keycode 43 = m +altgr keycode 43 = 0xf04 # Contrast-- + keycode 44 = k + keycode 45 = j + keycode 46 = h + keycode 47 = SAltGr # Fn + + keycode 48 = VoidSymbol + keycode 49 = n +control alt keycode 49 = 0xf0b # Debug 5 + keycode 50 = b +control alt keycode 50 = 0xf0a # Debug 4 + keycode 51 = v +control alt keycode 51 = 0xf09 # Debug 3 + keycode 52 = c +control alt keycode 52 = 0xf08 # Debug 2 + keycode 53 = x +control alt keycode 53 = 0xf07 # Debug 1 + keycode 54 = z +control alt keycode 54 = 0xf06 # Debug 0 + keycode 55 = SShift + + keycode 56 = VoidSymbol + keycode 57 = Right +altgr keycode 57 = Select # End + keycode 58 = Left +altgr keycode 58 = Find # Home + keycode 59 = comma slash +control alt keycode 59 = 0xf01 # SysRq + keycode 60 = Up Scroll_Forward +altgr keycode 60 = Next + keycode 61 = space +altgr keycode 61 = 0xf03 # Backlight + keycode 62 = F14 # STOP + keycode 63 = SShift + +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff -Naur linux-2.4.32.orig/drivers/char/keymap_psion_de.map linux-2.4.32/drivers/char/keymap_psion_de.map --- linux-2.4.32.orig/drivers/char/keymap_psion_de.map 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/keymap_psion_de.map 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,321 @@ +# arch/arm/drivers/char/keymap_psion_de.map +# +# Written 1999 by Ian E. Morgan +# +# largely based on keymap_psion.map by Werner Almesberger +# for UK keyboard layout +# +# based on keymap_geo.map, string and compose def's taken from +# drivers/char/defkeymap.map +# +# Special keys: +# F13 REC F14 STOP F15 PLAY +# F16 Contrast-- F17 Contrast++ F18 Backlight + + +# IEM: This is the mapping of US keyboard to keycodes: +# +# esc 1 2 3 4 5 6 7 8 9 0 del +# 23 6 5 4 3 2 1 14 13 12 11 10 +# +# q w e r t y u i o p enter +# 22 21 20 19 18 17 30 29 28 27 25 +# +# tab a s d f g h j k l : +# 38 37 36 35 34 33 46 45 44 26 9 +# +# shift z x c v b n m . up shift +# 55 54 53 52 51 50 49 43 42 60 63 +# +# ctrl fn menu space , left down right +# 39 47 31 61 59 58 41 47 + + +# Default kernel keymap. This uses 7 modifier combinations. +keymaps 0-2,4-5,8,12 + + keycode 1 = six ampersand +alt keycode 1 = Console_6 +altgr keycode 1 = greater + + keycode 2 = five percent +alt keycode 2 = Console_5 +altgr keycode 2 = less + + keycode 3 = four dollar +alt keycode 3 = Console_4 +altgr keycode 3 = asciitilde + + keycode 4 = three section +alt keycode 4 = Console_3 +altgr keycode 4 = backslash +control keycode 4 = Control_backslash + + keycode 5 = two quotedbl +alt keycode 5 = Console_2 +altgr keycode 5 = slash + + keycode 6 = one exclam +alt keycode 6 = Console_1 +altgr keycode 6 = underscore +control keycode 6 = Control_underscore + + keycode 7 = F13 # REC + + keycode 8 = VoidSymbol + + keycode 9 = numbersign asterisk +altgr keycode 9 = equal + + keycode 10 = Delete +alt keycode 10 = Remove +altgr keycode 10 = grave +control alt keycode 10 = Boot + + keycode 11 = zero apostrophe +alt keycode 11 = Console_10 +altgr keycode 11 = braceright + + keycode 12 = nine parenright +alt keycode 12 = Console_9 +altgr keycode 12 = braceleft + + keycode 13 = eight parenleft +alt keycode 13 = Console_8 +altgr keycode 13 = bracketright +control keycode 13 = Control_bracketright + + keycode 14 = seven question +alt keycode 14 = Console_7 +altgr keycode 14 = bracketleft +control keycode 14 = Escape + + keycode 15 = F15 # PLAY + + keycode 16 = VoidSymbol + + keycode 17 = z + + keycode 18 = t +altgr keycode 18 = bar + + keycode 19 = r +altgr keycode 19 = masculine + + keycode 20 = e +altgr keycode 20 = currency + + keycode 21 = w +altgr keycode 21 = asciicircum + + keycode 22 = q +altgr keycode 22 = at + + keycode 23 = Escape Escape +alt keycode 23 = Meta_Escape +altgr keycode 23 = 0xf02 # OFF + + keycode 24 = VoidSymbol + + keycode 25 = Return + + keycode 26 = l +altgr keycode 26 = minus + + keycode 27 = p +altgr keycode 27 = ssharp + + keycode 28 = o +altgr keycode 28 = odiaeresis + + keycode 29 = i +altgr keycode 29 = mu + + keycode 30 = u +altgr keycode 30 = udiaeresis + + keycode 31 = SAlt # Menu + + keycode 32 = VoidSymbol + + keycode 33 = g + + keycode 34 = f + + keycode 35 = d + + keycode 36 = s + + keycode 37 = a +altgr keycode 37 = adiaeresis + + keycode 38 = Tab +altgr keycode 38 = Caps_Lock + + keycode 39 = SControl + + keycode 40 = VoidSymbol + + keycode 41 = Down Scroll_Backward +altgr keycode 41 = Next + + keycode 42 = comma semicolon +altgr keycode 42 = 0xf05 # Contrast++ + + keycode 43 = m +altgr keycode 43 = 0xf04 # Contrast-- + + keycode 44 = k +altgr keycode 44 = plus + + keycode 45 = j +altgr keycode 45 = division + + keycode 46 = h +altgr keycode 46 = multiply + + keycode 47 = SAltGr # Fn + + keycode 48 = VoidSymbol + + keycode 49 = n +control alt keycode 49 = 0xf0b # Debug 5 + + keycode 50 = b +altgr keycode 50 = dead_caron +control alt keycode 50 = 0xf0a # Debug 4 + + keycode 51 = v +altgr keycode 51 = dead_breve +control alt keycode 51 = 0xf09 # Debug 3 + + keycode 52 = c +altgr keycode 52 = acute +control alt keycode 52 = 0xf08 # Debug 2 + + keycode 53 = x +altgr keycode 53 = grave +control alt keycode 53 = 0xf07 # Debug 1 + + keycode 54 = y +altgr keycode 54 = diaeresis +control alt keycode 54 = 0xf06 # Debug 0 + + keycode 55 = SShift + + keycode 56 = VoidSymbol + + keycode 57 = Right +altgr keycode 57 = Select # End + + keycode 58 = Left +altgr keycode 58 = Find # Home + + keycode 59 = period colon +control alt keycode 59 = 0xf01 # SysRq + + keycode 60 = Up Scroll_Forward +altgr keycode 60 = Prior + + keycode 61 = space +altgr keycode 61 = 0xf03 # Backlight + + keycode 62 = F14 # STOP + keycode 63 = SShift + +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff -Naur linux-2.4.32.orig/drivers/char/keymap_psion_fr.map linux-2.4.32/drivers/char/keymap_psion_fr.map --- linux-2.4.32.orig/drivers/char/keymap_psion_fr.map 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/keymap_psion_fr.map 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,274 @@ +keymaps 0-2,4-5,8,12 + + keycode 1 = parenright six dollar Control_bracketright +alt keycode 1 = Console_6 + + keycode 2 = parenleft five degree +alt keycode 2 = Console_5 + + keycode 3 = apostrophe four dead_tilde +alt keycode 3 = Console_4 + + keycode 4 = quotedbl three numbersign Escape +alt keycode 4 = Console_3 + + keycode 5 = eacute two percent +alt keycode 5 = Console_2 + + keycode 6 = ampersand one exclam +alt keycode 6 = Console_1 + + keycode 7 = F13 + + keycode 8 = VoidSymbol + + keycode 9 = m +AltGr keycode 9 = minus + + keycode 10 = Delete +alt keycode 10 = Remove +control alt keycode 10 = Boot + + keycode 11 = agrave zero at +alt keycode 11 = Console_10 + + keycode 12 = ccedilla nine dead_circumflex +alt keycode 12 = Console_9 + + keycode 13 = underscore eight backslash Control_backslash +alt keycode 13 = Console_8 + + keycode 14 = eacute seven sterling +alt keycode 14 = Console_7 + + keycode 15 = F15 + + keycode 16 = VoidSymbol + + keycode 17 = y +AltGr keycode 17 = braceright + + keycode 18 = t +AltGr keycode 18 = braceleft + + keycode 19 = r +AltGr keycode 19 = bracketright + + keycode 20 = e +AltGr keycode 20 = bracketleft + + keycode 21 = z +AltGr keycode 21 = greater + + keycode 22 = a +altgr keycode 22 = less + + keycode 23 = Escape Escape +alt keycode 23 = Meta_Escape +altgr keycode 23 = 0xf02 # OFF + + keycode 24 = VoidSymbol + + keycode 25 = Return + + keycode 26 = l +altgr keycode 26 = plus + + keycode 27 = p +altgr keycode 27 = equal + + keycode 28 = o +altgr keycode 28 = KP_Multiply + + keycode 29 = i +altgr keycode 29 = bar + + keycode 30 = u +altgr keycode 30 = ugrave + + keycode 31 = SAlt # Menu + + keycode 32 = VoidSymbol + + keycode 33 = g + + keycode 34 = f + + keycode 35 = d + + keycode 36 = s + + keycode 37 = q + + keycode 38 = Tab +altgr keycode 38 = Caps_Lock + + keycode 39 = SControl + + keycode 40 = VoidSymbol + + keycode 41 = Down Scroll_Backward +AltGr keycode 41 = Next + + keycode 42 = semicolon period +AltGr keycode 42 = slash + + keycode 43 = comma question +AltGr keycode 43 = KP_Multiply + + keycode 44 = k +AltGr keycode 44 = KP_Divide + + keycode 45 = j +altgr keycode 45 = 0xf05 # Contrast++ + + keycode 46 = h +altgr keycode 46 = 0xf04 # Contrast-- + + keycode 47 = SAltGr # Fn + + keycode 48 = VoidSymbol + + keycode 49 = n +control alt keycode 49 = 0xf0b # Debug 5 + + keycode 50 = b +AltGr keycode 50 = dead_circumflex +control alt keycode 50 = 0xf0a # Debug 4 + + keycode 51 = v +AltGr keycode 51 = dead_tilde +control alt keycode 51 = 0xf09 # Debug 3, ARCHKEY_9 + + keycode 52 = c +AltGr keycode 52 = apostrophe +control alt keycode 52 = 0xf08 # Debug 2, ARCHKEY_8 + + keycode 53 = x +AltGr keycode 53 = grave +control alt keycode 53 = 0xf07 # Debug 1, ARCHKEY_7 + + keycode 54 = w +AltGr keycode 54 = dead_diaeresis +control alt keycode 54 = 0xf06 # Debug 0, ARCHKEY_6 + + keycode 55 = SShift +altgr keycode 55 = slash + + keycode 56 = VoidSymbol + + keycode 57 = Right +altgr keycode 57 = Select + + keycode 58 = Left +altgr keycode 58 = Find + + keycode 59 = colon mu +control alt keycode 59 = 0xf01 # SysRq + + keycode 60 = Up Scroll_Forward +altgr keycode 60 = Prior + + keycode 61 = space +altgr keycode 61 = 0xf03 # Backlight + + keycode 62 = F14 + keycode 63 = SShift + + +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff -Naur linux-2.4.32.orig/drivers/char/keymap_psion_us.c linux-2.4.32/drivers/char/keymap_psion_us.c --- linux-2.4.32.orig/drivers/char/keymap_psion_us.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/keymap_psion_us.c 2006-03-19 21:46:56.000000000 +0000 @@ -0,0 +1,601 @@ + +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include +#include +#include + +u_short plain_map[] = { + 0xf200, 0xf036, 0xf035, 0xf034, 0xf033, 0xf032, 0xf031, 0xf10c, + 0xf200, 0xf03a, 0xf07f, 0xf030, 0xf039, 0xf038, 0xf037, 0xf10e, + 0xf200, 0xfb79, 0xfb74, 0xfb72, 0xfb65, 0xfb77, 0xfb71, 0xf01b, + 0xf200, 0xf201, 0xfb6c, 0xfb70, 0xfb6f, 0xfb69, 0xfb75, 0xfc03, + 0xf200, 0xfb67, 0xfb66, 0xfb64, 0xfb73, 0xfb61, 0xf009, 0xfc02, + 0xf200, 0xf600, 0xf02e, 0xfb6d, 0xfb6b, 0xfb6a, 0xfb68, 0xfc01, + 0xf200, 0xfb6e, 0xfb62, 0xfb76, 0xfb63, 0xfb78, 0xfb7a, 0xfc00, + 0xf200, 0xf602, 0xf601, 0xf02c, 0xf603, 0xf020, 0xf10d, 0xfc00, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_map[] = { + 0xf200, 0xf05e, 0xf025, 0xf024, 0xf023, 0xf040, 0xf021, 0xf10c, + 0xf200, 0xf022, 0xf07f, 0xf029, 0xf028, 0xf02a, 0xf026, 0xf10e, + 0xf200, 0xfb59, 0xfb54, 0xfb52, 0xfb45, 0xfb57, 0xfb51, 0xf01b, + 0xf200, 0xf201, 0xfb4c, 0xfb50, 0xfb4f, 0xfb49, 0xfb55, 0xfc03, + 0xf200, 0xfb47, 0xfb46, 0xfb44, 0xfb53, 0xfb41, 0xf009, 0xfc02, + 0xf200, 0xf20b, 0xf027, 0xfb4d, 0xfb4b, 0xfb4a, 0xfb48, 0xfc01, + 0xf200, 0xfb4e, 0xfb42, 0xfb56, 0xfb43, 0xfb58, 0xfb5a, 0xfc00, + 0xf200, 0xf602, 0xf601, 0xf03f, 0xf20a, 0xf020, 0xf10d, 0xfc00, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short altgr_map[] = { + 0xf200, 0xf03e, 0xf03c, 0xf02f, 0xf05c, 0xf07e, 0xf05f, 0xf10c, + 0xf200, 0xf03b, 0xf060, 0xf07d, 0xf07b, 0xf05d, 0xf05b, 0xf10e, + 0xf200, 0xf30c, 0xf07c, 0xfb72, 0xfb65, 0xfb77, 0xfb71, 0xf200, + 0xf200, 0xf201, 0xfb6c, 0xf03d, 0xf02d, 0xf02b, 0xf30d, 0xfc03, + 0xf200, 0xfb67, 0xfb66, 0xfb64, 0xfb73, 0xfb61, 0xf207, 0xfc02, + 0xf200, 0xf119, 0xf200, 0xfb6d, 0xfb6b, 0xfb6a, 0xfb68, 0xfc01, + 0xf200, 0xfb6e, 0xfb62, 0xfb76, 0xfb63, 0xfb78, 0xfb7a, 0xf02f, + 0xf200, 0xf117, 0xf114, 0xf200, 0xf118, 0xf020, 0xf10d, 0xfc00, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_map[] = { + 0xf200, 0xf01e, 0xf200, 0xf200, 0xf01c, 0xf200, 0xf01f, 0xf10c, + 0xf200, 0xf200, 0xf07f, 0xf200, 0xf200, 0xf01d, 0xf01b, 0xf10e, + 0xf200, 0xf019, 0xf014, 0xf012, 0xf005, 0xf017, 0xf011, 0xf200, + 0xf200, 0xf201, 0xf00c, 0xf010, 0xf00f, 0xf009, 0xf015, 0xfc03, + 0xf200, 0xf007, 0xf006, 0xf004, 0xf013, 0xf001, 0xf009, 0xfc02, + 0xf200, 0xf200, 0xf200, 0xf00d, 0xf00b, 0xf00a, 0xf008, 0xfc01, + 0xf200, 0xf00e, 0xf002, 0xf016, 0xf003, 0xf018, 0xf01a, 0xfc00, + 0xf200, 0xf602, 0xf601, 0xf200, 0xf200, 0xf020, 0xf10d, 0xfc00, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_ctrl_map[] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf10c, + 0xf200, 0xf200, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200, 0xf10e, + 0xf200, 0xf019, 0xf014, 0xf012, 0xf005, 0xf017, 0xf011, 0xf200, + 0xf200, 0xf201, 0xf00c, 0xf010, 0xf00f, 0xf009, 0xf015, 0xfc03, + 0xf200, 0xf007, 0xf006, 0xf004, 0xf013, 0xf001, 0xf009, 0xfc02, + 0xf200, 0xf200, 0xf200, 0xf00d, 0xf00b, 0xf00a, 0xf008, 0xfc01, + 0xf200, 0xf00e, 0xf002, 0xf016, 0xf003, 0xf018, 0xf01a, 0xfc00, + 0xf200, 0xf602, 0xf601, 0xf200, 0xf200, 0xf020, 0xf10d, 0xfc00, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short alt_map[] = { + 0xf200, 0xf505, 0xf504, 0xf503, 0xf502, 0xf501, 0xf500, 0xf10c, + 0xf200, 0xf200, 0xf116, 0xf509, 0xf508, 0xf507, 0xf506, 0xf10e, + 0xf200, 0xf879, 0xf874, 0xf872, 0xf865, 0xf877, 0xf871, 0xf81b, + 0xf200, 0xf201, 0xf86c, 0xf870, 0xf86f, 0xf869, 0xf875, 0xfc03, + 0xf200, 0xf867, 0xf866, 0xf864, 0xf873, 0xf861, 0xf009, 0xfc02, + 0xf200, 0xf200, 0xf200, 0xf86d, 0xf86b, 0xf86a, 0xf868, 0xfc01, + 0xf200, 0xf86e, 0xf862, 0xf876, 0xf863, 0xf878, 0xf87a, 0xfc00, + 0xf200, 0xf602, 0xf601, 0xf200, 0xf200, 0xf020, 0xf10d, 0xfc00, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_alt_map[] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf10c, + 0xf200, 0xf200, 0xf20c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf10e, + 0xf200, 0xf819, 0xf814, 0xf812, 0xf805, 0xf817, 0xf811, 0xf200, + 0xf200, 0xf201, 0xf80c, 0xf810, 0xf80f, 0xf809, 0xf815, 0xfc03, + 0xf200, 0xf807, 0xf806, 0xf804, 0xf813, 0xf801, 0xf009, 0xfc02, + 0xf200, 0xf200, 0xf200, 0xf80d, 0xf80b, 0xf80a, 0xf808, 0xfc01, + 0xf200, 0xf80e, 0xf802, 0xf816, 0xf803, 0xf818, 0xf81a, 0xfc00, + 0xf200, 0xf602, 0xf601, 0xf200, 0xf200, 0xf020, 0xf10d, 0xfc00, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 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', 'À'}, {'`', 'a', 'à'}, + {'\'', 'A', 'Á'}, {'\'', 'a', 'á'}, + {'^', 'A', 'Â'}, {'^', 'a', 'â'}, + {'~', 'A', 'Ã'}, {'~', 'a', 'ã'}, + {'"', 'A', 'Ä'}, {'"', 'a', 'ä'}, + {'O', 'A', 'Å'}, {'o', 'a', 'å'}, + {'0', 'A', 'Å'}, {'0', 'a', 'å'}, + {'A', 'A', 'Å'}, {'a', 'a', 'å'}, + {'A', 'E', 'Æ'}, {'a', 'e', 'æ'}, + {',', 'C', 'Ç'}, {',', 'c', 'ç'}, + {'`', 'E', 'È'}, {'`', 'e', 'è'}, + {'\'', 'E', 'É'}, {'\'', 'e', 'é'}, + {'^', 'E', 'Ê'}, {'^', 'e', 'ê'}, + {'"', 'E', 'Ë'}, {'"', 'e', 'ë'}, + {'`', 'I', 'Ì'}, {'`', 'i', 'ì'}, + {'\'', 'I', 'Í'}, {'\'', 'i', 'í'}, + {'^', 'I', 'Î'}, {'^', 'i', 'î'}, + {'"', 'I', 'Ï'}, {'"', 'i', 'ï'}, + {'-', 'D', 'Ð'}, {'-', 'd', 'ð'}, + {'~', 'N', 'Ñ'}, {'~', 'n', 'ñ'}, + {'`', 'O', 'Ò'}, {'`', 'o', 'ò'}, + {'\'', 'O', 'Ó'}, {'\'', 'o', 'ó'}, + {'^', 'O', 'Ô'}, {'^', 'o', 'ô'}, + {'~', 'O', 'Õ'}, {'~', 'o', 'õ'}, + {'"', 'O', 'Ö'}, {'"', 'o', 'ö'}, + {'/', 'O', 'Ø'}, {'/', 'o', 'ø'}, + {'`', 'U', 'Ù'}, {'`', 'u', 'ù'}, + {'\'', 'U', 'Ú'}, {'\'', 'u', 'ú'}, + {'^', 'U', 'Û'}, {'^', 'u', 'û'}, + {'"', 'U', 'Ü'}, {'"', 'u', 'ü'}, + {'\'', 'Y', 'Ý'}, {'\'', 'y', 'ý'}, + {'T', 'H', 'Þ'}, {'t', 'h', 'þ'}, + {'s', 's', 'ß'}, {'"', 'y', 'ÿ'}, + {'s', 'z', 'ß'}, {'i', 'j', 'ÿ'}, +}; + +unsigned int accent_table_size = 68; diff -Naur linux-2.4.32.orig/drivers/char/keymap_psion_us.map linux-2.4.32/drivers/char/keymap_psion_us.map --- linux-2.4.32.orig/drivers/char/keymap_psion_us.map 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/keymap_psion_us.map 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,311 @@ +# arch/arm/drivers/char/keymap_psion_us.map +# +# Written 1999 by Ian E. Morgan +# +# largely based on keymap_psion.map by Werner Almesberger +# for UK keyboard layout +# +# based on keymap_geo.map, string and compose def's taken from +# drivers/char/defkeymap.map +# +# Special keys: +# F13 REC F14 STOP F15 PLAY +# F16 Contrast-- F17 Contrast++ F18 Backlight + + +# IEM: This is the mapping of US keyboard to keycodes: +# +# esc 1 2 3 4 5 6 7 8 9 0 del +# 23 6 5 4 3 2 1 14 13 12 11 10 +# +# q w e r t y u i o p enter +# 22 21 20 19 18 17 30 29 28 27 25 +# +# tab a s d f g h j k l : +# 38 37 36 35 34 33 46 45 44 26 9 +# +# shift z x c v b n m . up shift +# 63 54 53 52 51 50 49 43 42 60 55 +# +# ctrl fn menu space , left down right +# 39 47 31 61 59 58 41 47 + + +# Default kernel keymap. This uses 7 modifier combinations. +keymaps 0-2,4-5,8,12 + + keycode 1 = six asciicircum +alt keycode 1 = Console_6 +altgr keycode 1 = greater +control keycode 1 = Control_asciicircum + + keycode 2 = five percent +alt keycode 2 = Console_5 +altgr keycode 2 = less + + keycode 3 = four dollar +alt keycode 3 = Console_4 +altgr keycode 3 = slash + + keycode 4 = three numbersign +alt keycode 4 = Console_3 +altgr keycode 4 = backslash +control keycode 4 = Control_backslash + + keycode 5 = two at +alt keycode 5 = Console_2 +altgr keycode 5 = asciitilde + + keycode 6 = one exclam +alt keycode 6 = Console_1 +altgr keycode 6 = underscore +control keycode 6 = Control_underscore + + keycode 7 = F13 # REC + + keycode 8 = VoidSymbol + + keycode 9 = colon quotedbl +altgr keycode 9 = semicolon + + keycode 10 = Delete +alt keycode 10 = Remove +altgr keycode 10 = grave +control alt keycode 10 = Boot + + keycode 11 = zero parenright +alt keycode 11 = Console_10 +altgr keycode 11 = braceright + + keycode 12 = nine parenleft +alt keycode 12 = Console_9 +altgr keycode 12 = braceleft + + keycode 13 = eight asterisk +alt keycode 13 = Console_8 +altgr keycode 13 = bracketright +control keycode 13 = Control_bracketright + + keycode 14 = seven ampersand +alt keycode 14 = Console_7 +altgr keycode 14 = bracketleft +control keycode 14 = Escape + + keycode 15 = F15 # PLAY + + keycode 16 = VoidSymbol + + keycode 17 = y +altgr keycode 17 = KP_Multiply + + keycode 18 = t +altgr keycode 18 = bar + + keycode 19 = r + + keycode 20 = e + + keycode 21 = w + + keycode 22 = q + + keycode 23 = Escape Escape +alt keycode 23 = Meta_Escape +altgr keycode 23 = 0xf02 # OFF + + keycode 24 = VoidSymbol + + keycode 25 = Return + + keycode 26 = l + + keycode 27 = p +altgr keycode 27 = equal + + keycode 28 = o +altgr keycode 28 = minus + + keycode 29 = i +altgr keycode 29 = plus + + keycode 30 = u +altgr keycode 30 = KP_Divide + + keycode 31 = SAlt # Menu + + keycode 32 = VoidSymbol + + keycode 33 = g + + keycode 34 = f + + keycode 35 = d + + keycode 36 = s + + keycode 37 = a + + keycode 38 = Tab +altgr keycode 38 = Caps_Lock + + keycode 39 = SControl + + keycode 40 = VoidSymbol + + keycode 41 = Down Scroll_Backward +altgr keycode 41 = Next + + keycode 42 = period apostrophe +altgr keycode 42 = 0xf05 # Contrast++ + + keycode 43 = m +altgr keycode 43 = 0xf04 # Contrast-- + + keycode 44 = k + + keycode 45 = j + + keycode 46 = h + + keycode 47 = SAltGr # Fn + + keycode 48 = VoidSymbol + + keycode 49 = n +control alt keycode 49 = 0xf0b # Debug 5 + + keycode 50 = b +control alt keycode 50 = 0xf0a # Debug 4 + + keycode 51 = v +control alt keycode 51 = 0xf09 # Debug 3, ARCHKEY_9 + + keycode 52 = c +control alt keycode 52 = 0xf08 # Debug 2, ARCHKEY_8 + + keycode 53 = x +control alt keycode 53 = 0xf07 # Debug 1, ARCHKEY_7 + + keycode 54 = z +control alt keycode 54 = 0xf06 # Debug 0, ARCHKEY_6 + + keycode 55 = SShift +altgr keycode 55 = slash + + keycode 56 = VoidSymbol + + keycode 57 = Right +altgr keycode 57 = Select # End + + keycode 58 = Left +altgr keycode 58 = Find # Home + + keycode 59 = comma question +control alt keycode 59 = 0xf01 # SysRq + + keycode 60 = Up Scroll_Forward +altgr keycode 60 = Prior + + keycode 61 = space +altgr keycode 61 = 0xf03 # Backlight + + keycode 62 = F14 # STOP + + keycode 63 = SShift + +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff -Naur linux-2.4.32.orig/drivers/char/psionw-rtc.c linux-2.4.32/drivers/char/psionw-rtc.c --- linux-2.4.32.orig/drivers/char/psionw-rtc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/psionw-rtc.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,445 @@ +/* + * Real Time Clock interface for Psion Windermere + * + * Copyright (c) 2002 Tony Lindgren + * + * Modified from the sa1100-rtc.c. + */ + +#include +#include +#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 int t, struct rtc_time *tval) +{ + unsigned int 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 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. + */ + 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 void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* update irq data & counter */ + rtc_irq_data |= (RTC_AF|RTC_IRQF); + rtc_irq_data += 0x100; + + /* wake up waiting process */ + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); +} + +static int rtc_set_timer(int frequency) { + unsigned int timer = 0; + unsigned int multiplier = 2000; + + if (frequency == 0) { + psionw_writel(0, TC1CTRL); + psionw_writel(0, TC1LOAD); + return 0; + } + + timer = psionw_readl(TC1CTRL); + timer |= TC_MODE; /* Periodic mode */ + + if (frequency > 1900) { + multiplier = 512000; + timer |= TC_CLKSEL; /* 512kHz mode */ + } else { + timer &= ~TC_CLKSEL; /* 2kHz mode */ + } + + timer |= TC_ENABLE; + psionw_writel(timer, TC1CTRL); + + psionw_writel((multiplier / frequency) - 1, TC1LOAD); + + return 0; +} + +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); + rtc_set_timer(0); + 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 0 + 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; + } + } +#endif + 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); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_AIE_ON: + spin_lock_irq(&rtc_lock); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_OFF: + spin_lock_irq(&rtc_lock); + rtc_set_timer(0); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_ON: + spin_lock_irq(&rtc_lock); + rtc_set_timer(1); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_OFF: + spin_lock_irq(&rtc_lock); + rtc_set_timer(0); + 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); + rtc_set_timer(rtc_freq); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_ALM_READ: + decodetime(psionw_read_rtc_alarm(), &tm); + break; + case RTC_ALM_SET: + if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2))) + return -EFAULT; + decodetime(psionw_read_rtc(), &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; + psionw_write_rtc_alarm(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(psionw_read_rtc(), &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; + psionw_write_rtc(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 psionwrtc_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(psionw_read_rtc(), &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(psionw_read_rtc_alarm(), &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 (&psionwrtc_miscdev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + + ret = request_irq (IRQ_TC1OI, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL); + if (ret) { + printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_TC1OI); + goto IRQ_TC1OI_failed; + } + + ret = request_irq (IRQ_RTCMI, rtc_interrupt, SA_INTERRUPT, "rtc alarm", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTCMI); + goto IRQ_RTCMI_failed; + } + + rtc_set_timer(0); + + printk (KERN_INFO "Psion Windermere Real Time Clock driver v" DRIVER_VERSION "\n"); + + return 0; + +IRQ_RTCMI_failed: + free_irq (IRQ_TC1OI, NULL); +IRQ_TC1OI_failed: + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&psionwrtc_miscdev); + return ret; +} + +static void __exit rtc_exit(void) +{ + rtc_set_timer(0); + free_irq (IRQ_TC1OI, NULL); + free_irq (IRQ_RTCMI, NULL); + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&psionwrtc_miscdev); +} + +module_init(rtc_init); +module_exit(rtc_exit); + +MODULE_AUTHOR("Tony Lindgren "); +MODULE_DESCRIPTION("Psion Windermere Realtime Clock Driver (RTC)"); +EXPORT_NO_SYMBOLS; diff -Naur linux-2.4.32.orig/drivers/char/psionw_procfs.c linux-2.4.32/drivers/char/psionw_procfs.c --- linux-2.4.32.orig/drivers/char/psionw_procfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/char/psionw_procfs.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,666 @@ +/* + * linux/drivers/chars/psionw_utils.c + * + * Psion procfs entries for manipulating backlight, contrast, power off + * and auto-sleep on Psion Windmere based Computers (Psion5mx(Pro), + * Revo(Plus), Mako). + * + * Copyright (C) 2002 Thilo Hille + * Vaclav Kulakovsky + * Tony Lindgren + * Simon Howard + * + * 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 <../drivers/pcmcia/psion_etna.h> + +#define MODULE_VERSION "0.2" +#define MODULE_NAME "procfs_psionw" +#define MODULE_DIR_NAME "psionw" +#define COREAD 3 + +extern void psion_off(void); +extern void psion_lcd_powerdown(int lock); +extern void psion_lcd_powerup(int lock); +extern int psion_get_contrast(void); +extern int psion_set_contrast(int new_contrast, int lock); +extern unsigned int psionw_get_cpu_speed(int cpu); +extern void psionw_set_cpu_speed(int speed); + +/* Psion sleep variables */ + +//#define PSLEEP_DEBUG 1 +#ifdef PSLEEP_DEBUG + #define DEBUG_PSLEEP(x...) printk(x) +#else + #define DEBUG_PSLEEP(x...) do { ; } while(0) +#endif + +#define PROC_SLEEP_MAX_LEN 8 + +static struct timer_list psion_sleep_timer; +static int psionblankinterval = 0; + +struct fb_data_t { + char name[PROC_SLEEP_MAX_LEN + 1]; + char value[PROC_SLEEP_MAX_LEN + 1]; +}; + +struct fb_data_t psion_sleep_data; + +static struct proc_dir_entry *psion_sleep_file; +extern void (*psion_sleep_set_callback)(void); +void psion_sleep_set(void); + +static int psionw_proc_lcdpower_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int psionw_proc_lcdpower_write(struct file *file, + const char *buffer, unsigned long count, void *data); + +static int psionw_proc_backlight_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int psionw_proc_backlight_write(struct file *file, + const char *buffer, unsigned long count, void *data); +static int psionw_proc_contrast_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int psionw_proc_contrast_write(struct file *file, + const char *buffer, unsigned long count, void *data); +static int psionw_proc_state_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int psionw_proc_state_write(struct file *file, + const char *buffer, unsigned long count, void *data); +static int psionw_proc_uart_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int psionw_proc_uart_write(struct file *file, + const char *buffer, unsigned long count, void *data); +static int psionw_proc_cpu_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int psionw_proc_cpu_write(struct file *file, + const char *buffer, unsigned long count, void *data); + +static struct proc_dir_entry *psionw_proc_dir, + *psionw_lcdpower_proc_entry, + *psionw_backlight_proc_entry, + *psionw_contrast_proc_entry, + *psionw_state_proc_entry, + *psionw_uart1_proc_entry, + *psionw_uart2_proc_entry, + *psionw_mains_proc_entry, + *psionw_case_proc_entry; + +struct uart_data_t { + short uart; +}; + +struct uart_data_t uart1_data, uart2_data; + +static int +psionw_proc_lcdpower_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return sprintf(page, "%d\n", + (psionw_readl(LCDCTL) & LCDCTL_EN)? 1 : 0 ); +} + +static int +psionw_proc_lcdpower_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned char char_value; + int value; + + if (count < 1) { + return -EINVAL; + } + + if (copy_from_user(&char_value, buffer, 1)) + return -EFAULT; + + value = char_value - '0'; + + if (value) { + psionw_lcd_powerup(1); + } else { + psionw_lcd_powerdown(1); + } + + return count; +} + +static int +psionw_proc_contrast_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return sprintf(page, "%i\n", psion_get_contrast()); +} + +static int damn_atoi(const char *name) +{ + int val = 0; + + for (;; name++) { + switch (*name) { + case '0'...'9': + val = 10*val+(*name-'0'); + break; + default: + return val; + } + } +} + +static int +psionw_proc_contrast_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char rbuffer[COREAD + 1]; + int new_contrast; + int len; + + if (count < 1) + return -EINVAL; + + if (count > COREAD){ + len = COREAD; + } + else { + len = count; + } + + if (copy_from_user(&rbuffer, buffer, len)) + return -EFAULT; + + rbuffer[len]='\0'; + new_contrast = damn_atoi(rbuffer); + + psion_set_contrast(new_contrast, 1); + + return len; +} + + +static int +psionw_proc_backlight_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return sprintf(page, "%d\n", + (psionw_readl(PCDR) & PCDR_LIGHT)? 1 : 0 ); +} + +static int +psionw_proc_backlight_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned char char_value; + int value; + int pcdr; + long flags; + + if (count < 1) { + return -EINVAL; + } + + if (copy_from_user(&char_value, buffer, 1)) + return -EFAULT; + + value = char_value - '0'; + + save_flags_cli(flags); + pcdr = psionw_readl(PCDR); + if (value) { + pcdr |= PCDR_LIGHT; + } else { + pcdr &= ~PCDR_LIGHT; + } + psionw_writel(pcdr, PCDR); + restore_flags(flags); + + return count; +} + +static int +psionw_proc_uart_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int psionw_proc_uart_state; + struct uart_data_t *uart_data = (struct uart_data_t *)data; + int pcdr; + + if (uart_data->uart == 1) { + pcdr = PCDR_UART1; + } + else if (uart_data->uart == 2) { + pcdr = PCDR_UART2; + } + else + return -EINVAL; + + psionw_proc_uart_state = (psionw_readb(PCDR) & pcdr); + + return sprintf(page, "%i\n",(psionw_proc_uart_state) ? 1 : 0); +} + +static int +psionw_proc_uart_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct uart_data_t *uart_data = (struct uart_data_t *)data; + unsigned char char_value; + int value; + int pcdr; + long flags; + + if (copy_from_user(&char_value, buffer, 1)) + return -EFAULT; + + value = char_value - '0'; + if (count < 1) { + return -EINVAL; + } + if (uart_data->uart==1){ + pcdr=PCDR_UART1; + } + else if (uart_data->uart==2){ + pcdr=PCDR_UART2; + } + else + return -EINVAL; + + save_flags_cli(flags); + if (value) { + printk("uart%i: powerup: \n",uart_data->uart); + psionw_writeb(psionw_readb(PCDR) | pcdr, PCDR); + } + else { + printk("uart%i: powerdown: \n",uart_data->uart); + psionw_writeb(psionw_readb(PCDR) & ~pcdr, PCDR); + } + restore_flags(flags); + + return count; +} + +static int +psionw_proc_state_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ +/* + * SlothAttack! + * If i had invented the refrigerator, their lights would remain on even with their door closed . + * We should output 0 if the machine is in sleepmode. But since noone will ever be able to read it + * it always sends 1 + */ + return sprintf(page, "1\n"); + +} + +static int +psionw_proc_state_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned char char_value; + int value; + int pcdr; + + if (count < 1) { + return -EINVAL; + } + + if (copy_from_user(&char_value, buffer, 1)) + return -EFAULT; + + value = char_value - '0'; + + pcdr = psionw_readl(PCDR); + if (!value){ + psion_off(); + } + + return count; +} + +static int +psionw_proc_cpu_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int speed = 0; + + speed = psionw_get_cpu_speed(0) / 100000; + return sprintf(page, "%d.%d\n", speed/10, speed%10); +} + +static int +psionw_proc_cpu_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned char char_value; + int value; + int pcdr; + + if (count < 1) { + return -EINVAL; + } + + if (copy_from_user(&char_value, buffer, 1)) + return -EFAULT; + + value = char_value - '0'; + + psionw_set_cpu_speed(value); + + return count; +} + +static void psion_sleep_timer_func(unsigned long dummy) +{ + psion_off(); + psion_sleep_set(); +} + +/* Called from keyboard_psion.c on a key press */ +void psion_sleep_set(void) +{ + /* modify timer*/ + psion_sleep_timer.function = psion_sleep_timer_func; + if (psionblankinterval) { + mod_timer(&psion_sleep_timer, jiffies + psionblankinterval); + } else { + del_timer(&psion_sleep_timer); + } +} + +static int psion_sleep_proc_read(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + int len; + struct fb_data_t *fb_data = (struct fb_data_t *)data; + + len = sprintf(page, "%s\n", fb_data->value); + return len; +} + +static int psion_sleep_proc_write(struct file *file, + const char *buffer, + unsigned long count, + void *data) +{ + int len, interval; + struct fb_data_t *fb_data = (struct fb_data_t *)data; + + if (count > PROC_SLEEP_MAX_LEN) len = PROC_SLEEP_MAX_LEN; + else len = count; + + if (copy_from_user(fb_data->value, buffer, len)) { + return 0; + } + fb_data->value[len] = '\0'; + + /* read new timeout */ + sscanf( fb_data->value, "%d", &interval ); + if( interval ) + if( interval < 10 ) { + interval = 10; + printk( "Minimal sleep inerval is 10 sec.\n" ); + } + snprintf(fb_data->value, PROC_SLEEP_MAX_LEN, "%d", interval); + fb_data->value[len] = '\0'; + + psionblankinterval = interval * HZ; + psion_sleep_set(); + DEBUG_PSLEEP("Psion sleep after %d s. \n", psionblankinterval/HZ ); + + return len; +} + + +static int psionw_proc_mains_read(char *page, char **start, + off_t off, int count, + int *eof, void *data) { + return sprintf(page, "%i\n", (psionw_readb(PWRSR) & DCDET) != 0); +} + +static int psionw_proc_mains_write(struct file *file, + const char *buffer, + unsigned long count, + void *data) { + /* i cant do that */ + return 0; +} + +static int psionw_proc_case_read(char *page, char **start, + off_t off, int count, + int *eof, void *data) { + + /* Make sure the data direction is set for read */ + if (psionw_readb(PBDDR) & PBDR_OPEN) { + long flags; + save_flags_cli(flags); + psionw_writeb(psionw_readb(PBDDR) & ~PBDR_OPEN, PBDDR); + restore_flags(flags); + } + + /* 0 on PBDR_OPEN = open, 1 = closed, invert this + (seems more intuitive this way) */ + return sprintf(page, "%i\n", (psionw_readb(PBDR) & PBDR_OPEN) == 0); +} + +static int psionw_proc_case_write(struct file *file, + const char *buffer, + unsigned long count, + void *data) { + /* i cant close the case for you! */ + return 0; +} + + +int __init psionwbl_init(void) +{ + int rv = 0; + + /* Create directory */ + psionw_proc_dir = proc_mkdir(MODULE_DIR_NAME, NULL); + if(psionw_proc_dir == NULL) { + printk("Couldn't create the procfs dir for psionw \n"); + rv = -ENOMEM; + goto out; + } + psionw_proc_dir->owner = THIS_MODULE; + + /* Register the /proc/psionw/lcd entry */ + psionw_lcdpower_proc_entry = create_proc_entry("lcd", 0444, + psionw_proc_dir); + if (psionw_lcdpower_proc_entry == NULL) { + printk("Couldn't create the procfs entry for lcd. \n"); + rv = -EINVAL; + goto out; + } + psionw_lcdpower_proc_entry->read_proc = + &psionw_proc_lcdpower_read; + psionw_lcdpower_proc_entry->write_proc = + &psionw_proc_lcdpower_write; + + /* Register the /proc/psionw/backlight entry. */ + psionw_backlight_proc_entry = create_proc_entry("backlight", 0444, + psionw_proc_dir); + if (psionw_backlight_proc_entry == NULL) { + printk("Couldn't create the procfs entry for backlight.\n"); + rv = -EINVAL; + goto out; + } + psionw_backlight_proc_entry->read_proc = + &psionw_proc_backlight_read; + psionw_backlight_proc_entry->write_proc = + &psionw_proc_backlight_write; + + /* Register the /proc/psionw/contrast entry. */ + psionw_contrast_proc_entry = create_proc_entry("contrast", 0444, + psionw_proc_dir); + if (psionw_contrast_proc_entry == NULL) { + printk("Couldn't create the procfs entry for the psionw-contrast.\n"); + rv = -EINVAL; + goto out; + } + + psionw_contrast_proc_entry->read_proc = + &psionw_proc_contrast_read; + psionw_contrast_proc_entry->write_proc = + &psionw_proc_contrast_write; + + /* Register the /proc/psionw/state entry. */ + psionw_state_proc_entry = create_proc_entry("state", 0444, + psionw_proc_dir); + if (psionw_state_proc_entry == NULL) { + printk("Couldn't create the procfs entry for the psionw-state.\n"); + rv = -EINVAL; + goto out; + } + psionw_state_proc_entry->read_proc = + &psionw_proc_state_read; + psionw_state_proc_entry->write_proc = + &psionw_proc_state_write; + + /* Register the /proc/psionw/cpu entry. */ + psionw_state_proc_entry = create_proc_entry("cpu", 0644, + psionw_proc_dir); + if (psionw_state_proc_entry == NULL) { + printk("Couldn't create the procfs entry for the psionw-cpu.\n"); + rv = -EINVAL; + goto out; + } + psionw_state_proc_entry->read_proc = + &psionw_proc_cpu_read; + psionw_state_proc_entry->write_proc = + &psionw_proc_cpu_write; + + /* Register the /proc/psionw/sleep entry. */ + psion_sleep_file = create_proc_entry("sleep", 0644, psionw_proc_dir ); + if( psion_sleep_file == NULL) { + printk("Couldn't create the procfs entry for the psionw-sleep.\n"); + } else { + strcpy(psion_sleep_data.name, "sleep"); + strcpy(psion_sleep_data.value, "0"); + psion_sleep_file->data = &psion_sleep_data; + psion_sleep_file->read_proc = psion_sleep_proc_read; + psion_sleep_file->write_proc = psion_sleep_proc_write; + psion_sleep_file->owner = THIS_MODULE; + } + + /* Init timer */ + psion_sleep_set(); + + /* Autosleep is initialised from keyboard_psion.c */ + psion_sleep_set_callback = psion_sleep_set; + DEBUG_PSLEEP("SLEEP: Callback registered.\n"); + + /* Register the /proc/psionw/uart entries for uart1 */ + psionw_uart1_proc_entry = create_proc_entry("uart1", 0444, + psionw_proc_dir); + if (psionw_uart1_proc_entry == NULL) { + printk("Couldn't create the procfs entry for the psionw-uart1.\n"); + rv = -EINVAL; + goto out; + } + uart1_data.uart = 1; + psionw_uart1_proc_entry->data = &uart1_data; + psionw_uart1_proc_entry->read_proc = + &psionw_proc_uart_read; + psionw_uart1_proc_entry->write_proc = + &psionw_proc_uart_write; + + /* Uart2 */ + psionw_uart2_proc_entry = create_proc_entry("uart2", 0444, + psionw_proc_dir); + if (psionw_uart2_proc_entry == NULL) { + printk("Couldn't create the procfs entry for the psionw-uart2.\n"); + rv = -EINVAL; + goto out; + } + uart2_data.uart = 2; + psionw_uart2_proc_entry->data = &uart2_data; + psionw_uart2_proc_entry->read_proc = + &psionw_proc_uart_read; + psionw_uart2_proc_entry->write_proc = + &psionw_proc_uart_write; + + psionw_mains_proc_entry = create_proc_entry("mains", 0444, + psionw_proc_dir); + + if (psionw_mains_proc_entry == NULL) { + printk("Couldn't create the procfs entry for mains\n"); + rv = -EINVAL; + goto out; + } else { + psionw_mains_proc_entry->read_proc = &psionw_proc_mains_read; + psionw_mains_proc_entry->write_proc = &psionw_proc_mains_write; + } + + psionw_case_proc_entry = create_proc_entry("case", 0444, + psionw_proc_dir); + + if (psionw_case_proc_entry == NULL) { + printk("Couldn't create the procfs entry for case\n"); + rv = -EINVAL; + goto out; + } else { + psionw_case_proc_entry->read_proc = &psionw_proc_case_read; + psionw_case_proc_entry->write_proc = &psionw_proc_case_write; + } + + return 0; + out: + return rv; +} + +static void __exit psionwbl_exit(void) +{ + del_timer(&psion_sleep_timer); + + /* Unregister sleep callback function */ + psion_sleep_set_callback = 0; + + remove_proc_entry("lcd", psionw_proc_dir); + remove_proc_entry("backlight", psionw_proc_dir); + remove_proc_entry("contrast", psionw_proc_dir); + remove_proc_entry("state", psionw_proc_dir); + remove_proc_entry("cpu", psionw_proc_dir); + remove_proc_entry("sleep", psionw_proc_dir); + remove_proc_entry("uart1", psionw_proc_dir); + remove_proc_entry("uart2", psionw_proc_dir); + remove_proc_entry("mains", psionw_proc_dir); + remove_proc_entry("case", psionw_proc_dir); + + remove_proc_entry(MODULE_NAME, NULL); +} + +module_init(psionwbl_init); +module_exit(psionwbl_exit); +MODULE_AUTHOR("Thilo Hille/Vaclac Kulakovsky"); +MODULE_DESCRIPTION("procfs utils for Psion5mx(Pro)/Revo(Plus)/Mako"); +EXPORT_NO_SYMBOLS; + diff -Naur linux-2.4.32.orig/drivers/pcmcia/Config.in linux-2.4.32/drivers/pcmcia/Config.in --- linux-2.4.32.orig/drivers/pcmcia/Config.in 2004-02-18 13:36:31.000000000 +0000 +++ linux-2.4.32/drivers/pcmcia/Config.in 2006-03-19 21:25:39.000000000 +0000 @@ -21,14 +21,13 @@ if [ "$CONFIG_HD64465" = "y" ]; then dep_tristate ' HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA 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 + dep_tristate ' Psion ETNA support' CONFIG_PCMCIA_ETNA $CONFIG_ARCH_PSIONW $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 diff -Naur linux-2.4.32.orig/drivers/pcmcia/Makefile linux-2.4.32/drivers/pcmcia/Makefile --- linux-2.4.32.orig/drivers/pcmcia/Makefile 2004-02-18 13:36:31.000000000 +0000 +++ linux-2.4.32/drivers/pcmcia/Makefile 2006-03-19 21:26:46.000000000 +0000 @@ -68,6 +68,7 @@ obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o obj-$(CONFIG_PCMCIA_SIBYTE) += sibyte_generic.o +obj-$(CONFIG_PCMCIA_ETNA) += psion_etna.o sa1100_cs-objs-y := sa1100_generic.o sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY) += sa1100_adsbitsy.o sa1111_generic.o diff -Naur linux-2.4.32.orig/drivers/pcmcia/psion_etna.c linux-2.4.32/drivers/pcmcia/psion_etna.c --- linux-2.4.32.orig/drivers/pcmcia/psion_etna.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/pcmcia/psion_etna.c 2006-03-19 21:09:04.000000000 +0000 @@ -0,0 +1,650 @@ +/* + * psion_etna.c - Psion-specific PCMCIA functions + * + * Copyright (C) 2001 Tony Lindgren + * Copyright (C) 2001 Shane Nay + * + * Contains code from the clps6700 driver for linux, + * Copyright (C) 2000 Deep Blue Solutions Ltd + * + * Contains code from the Psion 5 ETNA driver for linux, + * Copyright (C) 1999 Werner Almesberger. + * + * This driver is currently a big mess, so a rewrite is needed. + * But, it works, so we'll clean it up later. + * + * ETNA only has one interrupt that is shared with the card and + * the socket. The ETNA interrupt handler schedules a socket + * interrupt only if the interrupt is not a card interrupt. + * See irq.c on the ETNA interrupt handler. + * + * The code is currently all over the place. See also ide.h and + * irq.c. + * + * Psion is configured to trigger IRQ_MCINT with the CF card door + * switch. Epoc uses this, we don't. + * + * The various ETNA registers are not fully deciphered, mostly + * just working Epoc values are written to the registers without + * really knowing what they do. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include "psion_etna.h" + +MODULE_AUTHOR("Tony Lindgren"); +MODULE_DESCRIPTION("PSION ETNA PCMCIA socket driver"); + +#define NR_ETNA 1 + +static u8 cur_cfg = 0x1; /* Current card configuration */ +static u8 initialized; + +struct etna_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 etna_skt *skts[NR_ETNA]; + +static int etna_sock_init(u_int sock) +{ + struct etna_skt *skt = skts[sock]; + + skt->cur_cicr = 0; /* PC card interface config reg */ + skt->cur_pmr = skt->pmr; /* Power mgmt reg */ + skt->cur_pcimr = 0; /* PC card mask reg */ + skt->cur_cpcr = skt->cpcr; /* Card power ctrl reg */ + + DEBUG_ETNA("ETNA skt%d: sock_init()\n", sock); + return 0; +} + +static int etna_suspend(u_int sock) +{ + DEBUG_ETNA("ETNA: etna_suspend\n"); + etna_powerdown(1); + return 0; +} + +static int etna_register_callback(u_int sock, void (*handler)(void *, u_int), + void *info) +{ + struct etna_skt *skt = skts[sock]; + + DEBUG_ETNA("ETNA: skt%d: register_callback: %p (%p)\n", sock, handler, info); + skt->handler_info = info; + skt->handler = handler; + + return 0; +} + +static int etna_inquire_socket(u_int sock, socket_cap_t *cap) +{ + + DEBUG_ETNA("ETNA: etna_inquire_socket\n"); + +#if 0 + /* Leaving out SS_CAP_SATIC_MAP makes the card detection not to work*/ + cap->features = (SS_CAP_PAGE_REGS | SS_CAP_PCCARD | + SS_CAP_STATIC_MAP); +#endif + + cap->features = (SS_CAP_PAGE_REGS | SS_CAP_PCCARD | + SS_CAP_STATIC_MAP | SS_CAP_MEM_ALIGN); + + cap->irq_mask = 0; + cap->map_size = PAGE_SIZE; + cap->pci_irq = IRQ_EINT1; + cap->cb_dev = NULL; + cap->bus = NULL; + cap->io_offset = 0; + + return 0; +} + + +static int __etna_get_status(struct etna_skt *skt) +{ + unsigned int etna_state = 0; + int cardint, status, sktpower; + + DEBUG_ETNA("ETNA: __etna_get_status\n"); + + etna_state = (SS_DETECT | SS_READY); + + sktpower = etna_readb(ETNA_SKT_ACTIVE); + status = etna_readb(ETNA_SKT_STATUS); + + // Check if there is a card in the socket + if (status & SKT_CARD_OUT) { + DEBUG_ETNA("ETNA: Int: Card is not inserted: 0x%02x\n", status); + etna_state &= ~SS_DETECT; + etna_state &= ~SS_READY; + } + + // Check that the card has power if in socket + if (!sktpower) { + DEBUG_ETNA("ETNA: Int: No socket power\n"); + etna_state &= ~SS_READY; + etna_powerup(); + } + + // Test for socket ready + if ( (status & SKT_BUSY) || (status & SKT_NOT_READY) ) { + DEBUG_ETNA("ETNA: Int: Socket busy or not ready: 0x%02x\n", status); + etna_state &= ~SS_READY; + } + + /* Force some unknown values ready */ + etna_state &= ~SS_BATWARN; + etna_state &= ~SS_BATDEAD; + etna_state &= ~SS_XVCARD; + etna_state |= SS_3VCARD; + + // Clear pending status. What sets this on? + etna_state &= ~SS_PENDING; + + DEBUG_ETNA("ETNA: Status: skt%d: status: %08x -> %s %s %s %s %s %s)\n", + skt->nr, status, + etna_state & SS_READY ? "rdy" : "---", + etna_state & SS_DETECT ? "det" : "---", + etna_state & SS_BATWARN ? "bw" : "--", + etna_state & SS_BATDEAD ? "bd" : "--", + etna_state & SS_3VCARD ? "3v" : "--", + etna_state & SS_XVCARD ? "xv" : "--"); + + return etna_state; +} + +static int etna_get_status(u_int sock, u_int *valp) +{ + struct etna_skt *skt = skts[sock]; + *valp = __etna_get_status(skt); + return 0; /* not used! */ +} + +static int etna_get_socket(u_int sock, socket_state_t *state) +{ + DEBUG_ETNA("ETNA: etna_get_socket\n"); + return -EINVAL; +} + +static int etna_set_socket(u_int sock, socket_state_t *state) +{ + struct etna_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; + DEBUG_ETNA("ETNA: etna_set_socket with sock=%d\n", sock); + + if (state->flags & SS_RESET) { + DEBUG_ETNA("ETNA Socket: SS_RESET\n"); + } else if (state->flags & SS_OUTPUT_ENA) { + DEBUG_ETNA("ETNA Socket: SS_OUTPUT_ENA\n"); + } + + if (state->flags & SS_IOCARD) { + DEBUG_ETNA("ETNA Socket: SS_IOCARD\n"); + } else { + if (state->csc_mask & SS_BATDEAD) { + DEBUG_ETNA("ETNA Socket: SS_BATDEAD\n"); + pcimr = 0; + } + if (state->csc_mask & SS_BATWARN) { + DEBUG_ETNA("ETNA Socket: SS_BATWARN\n"); + pcimr = 0; + } + if (state->csc_mask & SS_READY) { + DEBUG_ETNA("ETNA Socket: SS_READY\n"); + pcimr = 0x41; + } + } + + if (state->csc_mask & SS_DETECT) { + DEBUG_ETNA("ETNA Socket: SS_DETECT\n"); + pcimr = 0x41; + } + + switch (state->Vcc) { + case 0: break; + case 33: cpcr |= skt->cpcr_3v3; break; + case 50: cpcr |= skt->cpcr_5v0; break; + default: return -EINVAL; + } + + DEBUG_ETNA("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' : '-'); + + 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); + } + + /* Enable card interrutps */ + if (skt->cur_pcimr != pcimr) { + skt->cur_pcimr = pcimr; + __raw_writeb(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 etna_get_io_map(u_int sock, struct pccard_io_map *io) +{ + return -EINVAL; +} + +static int etna_set_io_map(u_int sock, struct pccard_io_map *map) +{ + + unsigned long start; + + DEBUG_ETNA("ETNA: etna_set_io_map\n"); + + DEBUG_ETNA("ETNA: card speed: %u\n", map->speed); + + start=map->start; + if(map->stop==1) + map->stop=PAGE_SIZE-1; + + map->start = CF1_V_BASE; + map->stop=map->start+(map->stop-start); + + return 0; +} + +static int etna_get_mem_map(u_int sock, struct pccard_mem_map *mem) +{ + DEBUG_ETNA("ETNA: etna_get_mem_map\n"); + 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 etna_set_mem_map(u_int sock, struct pccard_mem_map *mem) +{ + struct etna_skt *skt = skts[sock]; + u_int off; + unsigned long start; + + start = mem->sys_start; + if(mem->sys_stop==0) + mem->sys_stop=PAGE_SIZE-1; + + // Only CF_ATTR_BASE 0x00000000 makes dump_cis work + if (mem->flags & MAP_ATTRIB) + off = CF_ATTR_BASE; + else + off = CF_MEM_BASE; /* This may not be right */ + + mem->sys_start = skt->physbase + off; + mem->sys_start += mem->card_start; + + ////mem->sys_stop=mem->sys_start+(mem->sys_stop-start); + + return 0; +} + +static void etna_proc_setup(u_int sock, struct proc_dir_entry *base) +{ +} + +static struct pccard_operations etna_operations = { + init: etna_sock_init, + suspend: etna_suspend, + register_callback: etna_register_callback, + inquire_socket: etna_inquire_socket, + get_status: etna_get_status, + get_socket: etna_get_socket, + set_socket: etna_set_socket, + get_io_map: etna_get_io_map, + set_io_map: etna_set_io_map, + get_mem_map: etna_get_mem_map, + set_mem_map: etna_set_mem_map, + proc_setup: etna_proc_setup +}; + +static void etna_bh(void *dummy) +{ + int i; + + DEBUG_ETNA("ETNA: etna_bh\n"); + for (i = 0; i < NR_ETNA; i++) { + struct etna_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 etna_task = { + routine: etna_bh +}; + +/* + * Handles card insert and eject. + * Scheduled from mask_etna_irq, as there seems to be only + * one interrupt for ETNA and the CF card. + */ +void etna_interrupt(void) +{ + struct etna_skt *skt = skts[0]; + int status, sktpower, cardpower; + int events; + + DEBUG_ETNA("ETNA: etna_interrupt\n"); + + /* We may get called from ide init */ + if (!initialized) { + return; + } + + events = __etna_get_status(skt); + + if ( (events & SS_DETECT) && (events & SS_READY) ) { + DEBUG_ETNA("ETNA: Int: Nothing to do, returning\n"); + return; + } + + spin_lock(&skt->ev_lock); + skt->ev_pending |= events; + spin_unlock(&skt->ev_lock); + schedule_task(&etna_task); +} + + +static int __init etna_init_skt(int nr) +{ + struct etna_skt *skt; + + DEBUG_ETNA("ETNA: etna_init_skt\n"); + skt = kmalloc(sizeof(struct etna_skt), GFP_KERNEL); + if (!skt) + return -ENOMEM; + + memset(skt, 0, sizeof(struct etna_skt)); + + spin_lock_init(&skt->ev_lock); + + skt->nr = nr; + //skt->physbase = nr ? 0x50000000 : 0x40000000; + skt->physbase = CF1_P_BASE; + DEBUG_ETNA("ETNA: skt->physbase=0x%x\n", skt->physbase); + + skt->cur_pmr = skt->pmr; + //skt->regbase = (u_int)__ioremap(skt->physbase + CF_REG_BASE, + // CF_REG_SIZE, 0); + skt->regbase = ETNA_V_BASE; + + if (!skt->regbase) + goto err_free; + + skts[nr] = skt; + return 0; + +err_unmap: + iounmap((void *)skt->regbase); +err_free: + kfree(skt); + skts[nr] = NULL; + return 1; +} + +static void etna_free_resources(void) +{ + int i; + + DEBUG_ETNA("ETNA: etna_free_resources\n"); + for (i = NR_ETNA; i >= 0; i--) { + struct etna_skt *skt = skts[i]; + skts[i] = NULL; + if (skt == NULL) + continue; + //free_irq(IRQ_EINT1, skt); /* Socket interrupt */ + if (skt->regbase) { + __raw_writeb(0, skt->regbase + PCIMR); /* Card int mask */ + + } + + //iounmap((void *)skt->regbase); /* Using ETNA_V_BASE for now */ + kfree(skt); + } +} + +static int __init etna_init(void) +{ + int err, nr; + + printk("ETNA: Initializing CF controller\n"); + + for (nr = 0; nr < NR_ETNA; nr++) { + err = etna_init_skt(nr); + if (err) + goto free; + } + + err = register_ss_entry(nr, &etna_operations); + if (err) + goto free; + + + initialized = 1; + etna_powerup(); + + return 0; + +free: + printk("ETNA: Failed to initialize\n"); + etna_free_resources(); + /* + * An error occurred. Unmap and free all ETNA + */ + return err; +} + +void etna_powerup(int lock) +{ + DEBUG_ETNA("ETNA: CompactFlash PCMCIA controller warm init\n"); + + /* Turn on the power if it is not already on */ + start_pump(lock); + mdelay(30); /* Needed for some cards */ + etna_init_hw(); + + /* Restore the card configuration */ + etna_set_cf(cur_cfg); + + /* Clear the ETNA interrupts */ + __raw_writeb(0x01, ETNA_V_BASE + ETNA_INT_CLEAR); + + /* Turn on ETNA interrupt */ + __raw_writeb(0x01, ETNA_V_BASE + ETNA_INT_MASK); + + //enable_irq(IRQ_EINT1); +} + +/* + * Powers down the card and ETNA. + * Some of these steps may not be necessary. + */ +void etna_powerdown(int lock) +{ + int cfg; + long flags; + + DEBUG_ETNA("ETNA: Powering down the CF controller\n"); + + if (lock) + save_flags_cli(flags); + + //disable_irq(IRQ_EINT1); + + /* Save the current card configuration */ + cur_cfg = CF1_READB(CF_CUR_CFG); + + /* Turn off card interrupts */ + __raw_writeb(0, ETNA_V_BASE + ETNA_INT_MASK); + + /* Clear card interrupts */ + __raw_writeb(0xff, ETNA_V_BASE + ETNA_INT_CLEAR); + + /* + * Writing this makes power up fail with garbage + * if something does not come up right. + */ + __raw_writeb(0, ETNA_V_BASE + ETNA_SKT_CTRL); + + __raw_writeb(0, ETNA_V_BASE + ETNA_SKT_ACTIVE); + __raw_writeb(0, ETNA_V_BASE + ETNA_SKT_CFG); + __raw_writeb(0, ETNA_V_BASE + ETNA_WAKE_1); + __raw_writeb(0, ETNA_V_BASE + ETNA_WAKE_2); + + /* Power down the CF card */ + cfg = psionw_readb(PBDR); + if (!(cfg & PBDR_VPCEN)) { + psionw_writeb(cfg | PBDR_VPCEN, PBDR); + } + + /* Delay is needed here */ + mdelay(20); + +#if 0 + /* Power down the ETNA socket */ + cfg = psionw_readb(PDDR); + if (cfg & PDDR_ETNA_PWR) { + //psionw_writeb(cfg & ~PDDR_ETNA_PWR, PDDR); + } +#endif + + if (lock) + restore_flags(flags); +} + +void psion_debug_etna(void) +{ + debug_gpio(); + + printk("ETNA: ATTR_BASE = 0x%08x ATTR_BASE + 4 = 0x%08x\n", + CF1_READL(0), + CF1_READL(4)); + + printk("ETNA: MEM_BASE = 0x%08x MEM_BASE + 4 = 0x%08x\n", + CF1_MEM_READL(0), + CF1_MEM_READL(4)); + + printk("ETNA: SKT_STATUS=0x%02x SKT_CFG=0x%02x SKT_CTRL=0x%02x SKT_ACTIVE=0x%02x\n", + __raw_readb(ETNA_V_BASE + ETNA_SKT_STATUS), + __raw_readb(ETNA_V_BASE + ETNA_SKT_CFG), + __raw_readb(ETNA_V_BASE + ETNA_SKT_CTRL), + __raw_readb(ETNA_V_BASE + ETNA_SKT_ACTIVE)); + + printk("ETNA: ETNA_INT_STATUS=0x%02x ETNA_INT_MASK=0x%02x\n", + __raw_readb(ETNA_V_BASE + ETNA_INT_STATUS), + __raw_readb(ETNA_V_BASE + ETNA_INT_MASK)); + + printk("CF1: 0x200=0x%02x 0x202=0x%02x 0x204=0x%02x\n", + CF1_READB(CF_CUR_CFG), + CF1_READB(0x202), + CF1_READB(0x204)); + DEBUG_CF1_REG("CFG_REG", CF_CFG_REG); + + printk("MEMCFG1: 0x%08x MEMCFG2=0x%08x\n", + psionw_readl(MEMCFG1), psionw_readl(MEMCFG2)); + + printk("CF1 READ TEST: 0x%x\n", + CF1_INB(0x3f6)); +} + +static void __exit etna_exit(void) +{ + unregister_ss_entry(&etna_operations); + etna_free_resources(); +} + +module_init(etna_init); +module_exit(etna_exit); diff -Naur linux-2.4.32.orig/drivers/pcmcia/psion_etna.h linux-2.4.32/drivers/pcmcia/psion_etna.h --- linux-2.4.32.orig/drivers/pcmcia/psion_etna.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/pcmcia/psion_etna.h 2006-03-19 21:55:50.000000000 +0000 @@ -0,0 +1,175 @@ +#include + +extern void debug_gpio(void); + +#define GET_ETNA_UNKNOWN_0 __raw_readb(ETNA_V_BASE + ETNA_UNKNOWN_0) +#define GET_ETNA_UNKNOWN_1 __raw_readb(ETNA_V_BASE + ETNA_UNKNOWN_1) +#define GET_ETNA_UNKNOWN_2 __raw_readb(ETNA_V_BASE + ETNA_UNKNOWN_2) +#define GET_ETNA_UNKNOWN_3 __raw_readb(ETNA_V_BASE + ETNA_UNKNOWN_3) +#define GET_ETNA_UNKNOWN_4 __raw_readb(ETNA_V_BASE + ETNA_UNKNOWN_4) +#define GET_ETNA_UNKNOWN_5 __raw_readb(ETNA_V_BASE + ETNA_UNKNOWN_5) +#define GET_ETNA_INT_STATUS __raw_readb(ETNA_V_BASE + ETNA_INT_STATUS) +#define GET_ETNA_INT_MASK __raw_readb(ETNA_V_BASE + ETNA_INT_MASK) +#define GET_ETNA_INT_CLEAR __raw_readb(ETNA_V_BASE + ETNA_INT_CLEAR) +#define GET_ETNA_SKT_STATUS __raw_readb(ETNA_V_BASE + ETNA_SKT_STATUS) +#define GET_ETNA_SKT_CFG __raw_readb(ETNA_V_BASE + ETNA_SKT_CFG) +#define GET_ETNA_SKT_WAKE1 __raw_readb(ETNA_V_BASE + ETNA_SKT_WAKE1) +#define GET_ETNA_SKT_ACTIVE __raw_readb(ETNA_V_BASE + ETNA_SKT_ACTIVE) +#define GET_ETNA_UNKNOWN_E __raw_readb(ETNA_V_BASE + ETNA_UNKNOWN_E) +#define GET_ETNA_SKT_WAKE2 __raw_readb(ETNA_V_BASE + ETNA_SKT_WAKE2) + +//#define ETNA_DEBUG 1 + +#ifdef ETNA_DEBUG +#define DEBUG_ETNA(x...) printk(x) + +#define DEBUG_ETNA_REG(desc, p) printk("ETNA: %s: Port 0x%08x = 0x%02x\n", \ + (desc), \ + skt->regbase + (p), \ + __raw_readb(skt->regbase + (p))) + +#define DEBUG_CF1_REG(desc, p) printk("CF: %s: Port 0x%08x = 0x%02x\n", \ + (desc), \ + CF1_V_BASE + CF_ATTR_BASE + (p), \ + __raw_readl(CF1_V_BASE + CF_ATTR_BASE + (p))) + +#define DEBUG_READ_TEST(desc) printk("CF: %s: Port 0x3f6 = 0x%02x\n", (desc), CF1_INB(0x3f6)) + +#else +#define DEBUG_ETNA(x...) do { ; } while(0) +#define DEBUG_ETNA_REG(desc,p) do { ; } while(0) +#define DEBUG_CF1_REG(desc,p) do { ; } while(0) +#define DEBUG_READ_TEST(desc) do { ; } while(0) +#endif + +#define CF1_READB(p) (*(volatile u8 *)(CF1_V_BASE + CF_ATTR_BASE + (p))) +#define CF1_WRITEB(v,p) (*(volatile u8 *)(CF1_V_BASE + CF_ATTR_BASE + (p)) = (v)) + +#define CF1_READL(p) __raw_readl(CF1_V_BASE + CF_ATTR_BASE + (p)) +#define CF1_WRITEL(v,p) __raw_writel((v), CF1_V_BASE + CF_ATTR_BASE + (p)) + +#define CF1_INB(p) (*(volatile uint8_t *)(CF1_V_BASE + CF_IO8_BASE + (p))) +#define CF1_INW(p) (*(volatile uint32_t *)(CF1_V_BASE + CF_IO16_BASE + (p)) & 0xffff) + +#define CF1_MEM_READB(p) (*(volatile u8 *)(CF1_V_BASE + CF_MEM_BASE + (p))) +#define CF1_MEM_READL(p) __raw_readl(CF1_V_BASE + CF_MEM_BASE + (p)) + +#define CF_DEF_CONFIG 0x1 /* Use the first one the CF card */ + +#define PCISR 0x06 /* PC card int status reg */ +#define PCIMR 0x07 /* PC card int mask reg */ +#define PCICR 0x08 /* PC card int clear reg */ + + +/* + * Sets up the ETNA hardware + */ +static __inline__ +void etna_init_hw(void) +{ + int cfg; + unsigned long flags; + + save_flags_cli(flags); + +#if defined(CONFIG_PSIONW_5MXPRO24MB) | defined(CONFIG_PSIONW_5MXPRO32MB) + /* Set the memory wait states to PCMCIA */ + // FIXME5MX: Should be checked... + psionw_writel(0x93930002, MEMCFG1); +#endif + + /* Clear the door switch interrupt (not in use) */ + psionw_writel(1, MCEOI); + + /* Power up the CF card */ + cfg = psionw_readb(PBDR); + DEBUG_ETNA("PBDR = 0x%x\n", cfg); + if (cfg & PBDR_VPCEN) { + DEBUG_ETNA("Clearing no CF power bit in PBDR\n"); + psionw_writeb(cfg & ~PBDR_VPCEN, PBDR); + } + + /* Wake up the socket */ + __raw_writeb(0x88, ETNA_V_BASE + ETNA_WAKE_1); + __raw_writeb(0x10, ETNA_V_BASE + ETNA_WAKE_2); + + /* Configure the ETNA socket */ + __raw_writeb(0x41, ETNA_V_BASE + ETNA_SKT_CTRL); + + /* Activate the socket */ + __raw_writeb(0x66, ETNA_V_BASE + ETNA_SKT_ACTIVE); + + restore_flags(flags); +} + +/* + * Sets the CF card configuration to selected value. + */ +static __inline__ +void etna_set_cf(int cf_config) +{ + int loops; + + //static int count = 0; + //count++; + //cpu_cache_clean_invalidate_all(); + //printk("XXX\n"); + again: + loops = 0; + + DEBUG_ETNA("ETNA: Restoring selected card configuration to: 0x%x\n", cf_config); + + while (CF1_READB(CF_CUR_CFG) != cf_config) { + loops++; + + /* Clear pending interrupts for both sockets at CCSR */ + CF1_WRITEB(0, 0x202); + + /* Restore the selected card configuration */ + CF1_WRITEB(cf_config, 0x200); + + mdelay(100); + + if (loops > 20) { + printk("ETNA: Unable to restore card configuration: 0x%02x != 0x%02x\n", + CF1_READB(CF_CUR_CFG), cf_config); + return; + } + } + + /* + * Did the configuration stay? + */ + while (CF1_READB(CF_CUR_CFG) != cf_config) { + //printk("XXX etna_cf_called total %d times\n", count); + printk("XXX Trying again\n"); + goto again; + } +} + +/* + * Initializes the ETNA hardware. Any previous card configuration + * is lost. Needed on 5mx Pro when booting without Epoc. Called + * from ide.h + */ +static __inline__ +void etna_cold_init(void) +{ + printk("ETNA: CompactFlash PCMCIA controller hard init\n"); + + /* Turn on the power if it is not already on */ + start_pump(1); + mdelay(30); /* Needed for some cards */ + etna_init_hw(); + + /* Set the card configuration */ + etna_set_cf(CF_DEF_CONFIG); + + /* Clear the ETNA interrupts */ + __raw_writeb(0x01, ETNA_V_BASE + ETNA_INT_CLEAR); + + /* Turn on ETNA interrupt */ + __raw_writeb(0x01, ETNA_V_BASE + ETNA_INT_MASK); + + //enable_irq(IRQ_EINT1); +} diff -Naur linux-2.4.32.orig/drivers/serial/21285.c linux-2.4.32/drivers/serial/21285.c --- linux-2.4.32.orig/drivers/serial/21285.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/21285.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,570 @@ +/* + * 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 2001/10/14 20:10:03 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) + +#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 + +#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 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) { + 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; + + 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"); + +#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; + + 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 int rs285_console_wait_key(struct console *co) +{ + int c; + + disable_irq(IRQ_CONRX); + while (*CSR_UARTFLG & 0x10); + c = *CSR_UARTDR; + enable_irq(IRQ_CONRX); + return c; +} + +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; +} + +#ifdef CONFIG_SERIAL_21285_OLD +static struct console rs285_old_cons = +{ + SERIAL_21285_OLD_NAME, + rs285_console_write, + NULL, + rs285_console_device, + rs285_console_wait_key, + 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, + wait_key: rs285_console_wait_key, + setup: rs285_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init rs285_console_init(void) +{ +#ifdef CONFIG_SERIAL_21285_OLD + if (!machine_is_ebsa285() && !machine_is_netwinder()) + register_console(&rs285_old_cons); +#endif + register_console(&rs285_cons); +} + +#endif /* CONFIG_SERIAL_21285_CONSOLE */ + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver"); diff -Naur linux-2.4.32.orig/drivers/serial/8250.c linux-2.4.32/drivers/serial/8250.c --- linux-2.4.32.orig/drivers/serial/8250.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/8250.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,1956 @@ +/* + * linux/drivers/char/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.6 2002/02/04 17:51:22 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 "serial_8250.h" + +/* + * 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_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS 1 +#endif +#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 + +#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]; +#ifdef CONFIG_SERIAL_8250_CONSOLE +static struct console serial8250_console; +static unsigned int lsr_break_flag; +#endif +static struct uart_info *IRQ_ports[NR_IRQS]; + +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; + +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif /* CONFIG_SERIAL_RSA */ + +#define port_acr unused[0] /* 8bit */ +#define port_ier unused[1] /* 8bit */ +#define port_rev unused[2] /* 8bit */ +#define port_lcr unused[3] /* 8bit */ + +/* + * 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_port *port, int offset) +{ + offset <<= port->regshift; + + switch (port->iotype) { +#ifdef CONFIG_SERIAL_8250_HUB6 + case SERIAL_IO_HUB6: + outb(port->hub6 - 1 + offset, port->iobase); + return inb(port->iobase + 1); +#endif + + case SERIAL_IO_MEM: + return readb((unsigned long)port->membase + offset); + + default: + return inb(port->iobase + offset); + } +} + +static _INLINE_ void +serial_out(struct uart_port *port, int offset, int value) +{ + offset <<= port->regshift; + + switch (port->iotype) { +#ifdef CONFIG_SERIAL_8250_HUB6 + case SERIAL_IO_HUB6: + outb(port->hub6 - 1 + offset, port->iobase); + outb(value, port->iobase + 1); + break; +#endif + + case SERIAL_IO_MEM: + writeb(value, (unsigned long)port->membase + offset); + break; + + default: + outb(value, 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(port, offset) serial_in(port, offset) +#define serial_outp(port, offset, value) serial_out(port, offset, value) + + +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_port *port, int offset, int value) +{ + serial_out(port, UART_SCR, offset); + serial_out(port, UART_ICR, value); +} + +static unsigned int serial_icr_read(struct uart_port *port, int offset) +{ + unsigned int value; + + serial_icr_write(port, UART_ACR, port->port_acr | UART_ACR_ICRRD); + serial_out(port, UART_SCR, offset); + value = serial_in(port, UART_ICR); + serial_icr_write(port, UART_ACR, port->port_acr); + + return value; +} + +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +static int enable_rsa(struct uart_port *port) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(port, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(port, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(port, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + restore_flags(flags); + return result; +} + +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +static int disable_rsa(struct uart_port *port) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(port, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(port, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(port, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + restore_flags(flags); + return result; +} +#endif /* CONFIG_SERIAL_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_port *port) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(port, UART_FCR); + old_mcr = serial_inp(port, UART_MCR); + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(port, UART_MCR, UART_MCR_LOOP); + serial_outp(port, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(port, UART_DLL); + old_dlm = serial_inp(port, UART_DLM); + serial_outp(port, UART_DLL, 0x01); + serial_outp(port, UART_DLM, 0x00); + serial_outp(port, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(port, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(port, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(port, UART_RX); + serial_outp(port, UART_FCR, old_fcr); + serial_outp(port, UART_MCR, old_mcr); + serial_outp(port, UART_LCR, UART_LCR_DLAB); + serial_outp(port, UART_DLL, old_dll); + serial_outp(port, UART_DLM, old_dlm); + + return count; +} + +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void +autoconfig_startech_uarts(struct uart_port *port) +{ + unsigned char scratch, scratch2, scratch3, scratch4; + + /* + * First we check to see if it's an Oxford Semiconductor UART. + * + * If we have to do this here because some non-National + * Semiconductor clone chips lock up if you try writing to the + * LSR register (which serial_icr_read does) + */ + if (port->type == PORT_16550A) { + /* + * 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). + */ + port->port_acr = 0; + serial_out(port, UART_LCR, 0xBF); + serial_out(port, UART_EFR, 0x10); + serial_out(port, UART_LCR, 0x00); + /* Check for Oxford Semiconductor 16C950 */ + scratch = serial_icr_read(port, UART_ID1); + scratch2 = serial_icr_read(port, UART_ID2); + scratch3 = serial_icr_read(port, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + port->type = PORT_16C950; + port->port_rev = serial_icr_read(port, UART_REV) | + (scratch3 << 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. + */ + + /* Save the DLL and DLM */ + + serial_outp(port, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(port, UART_DLL); + scratch4 = serial_inp(port, UART_DLM); + + serial_outp(port, UART_DLL, 0); + serial_outp(port, UART_DLM, 0); + scratch2 = serial_inp(port, UART_DLL); + scratch = serial_inp(port, UART_DLM); + serial_outp(port, UART_LCR, 0); + + if (scratch == 0x10 || scratch == 0x12 || scratch == 0x14) { + if (scratch == 0x10) + port->port_rev = scratch2; + port->type = PORT_16850; + return; + } + + /* Restore the DLL and DLM */ + + serial_outp(port, UART_LCR, UART_LCR_DLAB); + serial_outp(port, UART_DLL, scratch3); + serial_outp(port, UART_DLM, scratch4); + serial_outp(port, UART_LCR, 0); + + /* + * We distinguish between the '654 and the '650 by counting + * how many bytes are in the FIFO. I'm using this for now, + * since that's the technique that was sent to me in the + * serial driver update, but I'm not convinced this works. + * I've had problems doing this in the past. -TYT + */ + if (size_fifo(port) == 64) + port->type = PORT_16654; + else + port->type = PORT_16650V2; +} + +/* + * This routine is called by rs_init() to initialize a specific serial + * port. It determines what type of UART chip this serial port is + * using: 8250, 16450, 16550, 16550A. The important question is + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. + */ +static void autoconfig(struct uart_port *port, unsigned int probeflags) +{ + unsigned char status1, status2, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyS%d (0x%04x, 0x%08lx)...\n", + port->line, port->iobase, port->membase); +#endif + + if (!port->iobase && !port->membase) + return; + + save_flags(flags); cli(); + + if (!(port->flags & ASYNC_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. + */ + scratch = serial_inp(port, UART_IER); + serial_outp(port, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(port, UART_IER); + serial_outp(port, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(port, UART_IER); + serial_outp(port, UART_IER, scratch); + if (scratch2 || scratch3 != 0x0F) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: simple autoconfig failed " + "(%02x, %02x)\n", port->line, + scratch2, scratch3); +#endif + restore_flags(flags); + return; /* We failed; there's nothing here */ + } + } + + save_mcr = serial_in(port, UART_MCR); + save_lcr = serial_in(port, 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 (!(port->flags & ASYNC_SKIP_TEST)) { + serial_outp(port, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(port, UART_MSR) & 0xF0; + serial_outp(port, UART_MCR, save_mcr); + if (status1 != 0x90) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: no UART loopback failed\n", + port->line); +#endif + restore_flags(flags); + return; + } + } + serial_outp(port, UART_LCR, 0xBF); /* set up for StarTech test */ + serial_outp(port, UART_EFR, 0); /* EFR is the same as FCR */ + serial_outp(port, UART_LCR, 0); + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(port, UART_IIR) >> 6; + switch (scratch) { + case 0: + port->type = PORT_16450; + break; + case 1: + port->type = PORT_UNKNOWN; + break; + case 2: + port->type = PORT_16550; + break; + case 3: + port->type = PORT_16550A; + break; + } + if (port->type == PORT_16550A) { + /* Check for Startech UART's */ + serial_outp(port, UART_LCR, UART_LCR_DLAB); + if (serial_in(port, UART_EFR) == 0) { + port->type = PORT_16650; + } else { + serial_outp(port, UART_LCR, 0xBF); + if (serial_in(port, UART_EFR) == 0) + autoconfig_startech_uarts(port); + } + } + if (port->type == PORT_16550A) { + /* Check for TI 16750 */ + serial_outp(port, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(port, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(port, UART_IIR) >> 5; + if (scratch == 7) { + /* + * If this is a 16750, and not a cheap UART + * clone, then it should only go into 64 byte + * mode if the UART_FCR7_64BYTE bit was set + * while UART_LCR_DLAB was latched. + */ + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(port, UART_LCR, 0); + serial_outp(port, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(port, UART_IIR) >> 5; + if (scratch == 6) + port->type = PORT_16750; + } + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + } +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + /* + * Only probe for RSA ports if we got the region. + */ + if (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] != port->iobase) || + check_region(port->iobase + UART_RSA_BASE, 16)) && + (force_rsa[i] != port->iobase)) + continue; + if (!enable_rsa(port)) + continue; + port->type = PORT_RSA; + port->uartclk = SERIAL_RSA_BAUD_BASE * 16; + break; + } + } +#endif + serial_outp(port, UART_LCR, save_lcr); + if (port->type == PORT_16450) { + scratch = serial_in(port, UART_SCR); + serial_outp(port, UART_SCR, 0xa5); + status1 = serial_in(port, UART_SCR); + serial_outp(port, UART_SCR, 0x5a); + status2 = serial_in(port, UART_SCR); + serial_outp(port, UART_SCR, scratch); + + if ((status1 != 0xa5) || (status2 != 0x5a)) + port->type = PORT_8250; + } + port->fifosize = uart_config[port->type].dfl_xmit_fifo_size; + + if (port->type == PORT_UNKNOWN) { + restore_flags(flags); + return; + } + +#ifdef CONFIG_SERIAL_RSA + if (port->iobase && port->type == PORT_RSA) { + release_region(port->iobase, 8); + request_region(port->iobase + UART_RSA_BASE, 16, + "serial_rsa"); + } +#endif + + /* + * Reset the UART. + */ +#ifdef CONFIG_SERIAL_RSA + if (port->type == PORT_RSA) + serial_outp(port, UART_RSA_FRR, 0); +#endif + serial_outp(port, UART_MCR, save_mcr); + serial_outp(port, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(port, UART_FCR, 0); + (void)serial_in(port, UART_RX); + serial_outp(port, UART_IER, 0); + + restore_flags(flags); +} + +static void autoconfig_irq(struct uart_port *port) +{ + unsigned char save_mcr, save_ier; + unsigned long irqs; + int irq; + +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned char save_ICP = 0; + unsigned short ICP = 0; + + if (port->flags & ASYNC_FOURPORT) { + ICP = (port->iobase & 0xfe0) | 0x1f; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(port, UART_MCR); + save_ier = serial_inp(port, UART_IER); + serial_outp(port, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(port, UART_MCR, 0); + udelay (10); + if (port->flags & ASYNC_FOURPORT) { + serial_outp(port, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(port, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(port, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(port, UART_LSR); + (void)serial_inp(port, UART_RX); + (void)serial_inp(port, UART_IIR); + (void)serial_inp(port, UART_MSR); + serial_outp(port, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(port, UART_MCR, save_mcr); + serial_outp(port, UART_IER, save_ier); +#ifdef CONFIG_SERIAL_MANY_PORTS + if (port->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); +#endif + port->irq = (irq > 0)? irq : 0; +} + +static void serial8250_stop_tx(struct uart_port *port, u_int from_tty) +{ + if (port->port_ier & UART_IER_THRI) { + port->port_ier &= ~UART_IER_THRI; + serial_out(port, UART_IER, port->port_ier); + } + if (port->type == PORT_16C950) { + port->port_acr |= UART_ACR_TXDIS; + serial_icr_write(port, UART_ACR, port->port_acr); + } +} + +static void serial8250_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) +{ + if (nonempty && !(port->port_ier & UART_IER_THRI)) { + port->port_ier |= UART_IER_THRI; + serial_out(port, UART_IER, port->port_ier); + } + /* + * We only do this from uart_start + */ + if (from_tty && port->type == PORT_16C950) { + port->port_acr &= ~UART_ACR_TXDIS; + serial_icr_write(port, UART_ACR, port->port_acr); + } +} + +static void serial8250_stop_rx(struct uart_port *port) +{ + port->port_ier &= ~UART_IER_RLSI; + port->read_status_mask &= ~UART_LSR_DR; + serial_out(port, UART_IER, port->port_ier); +} + +static void serial8250_enable_ms(struct uart_port *port) +{ + port->port_ier |= UART_IER_MSI; + serial_out(port, UART_IER, port->port_ier); +} + +static _INLINE_ void +receive_chars(struct uart_info *info, int *status, struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + struct uart_port *port = info->port; + unsigned char ch; + int max_count = 256; + + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(port, UART_RX); + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + 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); + 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. + */ + uart_handle_break(info, &serial8250_console); + } else if (*status & UART_LSR_PE) + port->icount.parity++; + else if (*status & UART_LSR_FE) + port->icount.frame++; + if (*status & UART_LSR_OE) + port->icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + *status &= port->read_status_mask; + +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (port->line == serial8250_console.index) { + /* Recover the break flag from console xmit */ + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + if (uart_handle_sysrq_char(info, ch, regs)) + goto ignore_char; + if ((*status & 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(port, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static _INLINE_ void transmit_chars(struct uart_info *info, int *intr_done) +{ + struct uart_port *port = info->port; + int count; + + if (port->x_char) { + serial_outp(port, UART_TX, port->x_char); + port->icount.tx++; + port->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + serial8250_stop_tx(port, 0); + return; + } + + count = port->fifosize; + do { + serial_out(port, UART_TX, 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_event(info, EVT_WRITE_WAKEUP); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (intr_done) + *intr_done = 0; + + if (info->xmit.head == info->xmit.tail) + serial8250_stop_tx(info->port, 0); +} + +static _INLINE_ void check_modem_status(struct uart_info *info) +{ + struct uart_port *port = info->port; + int status; + + status = serial_in(port, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + if (status & UART_MSR_TERI) + port->icount.rng++; + if (status & UART_MSR_DDSR) + port->icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(info, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(info, status & UART_MSR_CTS); + + wake_up_interruptible(&info->delta_msr_wait); + } +} + +/* + * This handles the interrupt from one port. + */ +static inline void +serial8250_handle_port(struct uart_info *info, struct pt_regs *regs) +{ + int status = serial_inp(info->port, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + check_modem_status(info); + if (status & UART_LSR_THRE) + transmit_chars(info, 0); +} + +#ifdef CONFIG_SERIAL_8250_SHARE_IRQ +/* + * This is the serial driver's generic interrupt routine + */ +static void rs_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info, *end_mark = NULL; + int pass_counter = 0; +#ifdef CONFIG_SERIAL_8250_MULTIPORT + int first_multi = 0; + unsigned long port_monitor = rs_multiport[irq].port_monitor; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt(%d)...", irq); +#endif + + info = *(struct uart_info **)dev_id; + if (!info) + return; + +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (port_monitor) + first_multi = inb(port_monitor); +#endif + + do { + if (!info->tty || + (serial_in(info->port, UART_IIR) & UART_IIR_NO_INT)) { + if (!end_mark) + end_mark = info; + goto next; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info->port, UART_IIR)); +#endif + end_mark = NULL; + + serial8250_handle_port(info, regs); + + next: + info = info->next_info; + if (info) + continue; + info = *(struct uart_info **)dev_id; + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs loop break\n"); +#endif + break; /* Prevent infinite loops */ + } + } while (end_mark != info); +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (port_monitor) + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", + info->port->irq, first_multi, inb(port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +#endif + +/* + * This is the serial driver's interrupt routine for a single port + */ +static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info; + int pass_counter = 0; +#ifdef CONFIG_SERIAL_8250_MULTIPORT + int first_multi = 0; + unsigned long port_monitor = rs_multiport[irq].port_monitor; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = *(struct uart_info **)dev_id; + if (!info || !info->tty) + return; + +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (port_monitor) + first_multi = inb(port_monitor); +#endif + + do { + serial8250_handle_port(info, regs); + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs_single loop break.\n"); +#endif + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info->port, UART_IIR)); +#endif + } while (!(serial_in(info->port, UART_IIR) & UART_IIR_NO_INT)); +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (port_monitor) + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", + info->port->irq, first_multi, inb(port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +#ifdef CONFIG_SERIAL_8250_MULTIPORT +/* + * This is the serial driver's interrupt routine for multiport boards + */ +static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info; + int pass_counter = 0; + struct rs_multiport_struct *multi = &rs_multiport[irq]; + int first_multi = 0; + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_multi(%d)...", irq); +#endif + + info = *(struct uart_info **)dev_id; + if (!info) + return; + + if (!multi->port1) { + /* should never happen */ + printk("rs_interrupt_multi: port1 NULL!\n"); + return; + } + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); + + while (1) { + if (!info->tty || + (serial_in(info->port, UART_IIR) & UART_IIR_NO_INT)) + goto next; + + serial8250_handle_port(info, regs); + + next: + info = info->next; + if (info) + continue; + info = *(struct uart_info **)dev_id; + + /* + * The user was a bonehead, and misconfigured their + * multiport info. Rather than lock up the kernel + * in an infinite loop, if we loop too many times, + * print a message and break out of the loop. + */ + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("Misconfigured multiport serial info " + "for irq %d. Breaking out irq loop\n", irq); + break; + } + if (multi->port_monitor) + printk("rs port monitor irq %d: 0x%x, 0x%x\n", + info->port->irq, first_multi, + inb(multi->port_monitor)); + if ((inb(multi->port1) & multi->mask1) != multi->match1) + continue; + if (!multi->port2) + break; + if ((inb(multi->port2) & multi->mask2) != multi->match2) + continue; + if (!multi->port3) + break; + if ((inb(multi->port3) & multi->mask3) != multi->match3) + continue; + if (!multi->port4) + break; + if ((inb(multi->port4) & multi->mask4) != multi->match4) + continue; + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +#endif + +static u_int serial8250_tx_empty(struct uart_port *port) +{ + return serial_in(port, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +} + +static u_int serial8250_get_mctrl(struct uart_port *port) +{ + unsigned long flags; + unsigned char status; + unsigned int ret; + + save_flags(flags); cli(); + status = serial_in(port, UART_MSR); + restore_flags(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, u_int mctrl) +{ + 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; + + serial_out(port, UART_MCR, mcr); +} + +static void serial8250_break_ctl(struct uart_port *port, int break_state) +{ + if (break_state == -1) + port->port_lcr |= UART_LCR_SBC; + else + port->port_lcr &= ~UART_LCR_SBC; + serial_out(port, UART_LCR, port->port_lcr); +} + +static int serial8250_startup(struct uart_port *port, struct uart_info *info) +{ + void (*handler)(int, void *, struct pt_regs *); + unsigned long flags; + int retval; + + if (port->type == PORT_16C950) { + /* Wake up and initialize UART */ + port->port_acr = 0; + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, UART_EFR_ECB); + serial_outp(port, UART_IER, 0); + serial_outp(port, UART_LCR, 0); + serial_icr_write(port, UART_CSR, 0); /* Reset the UART */ + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, UART_EFR_ECB); + serial_outp(port, UART_LCR, 0); + } + +#ifdef CONFIG_SERIAL_RSA + /* + * If this is an RSA port, see if we can kick it up to the + * higher speed clock. + */ + if (port->type == PORT_RSA) { + if (port->uartclk != SERIAL_RSA_BAUD_BASE * 16 && + enable_rsa(port)) + port->uartclk = SERIAL_RSA_BAUD_BASE * 16; + if (port->uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_outp(port, UART_RSA_FRR, 0); + } +#endif + + /* + * Clear the FIFO buffers and disable them. + * (they will be reeanbled in change_speed()) + */ + if (uart_config[port->type].flags & UART_CLEAR_FIFO) { + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(port, UART_FCR, 0); + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(port, UART_LSR); + (void) serial_inp(port, UART_RX); + (void) serial_inp(port, UART_IIR); + (void) serial_inp(port, 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 (!(port->flags & ASYNC_BUGGY_UART) && + (serial_inp(port, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", port->line); + return -ENODEV; + } + + /* + * Allocate the IRQ if necessary + */ + if (port->irq && (!IRQ_ports[port->irq] || + !IRQ_ports[port->irq]->next_info)) { + handler = rs_interrupt_single; + if (IRQ_ports[port->irq]) { +#ifdef CONFIG_SERIAL_8250_SHARE_IRQ + handler = rs_interrupt; + free_irq(port->irq, &IRQ_ports[port->irq]); +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (rs_multiport[port->irq].port1) + handler = serial8250_interrupt_multi; +#endif +#else + return -EBUSY; +#endif /* CONFIG_SERIAL_8250_SHARE_IRQ */ + } + + retval = request_irq(port->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[port->irq]); + if (retval) + return retval; + } + + /* + * Insert serial port into IRQ chain. + */ + info->next_info = IRQ_ports[port->irq]; + IRQ_ports[port->irq] = info; + + /* + * Now, initialize the UART + */ + serial_outp(port, UART_LCR, UART_LCR_WLEN8); + +#ifdef CONFIG_SERIAL_MANY_PORTS + if (port->flags & ASYNC_FOURPORT) { + if (port->irq == 0) + info->mctrl |= TIOCM_OUT1; + } else +#endif + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + if (port->irq != 0) + info->mctrl |= TIOCM_OUT2; + + /* FIXME: ALPHA_KLUDGE_MCR; */ + serial8250_set_mctrl(port, info->mctrl); + + /* + * 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. + */ + port->port_ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(port, UART_IER, port->port_ier); + +#ifdef CONFIG_SERIAL_MANY_PORTS + if (port->flags & ASYNC_FOURPORT) { + unsigned int ICP; + /* + * Enable interrupts on the AST Fourport board + */ + ICP = (port->iobase & 0xfe0) | 0x01f; + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + + /* + * And clear the interrupt registers again for luck. + */ + (void) serial_inp(port, UART_LSR); + (void) serial_inp(port, UART_RX); + (void) serial_inp(port, UART_IIR); + (void) serial_inp(port, UART_MSR); + + return 0; +} + +static void serial8250_shutdown(struct uart_port *port, struct uart_info *info) +{ + struct uart_info **infop; + unsigned long flags; + int retval; + + /* + * First, disable all intrs from the port. + */ + port->port_ier = 0; + serial_outp(port, UART_IER, 0); + + synchronize_irq(); + + /* + * unlink the serial port from the IRQ chain... + */ + for (infop = &IRQ_ports[port->irq]; *infop; infop = &(*infop)->next_info) + if (*infop == info) + break; + + if (*infop == info) + *infop = info->next_info; + + /* + * Free the IRQ, if necessary + */ + if (port->irq && (!IRQ_ports[port->irq] || + !IRQ_ports[port->irq]->next_info)) { + free_irq(port->irq, &IRQ_ports[port->irq]); + if (IRQ_ports[port->irq]) { + retval = request_irq(port->irq, rs_interrupt_single, + SA_SHIRQ, "serial", &IRQ_ports[port->irq]); + if (retval) + printk("serial shutdown: request_irq: error %d" + " couldn't reacquire IRQ.\n", retval); + } + } + +#ifdef CONFIG_SERIAL_MANY_PORTS + if (port->flags & ASYNC_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((port->iobase & 0xfe0) | 0x1f); + info->mctrl |= TIOCM_OUT1; + } else +#endif + info->mctrl &= ~TIOCM_OUT2; + + /* FIXME: ALPHA_KLUDGE_MCR; */ + serial8250_set_mctrl(port, info->mctrl); + + /* + * Disable break condition and FIFOs + */ + serial_out(port, UART_LCR, serial_inp(port, UART_LCR) & ~UART_LCR_SBC); + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_outp(port, UART_FCR, 0); + +#ifdef CONFIG_SERIAL_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + if (port->type == PORT_RSA && + port->uartclk == SERIAL_RSA_BAUD_BASE * 16 && + disable_rsa(port)) + port->uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; +#endif + + /* + * Read data port to reset things + */ + (void) serial_in(port, UART_RX); +} + +static void serial8250_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) +{ + 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 && port->type == PORT_16C950 && + port->port_rev == 0x5201) + quot ++; + + if (uart_config[port->type].flags & UART_USE_FIFO) { + if ((port->uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_RSA + else if (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 (port->type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (iflag & IGNPAR) + port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (iflag & IGNBRK) { + 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) + port->ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + port->port_ier &= ~UART_IER_MSI; + if (port->flags & ASYNC_HARDPPS_CD || cflag & CRTSCTS || + !(cflag & CLOCAL)) + port->port_ier |= UART_IER_MSI; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + save_flags(flags); cli(); + serial_out(port, UART_IER, port->port_ier); + + if (uart_config[port->type].flags & UART_STARTECH) { + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0); + } + serial_outp(port, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ + serial_outp(port, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(port, UART_DLM, quot >> 8); /* MS of divisor */ + if (port->type == PORT_16750) + serial_outp(port, UART_FCR, fcr); /* set fcr */ + serial_outp(port, UART_LCR, cval); /* reset DLAB */ + port->port_lcr = cval; /* Save LCR */ + if (port->type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(port, UART_FCR, fcr); /* set fcr */ + } + restore_flags(flags); +} + +static void serial8250_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + if (state) { + /* sleep */ + if (uart_config[port->type].flags & UART_STARTECH) { + /* Arrange to enter sleep mode */ + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, UART_EFR_ECB); + serial_outp(port, UART_LCR, 0); + serial_outp(port, UART_IER, UART_IERX_SLEEP); + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, 0); + serial_outp(port, UART_LCR, 0); + } + if (port->type == PORT_16750) { + /* Arrange to enter sleep mode */ + serial_outp(port, UART_IER, UART_IERX_SLEEP); + } + } else { + /* wake */ + if (uart_config[port->type].flags & UART_STARTECH) { + /* Wake up UART */ + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, UART_EFR_ECB); + /* + * Turn off LCR == 0xBF so we actually set the IER + * register on the XR16C850 + */ + serial_outp(port, UART_LCR, 0); + serial_outp(port, UART_IER, 0); + /* + * Now reset LCR so we can turn off the ECB bit + */ + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, 0); + /* + * For a XR16C850, we need to set the trigger levels + */ + if (port->type == PORT_16850) { + unsigned char fctr; + + fctr = serial_inp(port, UART_FCTR) & + ~(UART_FCTR_RX | UART_FCTR_TX); + serial_outp(port, UART_FCTR, fctr | + UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(port, UART_TRG, UART_TRG_96); + serial_outp(port, UART_FCTR, fctr | + UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(port, UART_TRG, UART_TRG_96); + } + serial_outp(port, UART_LCR, 0); + } + + if (port->type == PORT_16750) { + /* Wake up UART */ + serial_outp(port, 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_port *port, struct resource **res) +{ + unsigned int size = 8 << port->regshift; + int ret = 0; + + switch (port->iotype) { + case SERIAL_IO_MEM: + if (port->mapbase) { + *res = request_mem_region(port->mapbase, size, "serial"); + if (!*res) + ret = -EBUSY; + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + *res = request_region(port->iobase, size, "serial"); + if (!*res) + ret = -EBUSY; + break; + } + return ret; +} + +static int +serial8250_request_rsa_resource(struct uart_port *port, struct resource **res) +{ + unsigned long start, size = 8 << port->regshift; + int ret = 0; + + switch (port->iotype) { + case SERIAL_IO_MEM: + if (port->mapbase) { + start = port->mapbase; + start += UART_RSA_BASE << port->regshift; + *res = request_mem_region(start, size, "serial-rsa"); + if (!*res) + ret = -EBUSY; + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + start = port->iobase; + start += UART_RSA_BASE << 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) +{ + unsigned long start, offset = 0, size = 0; + + if (port->type == PORT_RSA) { + offset = UART_RSA_BASE << port->regshift; + size = 8; + } + + offset <<= port->regshift; + size <<= port->regshift; + + switch (port->iotype) { + case SERIAL_IO_MEM: + if (port->mapbase) { + /* + * Unmap the area. + */ + iounmap(port->membase); + port->membase = NULL; + + start = port->mapbase; + + if (size) + release_mem_region(start + offset, size); + release_mem_region(start, 8 << port->regshift); + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + start = port->iobase; + + if (size) + release_region(start + offset, size); + release_region(start + offset, 8 << port->regshift); + break; + + default: + break; + } +} + +static int serial8250_request_port(struct uart_port *port) +{ + struct resource *res = NULL, *res_rsa = NULL; + int ret = -EBUSY; + + if (port->type == PORT_RSA) { + ret = serial8250_request_rsa_resource(port, &res_rsa); + if (ret) + return ret; + } + + ret = serial8250_request_std_resource(port, &res); + + /* + * If we have a mapbase, then request that as well. + */ + if (res != NULL && port->iotype == SERIAL_IO_MEM && + port->mapbase) { + int size = res->end - res->start + 1; + + port->membase = ioremap(port->mapbase, size); + if (!port->membase) + ret = -ENOMEM; + } + + if (ret) { + 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 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 (port->flags & ASYNC_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. + */ + ret = serial8250_request_std_resource(port, &res_std); + if (ret) + return; + + ret = serial8250_request_rsa_resource(port, &res_rsa); + if (ret) + probeflags &= ~PROBE_RSA; + + if (flags & UART_CONFIG_TYPE) + autoconfig(port, probeflags); + if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(port); + + /* + * If the port wasn't an RSA port, release the resource. + */ + if (port->type != PORT_RSA && res_rsa) + release_resource(res_rsa); + + if (port->type == PORT_UNKNOWN) + 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 >= PORT_MAX_8250) + 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_port serial8250_ports[UART_NR]; + +static void __init serial8250_isa_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < ARRAY_SIZE(old_serial_port); i++) { + serial8250_ports[i].iobase = old_serial_port[i].port; + serial8250_ports[i].irq = irq_cannonicalize(old_serial_port[i].irq); + serial8250_ports[i].uartclk = old_serial_port[i].base_baud * 16; + serial8250_ports[i].flags = old_serial_port[i].flags; + serial8250_ports[i].ops = &serial8250_pops; + } +} + +#ifdef CONFIG_SERIAL_8250_CONSOLE +#ifdef used_and_not_const_char_pointer +static int serial8250_console_read(struct uart_port *port, char *s, u_int count) +{ +} +#endif + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int status, tmout = 1000000; + + do { + status = serial_in(port, UART_LSR); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait for flow control if necessary */ + if (port->flags & ASYNC_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(port, UART_MSR) & UART_MSR_CTS) == 0)); + } +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void serial8250_console_write(struct console *co, const char *s, u_int count) +{ + struct uart_port *port = serial8250_ports + co->index; + unsigned int ier; + int i; + + /* + * First save the UER then disable the interrupts + */ + ier = serial_in(port, UART_IER); + serial_out(port, UART_IER, 0); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(port); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(port, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(port); + serial_out(port, UART_TX, 13); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(port); + serial_out(port, UART_IER, ier); +} + +static kdev_t serial8250_console_device(struct console *co) +{ + return MKDEV(TTY_MAJOR, 64 + co->index); +} + +static int serial8250_console_wait_key(struct console *co) +{ + struct uart_port *port = serial8250_ports + co->index; + int ier, c; + + /* + * First save the IER then disable the interrupts so + * that the real driver for the port does not get the + * character. + */ + ier = serial_in(port, UART_IER); + serial_out(port, UART_IER, 0); + + while ((serial_in(port, UART_LSR) & UART_LSR_DR) == 0); + c = serial_in(port, UART_RX); + + /* + * Restore the interrupts + */ + serial_out(port, UART_IER, ier); + + return c; +} + +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. + */ + port = uart_get_console(serial8250_ports, UART_NR, co); + + 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, +#ifdef used_and_not_const_char_pointer + read: serial8250_console_read, +#endif + device: serial8250_console_device, + wait_key: serial8250_console_wait_key, + 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: ARRAY_SIZE(old_serial_port), + port: serial8250_ports, + cons: SERIAL8250_CONSOLE, +}; + +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +int register_serial(struct serial_struct *req) +{ + 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 | ASYNC_BOOT_AUTOCONF; + + if (HIGH_BITS_OFFSET) + port.iobase |= 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); +} + +void unregister_serial(int line) +{ + uart_unregister_port(&serial8250_reg, line); +} + +static int __init serial8250_init(void) +{ + serial8250_isa_init_ports(); + return uart_register_driver(&serial8250_reg); +} + +static void __exit serial8250_exit(void) +{ + uart_unregister_driver(&serial8250_reg); +} + +module_init(serial8250_init); +module_exit(serial8250_exit); + +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); + diff -Naur linux-2.4.32.orig/drivers/serial/8250.h linux-2.4.32/drivers/serial/8250.h --- linux-2.4.32.orig/drivers/serial/8250.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/8250.h 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,101 @@ +/* + * linux/drivers/char/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 2001/07/08 22:07:04 rmk Exp $ + */ + +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); + +struct old_serial_port { + unsigned int uart; + unsigned int base_baud; + unsigned int port; + unsigned int irq; + unsigned int flags; +}; + +#undef SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +#ifdef CONFIG_PCI +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#endif + +#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +#ifndef ENABLE_SERIAL_PNP +#define ENABLE_SERIAL_PNP +#endif +#endif + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_PCI +#undef SERIAL_DEBUG_AUTOCONF + +/* Sanity checks */ + +#ifdef CONFIG_SERIAL_MULTIPORT +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef CONFIG_HUB6 +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef MODULE +#undef CONFIG_SERIAL_CONSOLE +#endif + +#define CONFIG_SERIAL_RSA + +#define RS_ISR_PASS_LIMIT 256 + +#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) + + diff -Naur linux-2.4.32.orig/drivers/serial/8250_pci.c linux-2.4.32/drivers/serial/8250_pci.c --- linux-2.4.32.orig/drivers/serial/8250_pci.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/8250_pci.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,1081 @@ +/* + * 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 2001/11/14 23:48:43 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 "serial_8250.h" + + +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_IO) +#endif +#ifndef IS_PCI_REGION_IOMEM +#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_MEM) +#endif +#ifndef PCI_IRQ_RESOURCE +#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +#endif + +#ifndef pci_get_subvendor +#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +#define pci_get_subdevice(dev) ((dev)->subsystem_device) +#endif + +struct serial_private { + unsigned int nr; + struct pci_board *board; + int line[0]; +}; + +struct pci_board { + int flags; + int num_ports; + int base_baud; + int uart_offset; + int reg_shift; + int (*init_fn)(struct pci_dev *dev, struct pci_board *board, + int enable); + int first_uart_offset; +}; + +static int +get_pci_port(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned long port; + int base_idx; + int max_port; + int offset; + + base_idx = SPCI_FL_GET_BASE(board->flags); + if (board->flags & SPCI_FL_BASE_TABLE) + base_idx += idx; + + if (board->flags & SPCI_FL_REGION_SZ_CAP) { + max_port = pci_resource_len(dev, base_idx) / 8; + if (idx >= max_port) + return 1; + } + + offset = board->first_uart_offset; + + /* Timedia/SUNIX uses a mixture of BARs and offsets */ + /* Ugh, this is ugly as all hell --- TYT */ + if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ + switch(idx) { + case 0: base_idx=0; + break; + case 1: base_idx=0; offset=8; + break; + case 2: base_idx=1; + break; + case 3: base_idx=1; offset=8; + break; + case 4: /* BAR 2*/ + case 5: /* BAR 3 */ + case 6: /* BAR 4*/ + case 7: base_idx=idx-2; /* BAR 5*/ + } + + /* Some Titan cards are also a little weird */ + if (dev->vendor == PCI_VENDOR_ID_TITAN && + (dev->device == PCI_DEVICE_ID_TITAN_400L || + dev->device == PCI_DEVICE_ID_TITAN_800L)) { + switch (idx) { + case 0: base_idx = 1; + break; + case 1: base_idx = 2; + break; + default: + base_idx = 4; + offset = 8 * (idx - 2); + } + } + + port = pci_resource_start(dev, base_idx) + offset; + + if ((board->flags & SPCI_FL_BASE_TABLE) == 0) + port += idx * (board->uart_offset ? board->uart_offset : 8); + + if (IS_PCI_REGION_IOPORT(dev, base_idx)) { + req->port = port; + if (HIGH_BITS_OFFSET) + req->port_high = port >> HIGH_BITS_OFFSET; + else + req->port_high = 0; + return 0; + } + req->io_type = SERIAL_IO_MEM; + req->iomem_base = ioremap(port, board->uart_offset); + req->iomem_reg_shift = board->reg_shift; + req->port = 0; + return 0; +} + +static _INLINE_ int get_pci_irq(struct pci_dev *dev, + struct pci_board *board, + int idx) +{ + int base_idx; + + if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) + return dev->irq; + + base_idx = SPCI_FL_GET_IRQBASE(board->flags); + if (board->flags & SPCI_FL_IRQ_TABLE) + base_idx += idx; + + return PCI_IRQ_RESOURCE(dev, base_idx); +} + +/* + * Some PCI serial cards using the PLX 9050 PCI interface chip require + * that the card interrupt be explicitly enabled or disabled. This + * seems to be mainly needed on card using the PLX which also use I/O + * mapped memory. + */ +static int __devinit +pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u8 data, *p, irq_config; + int pci_config; + + irq_config = 0x41; + pci_config = PCI_COMMAND_MEMORY; + if (dev->vendor == PCI_VENDOR_ID_PANACOM) + irq_config = 0x43; + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the deep + * FIFOs + */ + irq_config = 0x5b; + pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + } + + pci_read_config_byte(dev, PCI_COMMAND, &data); + + if (enable) + pci_write_config_byte(dev, PCI_COMMAND, + data | pci_config); + + /* enable/disable interrupts */ + p = ioremap(pci_resource_start(dev, 0), 0x80); + writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c); + iounmap(p); + + if (!enable) + pci_write_config_byte(dev, PCI_COMMAND, + data & ~pci_config); + return 0; +} + + +/* + * SIIG serial cards have an PCI interface chip which also controls + * the UART clocking frequency. Each UART can be clocked independently + * (except cards equiped with 4 UARTs) and initial clocking settings + * are stored in the EEPROM chip. It can cause problems because this + * version of serial driver doesn't support differently clocked UART's + * on single PCI card. To prevent this, initialization functions set + * high frequency clocking for all UART's on given card. It is safe (I + * hope) because it doesn't touch EEPROM settings to prevent conflicts + * with other OSes (like M$ DOS). + * + * SIIG support added by Andrey Panin , 10/1999 + * + * There is two family of SIIG serial cards with different PCI + * interface chip and different configuration methods: + * - 10x cards have control registers in IO and/or memory space; + * - 20x cards have control registers in standard PCI configuration space. + */ + +#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) + +static int __devinit +pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u16 data, *p; + + if (!enable) return 0; + + p = ioremap(pci_resource_start(dev, 0), 0x80); + + switch (dev->device & 0xfff8) { + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; + } + + writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); + iounmap(p); + return 0; +} + +#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) + +static int __devinit +pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + u8 data; + + if (!enable) return 0; + + /* Change clock frequency for the first UART. */ + pci_read_config_byte(dev, 0x6f, &data); + pci_write_config_byte(dev, 0x6f, data & 0xef); + + /* If this card has 2 UART, we have to do the same with second UART. */ + if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || + ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { + pci_read_config_byte(dev, 0x73, &data); + pci_write_config_byte(dev, 0x73, data & 0xef); + } + return 0; +} + +/* Added for EKF Intel i960 serial boards */ +static int __devinit +pci_inteli960ni_fn(struct pci_dev *dev, + struct pci_board *board, + int enable) +{ + unsigned long oldval; + + if (!(pci_get_subdevice(dev) & 0x1000)) + return(-1); + + if (!enable) /* is there something to deinit? */ + return(0); + + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void*) &oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return(-1); + } + return(0); +} + +/* + * Timedia has an explosion of boards, and to avoid the PCI table from + * growing *huge*, we use this function to collapse some 70 entries + * in the PCI table into one, for sanity's and compactness's sake. + */ +static unsigned short timedia_single_port[] = { + 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 }; +static unsigned short timedia_dual_port[] = { + 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, + 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, + 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, + 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, + 0xD079, 0 }; +static unsigned short timedia_quad_port[] = { + 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, + 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, + 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, + 0xB157, 0 }; +static unsigned short timedia_eight_port[] = { + 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, + 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 }; +static struct timedia_struct { + int num; + unsigned short *ids; +} timedia_data[] = { + { 1, timedia_single_port }, + { 2, timedia_dual_port }, + { 4, timedia_quad_port }, + { 8, timedia_eight_port }, + { 0, 0 } +}; + +static int __devinit +pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + int i, j; + unsigned short *ids; + + if (!enable) + return 0; + + for (i=0; timedia_data[i].num; i++) { + ids = timedia_data[i].ids; + for (j=0; ids[j]; j++) { + if (pci_get_subdevice(dev) == ids[j]) { + board->num_ports = timedia_data[i].num; + return 0; + } + } + } + return 0; +} + +static int __devinit +pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable) +{ + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + return 0; +} + +/* + * This is the configuration table for all of the PCI serial boards + * which we support. It is directly indexed by the pci_board_num_t enum + * value, which is encoded in the pci_device_id PCI probe table's + * driver_data member. + */ +enum pci_board_num_t { + pbn_b0_1_115200, + pbn_default = 0, + + pbn_b0_2_115200, + pbn_b0_4_115200, + + pbn_b0_1_921600, + pbn_b0_2_921600, + pbn_b0_4_921600, + + pbn_b0_bt_1_115200, + pbn_b0_bt_2_115200, + pbn_b0_bt_1_460800, + pbn_b0_bt_2_460800, + + pbn_b1_1_115200, + pbn_b1_2_115200, + pbn_b1_4_115200, + pbn_b1_8_115200, + + pbn_b1_2_921600, + pbn_b1_4_921600, + pbn_b1_8_921600, + + pbn_b1_2_1382400, + pbn_b1_4_1382400, + pbn_b1_8_1382400, + + pbn_b2_8_115200, + pbn_b2_4_460800, + pbn_b2_8_460800, + pbn_b2_16_460800, + pbn_b2_4_921600, + pbn_b2_8_921600, + + pbn_b2_bt_1_115200, + pbn_b2_bt_2_115200, + pbn_b2_bt_4_115200, + pbn_b2_bt_2_921600, + + pbn_panacom, + pbn_panacom2, + pbn_panacom4, + pbn_plx_romulus, + pbn_oxsemi, + pbn_timedia, + pbn_intel_i960, + pbn_sgi_ioc3, +#ifdef CONFIG_DDB5074 + pbn_nec_nile4, +#endif +#if 0 + pbn_dci_pccom8, +#endif + pbn_xircom_combo, + + pbn_siig10x_0, + pbn_siig10x_1, + pbn_siig10x_2, + pbn_siig10x_4, + pbn_siig20x_0, + pbn_siig20x_2, + pbn_siig20x_4, + + pbn_computone_4, + pbn_computone_6, + pbn_computone_8, +}; + +static struct pci_board pci_boards[] __devinitdata = { + /* + * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, + * Offset to get to next UART's registers, + * Register shift to use for memory-mapped I/O, + * Initialization function, first UART offset + */ + + /* Generic serial board, pbn_b0_1_115200, pbn_default */ + { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, + pbn_default */ + + { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ + { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ + + { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ + { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ + { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ + + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ + + { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ + { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ + { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ + { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ + + { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ + { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ + { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ + + { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ + { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ + { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ + + { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ + { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ + { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ + { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ + { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ + { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ + + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ + + { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ + 0x400, 7, pci_plx9050_fn }, + { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ + 0x20, 2, pci_plx9050_fn, 0x03 }, + /* This board uses the size of PCI Base region 0 to + * signal now many ports are available */ + { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ + { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ + 0, 0, pci_timedia_fn }, + /* EKF addition for i960 Boards form EKF with serial port */ + { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ + 8<<2, 2, pci_inteli960ni_fn, 0x10000}, + { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ + 1, 458333, 0, 0, 0, 0x20178 }, +#ifdef CONFIG_DDB5074 + /* + * NEC Vrc-5074 (Nile 4) builtin UART. + * Conditionally compiled in since this is a motherboard device. + */ + { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ + 64, 3, NULL, 0x300 }, +#endif +#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ /* pbn_dci_pccom8 */ + { SPCI_FL_BASE3, 8, 115200, 8 }, +#endif + { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ + 0, 0, pci_xircom_fn }, + + { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ + 0, 0, pci_siig10x_fn }, + { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ + 0, 0, pci_siig20x_fn }, + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ + 0, 0, pci_siig20x_fn }, + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ + 0, 0, pci_siig20x_fn }, + + { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ + 0x40, 2, NULL, 0x200 }, + { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ + 0x40, 2, NULL, 0x200 }, + { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ + 0x40, 2, NULL, 0x200 }, +}; + +/* + * Given a complete unknown PCI device, try to use some heuristics to + * guess what the configuration might be, based on the pitiful PCI + * serial specs. Returns 0 on success, 1 on failure. + */ +static int __devinit serial_pci_guess_board(struct pci_dev *dev, + struct pci_board *board) +{ + int num_iomem = 0, num_port = 0, first_port = -1; + int i; + + /* + * If it is not a communications device or the programming + * interface is greater than 6, give up. + * + * (Should we try to make guesses for multiport serial devices + * later?) + */ + if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && + ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || + (dev->class & 0xff) > 6) + return 1; + + for (i=0; i < 6; i++) { + if (IS_PCI_REGION_IOPORT(dev, i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + if (IS_PCI_REGION_IOMEM(dev, i)) + num_iomem++; + } + + /* + * If there is 1 or 0 iomem regions, and exactly one port, use + * it. + */ + if (num_iomem <= 1 && num_port == 1) { + board->flags = first_port; + return 0; + } + return 1; +} + +/* + * return -1 to refuse + */ +static int pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct serial_private *priv; + struct pci_board *board, tmp; + struct serial_struct serial_req; + int base_baud, rc, k; + + board = &pci_boards[ent->driver_data]; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + if (ent->driver_data == pbn_default && + serial_pci_guess_board(dev, board)) + return -ENODEV; + else if (serial_pci_guess_board(dev, &tmp) == 0) { + printk(KERN_INFO "Redundant entry in serial pci_table. " + "Please send the output of\n" + "lspci -vv, this message (%d,%d,%d,%d)\n" + "and the manufacturer and name of " + "serial board or modem board\n" + "to serial-pci-info@lists.sourceforge.net.\n", + dev->vendor, dev->device, + pci_get_subvendor(dev), pci_get_subdevice(dev)); + } + + + priv = kmalloc(sizeof(struct serial_private) + + sizeof(unsigned int) * board->num_ports, + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * Run the initialization function, if any + */ + if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0)) { + kfree(priv); + return -ENODEV; + } + + base_baud = board->base_baud; + if (!base_baud) + base_baud = BASE_BAUD; + memset(&serial_req, 0, sizeof(serial_req)); + for (k=0; k < board->num_ports; k++) { + serial_req.irq = get_pci_irq(dev, board, k); + if (get_pci_port(dev, board, &serial_req, k)) + break; +#ifdef SERIAL_DEBUG_PCI + printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", + serial_req.port, serial_req.irq, serial_req.io_type); +#endif + serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; + serial_req.baud_base = base_baud; + priv->line[k] = register_serial(&serial_req); + if (priv->line[k] < 0) + break; + } + + priv->board = board; + priv->nr = k; + + pci_set_drvdata(dev, priv); + + return 0; +} + +static void pci_remove_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + int i; + + pci_set_drvdata(dev, NULL); + + for (i = 0; i < priv->nr; i++) + unregister_serial(priv->line[i]); + + priv->board->init_fn(dev, priv->board, 0); + + kfree(priv); +} + +static struct pci_device_id serial_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, + pbn_b1_2_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, + pbn_b1_4_921600 }, + + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_1_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + /* VScom SPCOM800, from sl@s.pl */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, + PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, + pbn_panacom }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom4 }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom2 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, + pbn_b2_8_460800 }, + /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ + /* (Exoray@isys.ca) */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, + 0x10b5, 0x106a, 0, 0, + pbn_plx_romulus }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_115200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_115200 }, + + /* Digitan DS560-558, from jimd@esoft.com */ + { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_115200 }, + + /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ + { PCI_VENDOR_ID_USR, 0x1008, + PCI_ANY_ID, PCI_ANY_ID, }, + + /* Titan Electronic cards */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE1, 1, 921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, + /* The 400L and 800L have a custom hack in get_pci_port */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE_TABLE, 4, 921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE_TABLE, 8, 921600 }, + + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_1 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig10x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_0 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_2 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_siig20x_4 }, + + /* Computone devices submitted by Doug McNash dmcnash@computone.com */ + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, + 0, 0, pbn_computone_4 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, + 0, 0, pbn_computone_8 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, + 0, 0, pbn_computone_6 }, + + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, + { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, + + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_460800 }, + + /* RAStel 2 port modem, gerg@moreton.com.au */ + { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + + /* EKF addition for i960 Boards form EKF with serial port */ + { PCI_VENDOR_ID_INTEL, 0x1960, + 0xE4BF, PCI_ANY_ID, 0, 0, + pbn_intel_i960 }, + + /* Xircom Cardbus/Ethernet combos */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_xircom_combo }, + + /* + * Untested PCI modems, sent in from various folks... + */ + + /* Elsa Model 56K PCI Modem, from Andreas Rath */ + { PCI_VENDOR_ID_ROCKWELL, 0x1004, + 0x1048, 0x1500, 0, 0, + pbn_b1_1_115200 }, + + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + 0xFF00, 0, 0, 0, + pbn_sgi_ioc3 }, + +#ifdef CONFIG_DDB5074 + /* + * NEC Vrc-5074 (Nile 4) builtin UART. + * Conditionally compiled in since this is a motherboard device. + */ + { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_nec_nile4 }, +#endif + +#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_dci_pccom8 }, +#endif + + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, }, + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00, }, + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, }, + { 0, } +}; + +static struct pci_driver serial_pci_driver = { + name: "serial", + probe: pci_init_one, + remove: pci_remove_one, + id_table: serial_pci_tbl, +}; + +static int __init serial8250_pci_init(void) +{ + return pci_module_init(&serial_pci_driver); +} + +static void __exit serial8250_pci_exit(void) +{ + pci_unregister_driver(&serial_pci_driver); +} + +module_init(serial8250_pci_init); +module_exit(serial8250_pci_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); +MODULE_GENERIC_TABLE(pci, serial_pci_tbl); diff -Naur linux-2.4.32.orig/drivers/serial/8250_pnp.c linux-2.4.32/drivers/serial/8250_pnp.c --- linux-2.4.32.orig/drivers/serial/8250_pnp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/8250_pnp.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,553 @@ +/* + * linux/drivers/char/serial_8250_pnp.c + * + * Probe module for 8250/16550-type ISAPNP serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * $Id: 8250_pnp.c,v 1.3 2001/10/02 10:13:34 rmk Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "serial_8250.h" + +static struct serial_state rs_table[] = { }; +#define NR_PORTS 0 + +struct pnpbios_device_id +{ + char id[8]; + unsigned long driver_data; +}; + +static const struct pnpbios_device_id pnp_dev_table[] = { + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "AAC000F", 0 }, + /* Anchor Datacomm BV */ + /* SXPro 144 External Data Fax Modem Plug & Play */ + { "ADC0001", 0 }, + /* SXPro 288 External Data Fax Modem Plug & Play */ + { "ADC0002", 0 }, + /* Rockwell 56K ACF II Fax+Data+Voice Modem */ + { "AKY1021", SPCI_FL_NO_SHIRQ }, + /* AZT3005 PnP SOUND DEVICE */ + { "AZT4001", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Boca Research */ + /* Boca Complete Ofc Communicator 14.4 Data-FAX */ + { "BRI0A49", 0 }, + /* Boca Research 33,600 ACF Modem */ + { "BRI1400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI3400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI0A49", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Computer Peripherals Inc */ + /* EuroViVa CommCenter-33.6 SP PnP */ + { "CPI4050", 0 }, + /* Creative Labs */ + /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ + { "CTL3001", 0 }, + /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ + { "CTL3011", 0 }, + /* Creative */ + /* Creative Modem Blaster Flash56 DI5601-1 */ + { "DMB1032", 0 }, + /* Creative Modem Blaster V.90 DI5660 */ + { "DMB2001", 0 }, + /* FUJITSU */ + /* Fujitsu 33600 PnP-I2 R Plug & Play */ + { "FUJ0202", 0 }, + /* Fujitsu FMV-FX431 Plug & Play */ + { "FUJ0205", 0 }, + /* Fujitsu 33600 PnP-I4 R Plug & Play */ + { "FUJ0206", 0 }, + /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ + { "FUJ0209", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "GVC000F", 0 }, + /* Hayes */ + /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ + { "HAY0001", 0 }, + /* Hayes Optima 336 V.34 + FAX + Voice PnP */ + { "HAY000C", 0 }, + /* Hayes Optima 336B V.34 + FAX + Voice PnP */ + { "HAY000D", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5670", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5674", 0 }, + /* Hayes Accura 56K Fax Modem PnP */ + { "HAY5675", 0 }, + /* Hayes 288, V.34 + FAX */ + { "HAYF000", 0 }, + /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ + { "HAYF001", 0 }, + /* IBM */ + /* IBM Thinkpad 701 Internal Modem Voice */ + { "IBM0033", 0 }, + /* Intertex */ + /* Intertex 28k8 33k6 Voice EXT PnP */ + { "IXDC801", 0 }, + /* Intertex 33k6 56k Voice EXT PnP */ + { "IXDC901", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDD801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDD901", 0 }, + /* Intertex 28k8 33k6 Voice SP INT PnP */ + { "IXDF401", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDF801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDF901", 0 }, + /* Kortex International */ + /* KORTEX 28800 Externe PnP */ + { "KOR4522", 0 }, + /* KXPro 33.6 Vocal ASVD PnP */ + { "KORF661", 0 }, + /* Lasat */ + /* LASAT Internet 33600 PnP */ + { "LAS4040", 0 }, + /* Lasat Safire 560 PnP */ + { "LAS4540", 0 }, + /* Lasat Safire 336 PnP */ + { "LAS5440", 0 }, + /* Microcom, Inc. */ + /* Microcom TravelPorte FAST V.34 Plug & Play */ + { "MNP0281", 0 }, + /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ + { "MNP0336", 0 }, + /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ + { "MNP0339", 0 }, + /* Microcom DeskPorte 28.8P Plug & Play */ + { "MNP0342", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0500", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0501", 0 }, + /* Microcom DeskPorte 28.8S Internal Plug & Play */ + { "MNP0502", 0 }, + /* Motorola */ + /* Motorola BitSURFR Plug & Play */ + { "MOT1105", 0 }, + /* Motorola TA210 Plug & Play */ + { "MOT1111", 0 }, + /* Motorola HMTA 200 (ISDN) Plug & Play */ + { "MOT1114", 0 }, + /* Motorola BitSURFR Plug & Play */ + { "MOT1115", 0 }, + /* Motorola Lifestyle 28.8 Internal */ + { "MOT1190", 0 }, + /* Motorola V.3400 Plug & Play */ + { "MOT1501", 0 }, + /* Motorola Lifestyle 28.8 V.34 Plug & Play */ + { "MOT1502", 0 }, + /* Motorola Power 28.8 V.34 Plug & Play */ + { "MOT1505", 0 }, + /* Motorola ModemSURFR External 28.8 Plug & Play */ + { "MOT1509", 0 }, + /* Motorola Premier 33.6 Desktop Plug & Play */ + { "MOT150A", 0 }, + /* Motorola VoiceSURFR 56K External PnP */ + { "MOT150F", 0 }, + /* Motorola ModemSURFR 56K External PnP */ + { "MOT1510", 0 }, + /* Motorola ModemSURFR 56K Internal PnP */ + { "MOT1550", 0 }, + /* Motorola ModemSURFR Internal 28.8 Plug & Play */ + { "MOT1560", 0 }, + /* Motorola Premier 33.6 Internal Plug & Play */ + { "MOT1580", 0 }, + /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ + { "MOT15B0", 0 }, + /* Motorola VoiceSURFR 56K Internal PnP */ + { "MOT15F0", 0 }, + /* Com 1 */ + /* Deskline K56 Phone System PnP */ + { "MVX00A1", 0 }, + /* PC Rider K56 Phone System PnP */ + { "MVX00F2", 0 }, + /* Pace 56 Voice Internal Plug & Play Modem */ + { "PMC2430", 0 }, + /* Generic */ + /* Generic standard PC COM port */ + { "PNP0500", 0 }, + /* Generic 16550A-compatible COM port */ + { "PNP0501", 0 }, + /* Compaq 14400 Modem */ + { "PNPC000", 0 }, + /* Compaq 2400/9600 Modem */ + { "PNPC001", 0 }, + /* Dial-Up Networking Serial Cable between 2 PCs */ + { "PNPC031", 0 }, + /* Dial-Up Networking Parallel Cable between 2 PCs */ + { "PNPC032", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC100", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC101", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC102", 0 }, + /* Standard Modem*/ + { "PNPC103", 0 }, + /* Standard 9600 bps Modem*/ + { "PNPC104", 0 }, + /* Standard 14400 bps Modem*/ + { "PNPC105", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC106", 0 }, + /* Standard Modem */ + { "PNPC107", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC108", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC109", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10A", 0 }, + /* Standard Modem */ + { "PNPC10B", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC10C", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC10D", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10E", 0 }, + /* Standard Modem */ + { "PNPC10F", 0 }, + /* Standard PCMCIA Card Modem */ + { "PNP2000", 0 }, + /* Rockwell */ + /* Modular Technology */ + /* Rockwell 33.6 DPF Internal PnP */ + /* Modular Technology 33.6 Internal PnP */ + { "ROK0030", 0 }, + /* Kortex International */ + /* KORTEX 14400 Externe PnP */ + { "ROK0100", 0 }, + /* Viking Components, Inc */ + /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ + { "ROK4920", 0 }, + /* Rockwell */ + /* British Telecom */ + /* Modular Technology */ + /* Rockwell 33.6 DPF External PnP */ + /* BT Prologue 33.6 External PnP */ + /* Modular Technology 33.6 External PnP */ + { "RSS00A0", 0 }, + /* Viking 56K FAX INT */ + { "RSS0262", 0 }, + /* SupraExpress 28.8 Data/Fax PnP modem */ + { "SUP1310", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1421", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1590", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1760", 0 }, + /* Phoebe Micro */ + /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ + { "TEX0011", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "UAC000F", 0 }, + /* 3Com Corp. */ + /* Gateway Telepath IIvi 33.6 */ + { "USR0000", 0 }, + /* Sportster Vi 14.4 PnP FAX Voicemail */ + { "USR0004", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR0006", 0 }, + /* U.S. Robotics 33.6K Voice EXT PnP */ + { "USR0007", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR2002", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR2070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR2080", 0 }, + /* U.S. Robotics 56K FAX INT */ + { "USR3031", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR3080", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3090", 0 }, + /* U.S. Robotics 56K Message */ + { "USR9100", 0 }, + /* U.S. Robotics 56K FAX EXT PnP*/ + { "USR9160", 0 }, + /* U.S. Robotics 56K FAX INT PnP*/ + { "USR9170", 0 }, + /* U.S. Robotics 56K Voice EXT PnP*/ + { "USR9180", 0 }, + /* U.S. Robotics 56K Voice INT PnP*/ + { "USR9190", 0 }, + { "", 0 } +}; + +static void inline avoid_irq_share(struct pci_dev *dev) +{ + int i, map = 0x1FF8; + struct serial_state *state = rs_table; + struct isapnp_irq *irq; + struct isapnp_resources *res = dev->sysdata; + + for (i = 0; i < NR_PORTS; i++) { + if (state->type != PORT_UNKNOWN) + clear_bit(state->irq, &map); + state++; + } + + for ( ; res; res = res->alt) + for(irq = res->irq; irq; irq = irq->next) + irq->map = map; +} + +static char *modem_names[] __devinitdata = { + "MODEM", "Modem", "modem", "FAX", "Fax", "fax", + "56K", "56k", "K56", "33.6", "28.8", "14.4", + "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", + "33600", "28800", "14400", "V.90", "V.34", "V.32", 0 +}; + +static int __devinit check_name(char *name) +{ + char **tmp; + + for (tmp = modem_names; *tmp; tmp++) + if (strstr(name, *tmp)) + return 1; + + return 0; +} + +static int inline check_compatible_id(struct pci_dev *dev) +{ + int i; + for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) + if ((dev->vendor_compatible[i] == + ISAPNP_VENDOR('P', 'N', 'P')) && + (swab16(dev->device_compatible[i]) >= 0xc000) && + (swab16(dev->device_compatible[i]) <= 0xdfff)) + return 0; + return 1; +} + +/* + * Given a complete unknown ISA PnP device, try to use some heuristics to + * detect modems. Currently use such heuristic set: + * - dev->name or dev->bus->name must contain "modem" substring; + * - device must have only one IO region (8 byte long) with base adress + * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. + * + * Such detection looks very ugly, but can detect at least some of numerous + * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[] + * table. + */ +static int serial_pnp_guess_board(struct pci_dev *dev, int *flags) +{ + struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata; + struct isapnp_resources *resa; + + if (!(check_name(dev->name) || check_name(dev->bus->name)) && + !(check_compatible_id(dev))) + return -ENODEV; + + if (!res || res->next) + return -ENODEV; + + for (resa = res->alt; resa; resa = resa->alt) { + struct isapnp_port *port; + for (port = res->port; port; port = port->next) + if ((port->size == 8) && + ((port->min == 0x2f8) || + (port->min == 0x3f8) || + (port->min == 0x2e8) || + (port->min == 0x3e8))) + return 0; + } + + return -ENODEV; +} + +static int +pnp_init_one(struct pci_dev *dev, const struct pnpbios_device_id *ent, + char *slot_name) +{ + struct serial_struct serial_req; + int ret, line, flags = ent ? ent->driver_data : 0; + + if (!ent) { + ret = serial_pnp_guess_board(dev, &flags); + if (ret) + return ret; + } + + if (dev->prepare(dev) < 0) { + printk("serial: PNP device '%s' prepare failed\n", + slot_name); + return -ENODEV; + } + + if (dev->active) + return -ENODEV; + + if (flags & SPCI_FL_NO_SHIRQ) + avoid_irq_share(dev); + + if (dev->activate(dev) < 0) { + printk("serial: PNP device '%s' activate failed\n", + slot_name); + return -ENODEV; + } + + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.irq = dev->irq_resource[0].start; + serial_req.port = pci_resource_start(dev, 0); + if (HIGH_BITS_OFFSET) + serial_req.port = pci_resource_start(dev, 0) >> HIGH_BITS_OFFSET; + +#ifdef SERIAL_DEBUG_PCI + printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", + serial_req.port, serial_req.irq, serial_req.io_type); +#endif + + serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; + serial_req.baud_base = 115200; + line = register_serial(&serial_req); + + if (line >= 0) { + pci_set_drvdata(dev, (void *)(line + 1)); + + /* + * Public health warning: remove this once the 2.5 + * pnpbios_module_init() stuff is incorporated. + */ + dev->driver = (void *)pnp_dev_table; + } else + dev->deactivate(dev); + + return line >= 0 ? 0 : -ENODEV; +} + +static void pnp_remove_one(struct pci_dev *dev) +{ + int line = (int)pci_get_drvdata(dev); + + if (line) { + pci_set_drvdata(dev, NULL); + + unregister_serial(line - 1); + + dev->deactivate(dev); + } +} + +static char hex[] = "0123456789ABCDEF"; + +/* + * This function should vanish when 2.5 comes around and + * we have pnpbios_module_init() + */ +static void pnp_init(void) +{ + const struct pnpbios_device_id *id; + struct pci_dev *dev = NULL; + +#ifdef SERIAL_DEBUG_PNP + printk("Entered probe_serial_pnp()\n"); +#endif + + isapnp_for_each_dev(dev) { + char slot_name[8]; + u32 pnpid; + + if (dev->active) + continue; + + pnpid = dev->vendor << 16 | dev->device; + pnpid = cpu_to_le32(pnpid); + +#define HEX(id,a) hex[((id)>>a) & 15] +#define CHAR(id,a) (0x40 + (((id)>>a) & 31)) + slot_name[0] = CHAR(pnpid, 26); + slot_name[1] = CHAR(pnpid, 21); + slot_name[2] = CHAR(pnpid, 16); + slot_name[3] = HEX(pnpid, 12); + slot_name[4] = HEX(pnpid, 8); + slot_name[5] = HEX(pnpid, 4); + slot_name[6] = HEX(pnpid, 0); + slot_name[7] = '\0'; + + for (id = pnp_dev_table; id->id[0]; id++) + if (memcmp(id->id, slot_name, 7) == 0) + break; + + if (id->id[0]) + pnp_init_one(dev, id, slot_name); + else + pnp_init_one(dev, NULL, slot_name); + } + +#ifdef SERIAL_DEBUG_PNP + printk("Leaving probe_serial_pnp() (probe finished)\n"); +#endif +} + +static int __init serial8250_pnp_init(void) +{ + if (!isapnp_present()) { +#ifdef SERIAL_DEBUG_PNP + printk("Leaving probe_serial_pnp() (no isapnp)\n"); +#endif + return -ENODEV; + } + pnp_init(); + return 0; +} + +static void __exit serial8250_pnp_exit(void) +{ + struct pci_dev *dev = NULL; + + isapnp_for_each_dev(dev) { + if (dev->driver != (void *)pnp_dev_table) + continue; + pnp_remove_one(dev); + } +} + +module_init(serial8250_pnp_init); +module_exit(serial8250_pnp_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PNPBIOS serial probe module"); +MODULE_GENERIC_TABLE(pnp, pnp_dev_table); + diff -Naur linux-2.4.32.orig/drivers/serial/Config.in linux-2.4.32/drivers/serial/Config.in --- linux-2.4.32.orig/drivers/serial/Config.in 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/Config.in 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,84 @@ +# +# 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_tristate 'Psion serial port support' CONFIG_SERIAL_PSIONW $CONFIG_ARCH_PSIONW + dep_bool ' Support for console on Psion serial port' CONFIG_SERIAL_PSIONW_CONSOLE $CONFIG_SERIAL_PSIONW + + 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 +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_PSIONW" = "y" -o \ + "$CONFIG_SERIAL_SA1100" = "y" -o \ + "$CONFIG_SERIAL_ANAKIN" = "y" -o \ + "$CONFIG_SERIAL_UART00" = "y" -o \ + "$CONFIG_SERIAL_8250" = "y" ]; then + define_bool CONFIG_SERIAL_CORE y +else + if [ "$CONFIG_SERIAL_AMBA" = "m" -o \ + "$CONFIG_SERIAL_CLPS711X" = "m" -o \ + "$CONFIG_SERIAL_PSIONW" = "m" -o \ + "$CONFIG_SERIAL_SA1100" = "m" -o \ + "$CONFIG_SERIAL_ANAKIN" = "m" -o \ + "$CONFIG_SERIAL_UART00" = "m" -o \ + "$CONFIG_SERIAL_8250" = "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_PSIONW_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" ]; then + define_bool CONFIG_SERIAL_CORE_CONSOLE y +fi + +endmenu diff -Naur linux-2.4.32.orig/drivers/serial/Makefile linux-2.4.32/drivers/serial/Makefile --- linux-2.4.32.orig/drivers/serial/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/Makefile 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,38 @@ +# +# 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 := serial_core.o serial_8250.o +obj-y := +obj-m := +obj-n := +obj- := + +serial-8250-y := +serial-8250-$(CONFIG_PCI) += serial_8250_pci.o +serial-8250-$(CONFIG_ISAPNP) += serial_8250_pnp.o +obj-$(CONFIG_SERIAL_CORE) += serial_core.o +obj-$(CONFIG_SERIAL_21285) += serial_21285.o +obj-$(CONFIG_SERIAL_8250) += serial_8250.o $(serial-8250-y) +obj-$(CONFIG_SERIAL_ANAKIN) += serial_anakin.o +obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o +obj-$(CONFIG_SERIAL_CLPS711X) += serial_clps711x.o +obj-$(CONFIG_SERIAL_PSIONW) += serial_psionw.o +obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o +obj-$(CONFIG_SERIAL_UART00) += serial_uart00.o + +include $(TOPDIR)/Rules.make + +fastdep: + diff -Naur linux-2.4.32.orig/drivers/serial/amba.c linux-2.4.32/drivers/serial/amba.c --- linux-2.4.32.orig/drivers/serial/amba.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/amba.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,796 @@ +/* + * 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.1 2001/11/27 17:35:39 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_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 + * + * We encode this bit information into port->driver_priv using the + * following macros. + */ +//#define PORT_CTRLS(dtrbit,rtsbit) ((1 << dtrbit) | (1 << (16 + rtsbit))) +#define PORT_CTRLS_DTR(port) (1 << (port)->unused[1]) +#define PORT_CTRLS_RTS(port) (1 << (port)->unused[0]) + +#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) + +/* + * Our private driver data mappings. + */ +#define drv_old_status driver_priv + +static void ambauart_stop_tx(struct uart_port *port, u_int from_tty) +{ + 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, u_int nonempty, u_int from_tty) +{ + if (nonempty) { + 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_info *info, struct pt_regs *regs) +#else +ambauart_rx_chars(struct uart_info *info) +#endif +{ + struct tty_struct *tty = info->tty; + unsigned int status, ch, rsr, max_count = 256; + struct uart_port *port = info->port; + + 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(info, &amba_console)) + 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(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 & 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_info *info) +{ + struct uart_port *port = info->port; + int count; + + 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) { + ambauart_stop_tx(port, 0); + return; + } + + count = port->fifosize >> 1; + do { + 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_event(info, EVT_WRITE_WAKEUP); + + if (info->xmit.head == info->xmit.tail) + ambauart_stop_tx(info->port, 0); +} + +static void ambauart_modem_status(struct uart_info *info) +{ + struct uart_port *port = info->port; + unsigned int status, delta; + + UART_PUT_ICR(port, 0); + + status = UART_GET_FR(port) & AMBA_UARTFR_MODEM_ANY; + + delta = status ^ info->drv_old_status; + info->drv_old_status = status; + + if (!delta) + return; + + if (delta & AMBA_UARTFR_DCD) + uart_handle_dcd_change(info, status & AMBA_UARTFR_DCD); + + if (delta & AMBA_UARTFR_DSR) + port->icount.dsr++; + + if (delta & AMBA_UARTFR_CTS) + uart_handle_cts_change(info, status & AMBA_UARTFR_CTS); + + wake_up_interruptible(&info->delta_msr_wait); +} + +static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + + status = UART_GET_INT_STATUS(info->port); + do { + 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-- == 0) + break; + + status = UART_GET_INT_STATUS(info->port); + } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | + AMBA_UARTIIR_TIS)); +} + +static u_int ambauart_tx_empty(struct uart_port *port) +{ + return UART_GET_FR(port) & AMBA_UARTFR_BUSY ? 0 : TIOCSER_TEMT; +} + +static u_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, u_int mctrl) +{ + u_int ctrls = 0, ctrlc = 0; + + if (mctrl & TIOCM_RTS) + ctrlc |= PORT_CTRLS_RTS(port); + else + ctrls |= PORT_CTRLS_RTS(port); + + if (mctrl & TIOCM_DTR) + ctrlc |= PORT_CTRLS_DTR(port); + else + ctrls |= PORT_CTRLS_DTR(port); + + __raw_writel(ctrls, SC_CTRLS); + __raw_writel(ctrlc, SC_CTRLC); +} + +static void ambauart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int lcr_h; + + 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); +} + +static int ambauart_startup(struct uart_port *port, struct uart_info *info) +{ + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, ambauart_int, 0, "amba", info); + if (retval) + return retval; + + /* + * initialise the old status of the modem signals + */ + info->drv_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, struct uart_info *info) +{ + /* + * Free the interrupt + */ + free_irq(port->irq, info); + + /* + * 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, u_int cflag, u_int iflag, u_int quot) +{ + u_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: lcr_h = AMBA_UARTLCR_H_WLEN_8; break; // CS8 + } + 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; + + 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; + + /* first, disable everything */ + save_flags(flags); cli(); + old_cr = UART_GET_CR(port) & ~AMBA_UARTCR_MSIE; + + if ((port->flags & ASYNC_HARDPPS_CD) || + (cflag & CRTSCTS) || !(cflag & CLOCAL)) + 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); + + restore_flags(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_port amba_ports[UART_NR] = { + { + membase: (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE), + mapbase: INTEGRATOR_UART0_BASE, + iotype: SERIAL_IO_MEM, + irq: IRQ_UARTINT0, + uartclk: 14745600, + fifosize: 16, + unused: { 4, 5 }, /*driver_priv: PORT_CTRLS(5, 4), */ + ops: &amba_pops, + flags: ASYNC_BOOT_AUTOCONF, + }, + { + membase: (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE), + mapbase: INTEGRATOR_UART1_BASE, + iotype: SERIAL_IO_MEM, + irq: IRQ_UARTINT1, + uartclk: 14745600, + fifosize: 16, + unused: { 6, 7 }, /*driver_priv: PORT_CTRLS(7, 6), */ + ops: &amba_pops, + flags: ASYNC_BOOT_AUTOCONF, + } +}; + +#ifdef CONFIG_SERIAL_AMBA_CONSOLE +#ifdef used_and_not_const_char_pointer +static int ambauart_console_read(struct uart_port *port, char *s, u_int count) +{ + unsigned int status; + int c; +#if DEBUG + printk("ambauart_console_read() called\n"); +#endif + + c = 0; + while (c < count) { + status = UART_GET_FR(port); + if (UART_RX_DATA(status)) { + *s++ = UART_GET_CHAR(port); + c++; + } else { + // nothing more to get, return + return c; + } + } + // return the count + return c; +} +#endif + +static void ambauart_console_write(struct console *co, const char *s, u_int count) +{ + struct uart_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 *co) +{ + return MKDEV(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + co->index); +} + +static int ambauart_console_wait_key(struct console *co) +{ + struct uart_port *port = amba_ports + co->index; + unsigned int status; + + do { + status = UART_GET_FR(port); + } while (!UART_RX_DATA(status)); + return UART_GET_CHAR(port); +} + +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) { + u_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. + */ + port = uart_get_console(amba_ports, UART_NR, co); + + 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, +#ifdef used_and_not_const_char_pointer + read: ambauart_console_read, +#endif + device: ambauart_console_device, + wait_key: ambauart_console_wait_key, + 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, + port: amba_ports, + cons: AMBA_CONSOLE, +}; + +static int __init ambauart_init(void) +{ + return uart_register_driver(&amba_reg); +} + +static void __exit ambauart_exit(void) +{ + uart_unregister_driver(&amba_reg); +} + +module_init(ambauart_init); +module_exit(ambauart_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("ARM AMBA serial port driver"); +MODULE_LICENSE("GPL"); diff -Naur linux-2.4.32.orig/drivers/serial/anakin.c linux-2.4.32/drivers/serial/anakin.c --- linux-2.4.32.orig/drivers/serial/anakin.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/anakin.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,568 @@ +/* + * 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.1 2001/11/27 17:35:39 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); +} + +static int +anakin_console_wait_key(struct console *co) +{ + struct uart_port *port = anakin_ports + co->index; + unsigned int flags, status, ch; + + save_flags_cli(flags); + status = anakin_in(port, 0x18); + anakin_out(port, 0x18, status & ~IRQENABLE); + restore_flags(flags); + + while (!(anakin_in(port, 0x10) & RXRELEASE)); + ch = anakin_in(port, 0x14); + + if (status & IRQENABLE) { + save_flags_cli(flags); + anakin_out(port, 0x18, anakin_in(port, 0x18) | IRQENABLE); + restore_flags(flags); + } + return ch; +} + +/* + * 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, + wait_key: anakin_console_wait_key, + setup: anakin_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init +anakin_console_init(void) +{ + register_console(&anakin_console); +} + +#define ANAKIN_CONSOLE &anakin_console +#else +#define ANAKIN_CONSOLE NULL +#endif + +static struct uart_register anakin_reg = { + normal_major: SERIAL_ANAKIN_MAJOR, + normal_name: SERIAL_ANAKIN_NAME, + normal_driver: &normal, + callout_major: CALLOUT_ANAKIN_MAJOR, + callout_name: CALLOUT_ANAKIN_NAME, + callout_driver: &callout, + table: anakin_table, + termios: anakin_termios, + termios_locked: anakin_termios_locked, + minor: SERIAL_ANAKIN_MINOR, + nr: UART_NR, + state: anakin_state, + port: anakin_ports, + cons: ANAKIN_CONSOLE, +}; + +static int __init +anakin_init(void) +{ + return uart_register_port(&anakin_reg); +} + +__initcall(anakin_init); + +MODULE_DESCRIPTION("Anakin serial driver"); +MODULE_AUTHOR("Tak-Shing Chan "); +MODULE_SUPPORTED_DEVICE("ttyAN"); +MODULE_LICENSE("GPL"); + +EXPORT_NO_SYMBOLS; diff -Naur linux-2.4.32.orig/drivers/serial/clps711x.c linux-2.4.32/drivers/serial/clps711x.c --- linux-2.4.32.orig/drivers/serial/clps711x.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/clps711x.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,693 @@ +/* + * 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.1 2001/11/27 17:35:39 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) + +static void clps711xuart_stop_tx(struct uart_port *port, u_int from_tty) +{ + disable_irq(TX_IRQ(port)); +} + +static void clps711xuart_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) +{ + if (nonempty) + enable_irq(TX_IRQ(port)); +} + +static void clps711xuart_stop_rx(struct uart_port *port) +{ + disable_irq(RX_IRQ(port)); +} + +static void clps711xuart_enable_ms(struct uart_port *port) +{ +} + +#if 0 +static void ambauart_modem_status(struct uart_info *info) +{ + unsigned int status, delta; + struct uart_icount *icount = &info->port->icount; + + UART_PUT_ICR(info->port, 0); + + status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; + + delta = status ^ info->port->old_status; + info->port->old_status = status; + + if (!delta) + return; + + if (delta & AMBA_UARTFR_DCD) + uart_handle_dcd_change(info, status & AMBA_UARTFR_DCD); + + if (delta & AMBA_UARTFR_DSR) + icount->dsr++; + + if (delta & AMBA_UARTFR_CTS) + uart_handle_cts_change(info, status & AMBA_UARTFR_CTS); + + wake_up_interruptible(&info->delta_msr_wait); +} +#endif + +static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + struct tty_struct *tty = info->tty; + unsigned int status, ch, flg, ignored = 0; + struct uart_port *port = info->port; + + 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(info, 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 + info->sysrq = 0; +#endif + goto error_return; +} + +static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + struct uart_port *port = info->port; + int count; + + if (port->x_char) { + clps_writel(port->x_char, UARTDR(port)); + port->icount.tx++; + port->x_char = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + clps711xuart_stop_tx(info->port, 0); + return; + } + + count = port->fifosize >> 1; + do { + clps_writel(info->xmit.buf[info->xmit.tail], UARTDR(port)); + 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_event(info, EVT_WRITE_WAKEUP); + + if (info->xmit.head == info->xmit.tail) + clps711xuart_stop_tx(info->port, 0); +} + +static u_int clps711xuart_tx_empty(struct uart_port *port) +{ + u_int status = clps_readl(SYSFLG(port)); + return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; +} + +static u_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, u_int mctrl) +{ +} + +static void clps711xuart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int ubrlcr; + + ubrlcr = clps_readl(UBRLCR(port)); + if (break_state == -1) + ubrlcr |= UBRLCR_BREAK; + else + ubrlcr &= ~UBRLCR_BREAK; + clps_writel(ubrlcr, UBRLCR(port)); +} + +static int clps711xuart_startup(struct uart_port *port, struct uart_info *info) +{ + u_int syscon; + int retval; + + /* + * Allocate the IRQs + */ + retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0, + "clps711xuart_tx", info); + if (retval) + return retval; + + retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0, + "clps711xuart_rx", info); + if (retval) { + free_irq(TX_IRQ(port), info); + return retval; + } + + port->ops->set_mctrl(port, info->mctrl); + + /* + * 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, struct uart_info *info) +{ + u_int ubrlcr, syscon; + + /* + * Free the interrupt + */ + free_irq(TX_IRQ(port), info); /* TX interrupt */ + free_irq(RX_IRQ(port), info); /* 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, u_int cflag, u_int iflag, u_int quot) +{ + u_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: ubrlcr = UBRLCR_WRDLEN8; break; // CS8 + } + 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; + + port->read_status_mask = UARTDR_OVERR; + if (iflag & INPCK) + port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; +// 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 |= UARTDR_FRMERR | UARTDR_PARERR; + if (iflag & IGNBRK) { +// port->ignore_status_mask |= AMBA_UARTRSR_BE; + /* + * 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; + + /* first, disable everything */ + save_flags(flags); cli(); + + clps_writel(ubrlcr | quot, UBRLCR(port)); + + restore_flags(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, + flags: ASYNC_BOOT_AUTOCONF, + }, + { + iobase: SYSCON2, + irq: IRQ_UTXINT2, /* IRQ_URXINT2 */ + uartclk: 3686400, + fifosize: 16, + ops: &clps711x_pops, + flags: ASYNC_BOOT_AUTOCONF, + } +}; + +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + +#ifdef used_and_not_const_char_pointer +/* + * 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) + */ +static int clps711xuart_console_read(struct uart_port *port, char *s, u_int count) +{ + u_int status; + int c; +#if DEBUG + printk("clps711xuart_console_read() called\n"); +#endif + + c = 0; + while (c < count) { + status = clps_readl(SYSFLG(port)); + if (status & SYSFLG_URXFE) { + // nothing more to get, return + return c; + } else { + *s++ = clps_readl(UARTDR(port)); + 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_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, u_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); +} + +/* + * Receive character from the serial port + * what about interrupts? + */ +static int clps711xuart_console_wait_key(struct console *co) +{ + struct uart_port *port = clps711x_ports + co->index; + u_int status, syscon, ch; + + /* + * Ensure that the port is enabled. + */ + syscon = clps_readl(SYSCON(port)); + clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); + + do { + status = clps_readl(SYSFLG(port)); + } while (status & SYSFLG_URXFE); + ch = clps_readl(UARTDR(port)) & 0xff; + + clps_writel(syscon, SYSCON(port)); + + return ch; +} + +static void __init +clps711xuart_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { + u_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, +#ifdef used_and_not_const_char_pointer + read: clps711xuart_console_read, +#endif + device: clps711xuart_console_device, + wait_key: clps711xuart_console_wait_key, + 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, + + port: clps711x_ports, + cons: CLPS711X_CONSOLE, +}; + +static int __init clps711xuart_init(void) +{ + return uart_register_driver(&clps711x_reg); +} + +static void __exit clps711xuart_exit(void) +{ + uart_unregister_driver(&clps711x_reg); +} + +module_init(clps711xuart_init); +module_exit(clps711xuart_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("CLPS-711x generic serial driver"); +MODULE_LICENSE("GPL"); diff -Naur linux-2.4.32.orig/drivers/serial/core.c linux-2.4.32/drivers/serial/core.c --- linux-2.4.32.orig/drivers/serial/core.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/core.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,2247 @@ +/* + * linux/drivers/char/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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifndef CONFIG_PM +#define pm_access(pm) do { } while (0) +#define pm_unregister(pm) do { } while (0) +#endif + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static u_char *tmp_buf; +static DECLARE_MUTEX(tmp_buf_sem); + +/* + * This is used to lock changes in serial line configuration. + */ +static DECLARE_MUTEX(port_sem); + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static void uart_change_speed(struct uart_info *info, struct termios *old_termios); +static void uart_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * This routine is used by the interrupt handler to schedule processing in + * the software interrupt portion of the driver. It is expected that + * interrupts will be disabled (and so the tasklet will be prevented + * from running (CHECK)). + */ +void uart_event(struct uart_info *info, int event) +{ + info->event |= 1 << event; + tasklet_schedule(&info->tlet); +} + +static void uart_stop(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + info->ops->stop_tx(info->port, 1); + spin_unlock_irqrestore(&info->lock, flags); +} + +static void __uart_start(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + if (info->xmit.head != info->xmit.tail && info->xmit.buf && + !tty->stopped && !tty->hw_stopped) + info->ops->start_tx(info->port, 1, 1); +} + +static void uart_start(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + + pm_access(info->state->pm); + + spin_lock_irqsave(&info->lock, flags); + __uart_start(tty); + spin_unlock_irqrestore(&info->lock, flags); +} + +static void uart_tasklet_action(unsigned long data) +{ + struct uart_info *info = (struct uart_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 inline void uart_update_altspeed(struct uart_info *info) +{ + 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; +} + +static int uart_startup(struct uart_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->port->type == PORT_UNKNOWN) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + + info->mctrl = 0; + + retval = info->ops->startup(info->port, info); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + retval = 0; + } + goto errout; + } + + 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) + uart_update_altspeed(info); + + /* + * and set the speed of the serial port + */ + uart_change_speed(info, NULL); + + /* + * Setup the RTS and DTR signals once the port + * is open and ready to respond. + */ + if (info->tty->termios->c_cflag & CBAUD) + info->mctrl |= TIOCM_RTS | TIOCM_DTR; + info->ops->set_mctrl(info->port, info->mctrl); + + info->flags |= ASYNC_INITIALIZED; + retval = 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 uart_shutdown(struct uart_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 and disable the port + */ + info->ops->shutdown(info->port, info); + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = NULL; + free_page(pg); + } + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS); + info->ops->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 inline u_int uart_calculate_quot(struct uart_info *info, u_int baud) +{ + u_int quot; + + /* Special case: B0 rate */ + if (!baud) + baud = 9600; + + /* Old HI/VHI/custom speed handling */ + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else + quot = info->port->uartclk / (16 * baud); + + return quot; +} + +static void uart_change_speed(struct uart_info *info, struct termios *old_termios) +{ + struct uart_port *port = info->port; + u_int quot, baud, cflag, bits, try; + + /* + * If we have no tty, termios, or the port does not exist, + * then we can't set the parameters for this port. + */ + if (!info->tty || !info->tty->termios || + info->port->type == PORT_UNKNOWN) + return; + + cflag = info->tty->termios->c_cflag; + + /* 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++; + + for (try = 0; try < 2; try ++) { + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(info->tty); + quot = uart_calculate_quot(info, baud); + if (quot) + break; + + /* + * Oops, the quotient was zero. Try again with + * the old baud rate if possible. + */ + info->tty->termios->c_cflag &= ~CBAUD; + if (old_termios) { + info->tty->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 + */ + info->tty->termios->c_cflag |= B9600; + } + + info->timeout = (port->fifosize * HZ * bits * quot) / + (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)) + + pm_access(info->state->pm); + + info->ops->change_speed(port, cflag, info->tty->termios->c_iflag, quot); +} + +static void uart_put_char(struct tty_struct *tty, u_char ch) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit.buf) + return; + + spin_lock_irqsave(&info->lock, flags); + if (CIRC_SPACE(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) != 0) { + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (UART_XMIT_SIZE - 1); + } + spin_unlock_irqrestore(&info->lock, flags); +} + +static void uart_flush_chars(struct tty_struct *tty) +{ + uart_start(tty); +} + +static int uart_write(struct tty_struct *tty, int from_user, + const u_char * buf, int count) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + int c, ret = 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + UART_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; + } + spin_lock_irqsave(&info->lock, flags); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + UART_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = (info->xmit.head + c) & + (UART_XMIT_SIZE - 1); + spin_unlock_irqrestore(&info->lock, flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + spin_lock_irqsave(&info->lock, flags); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + UART_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) & + (UART_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + spin_unlock_irqrestore(&info->lock, flags); + } + + uart_start(tty); + return ret; +} + +static int uart_write_room(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + + return CIRC_SPACE(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE); +} + +static int uart_chars_in_buffer(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + + return CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE); +} + +static void uart_flush_buffer(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + +#ifdef DEBUG + printk("uart_flush_buffer(%d) called\n", + MINOR(tty->device) - tty->driver.minor_start); +#endif + spin_lock_irqsave(&info->lock, flags); + info->xmit.head = info->xmit.tail = 0; + spin_unlock_irqrestore(&info->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_info *info = tty->driver_data; + + info->port->x_char = ch; + if (ch) + info->ops->start_tx(info->port, 1, 0); +} + +static void uart_throttle(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) + uart_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->lock, flags); + info->mctrl &= ~TIOCM_RTS; + info->ops->set_mctrl(info->port, info->mctrl); + spin_unlock_irqrestore(&info->lock, flags); + } +} + +static void uart_unthrottle(struct tty_struct *tty) +{ + struct uart_info *info = (struct uart_info *) tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) { + if (info->port->x_char) + info->port->x_char = 0; + else + uart_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->lock, flags); + info->mctrl |= TIOCM_RTS; + info->ops->set_mctrl(info->port, info->mctrl); + spin_unlock_irqrestore(&info->lock, flags); + } +} + +static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) +{ + struct uart_state *state = info->state; + struct uart_port *port = info->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 = 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 = state->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_info *info, + struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct uart_state *state = info->state; + struct uart_port *port = info->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(&port_sem); + down(&state->count_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 = state->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 & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) + goto exit; + port->flags = ((port->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; + } + + /* + * 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 (state->count > 1) + goto exit; + + /* + * We need to shutdown the serial port at the old + * port/type/irq combination. + */ + uart_shutdown(info); + } + + 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); + + /* + * 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 & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((port->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; + +check_and_exit: + retval = 0; + if (port->type == PORT_UNKNOWN) + goto exit; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_flags & info->flags) & ASYNC_SPD_MASK) || + old_custom_divisor != state->custom_divisor) { + uart_update_altspeed(info); + uart_change_speed(info, NULL); + } + } else + retval = uart_startup(info); +exit: + up(&state->count_sem); + up(&port_sem); + return retval; +} + + +/* + * uart_get_lsr_info - get line status register info + */ +static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) +{ + u_int result; + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + result = info->ops->tx_empty(info->port); + spin_unlock_irqrestore(&info->lock, flags); + + /* + * 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->port->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + UART_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + return put_user(result, value); +} + +static int uart_get_modem_info(struct uart_info *info, unsigned int *value) +{ + unsigned int result = info->mctrl; + + result |= info->ops->get_mctrl(info->port); + + return put_user(result, value); +} + +static int uart_set_modem_info(struct uart_info *info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg, old; + int ret = 0; + + if (get_user(arg, value)) + return -EFAULT; + + spin_lock_irq(&info->lock); + old = info->mctrl; + switch (cmd) { + case TIOCMBIS: info->mctrl |= arg; break; + case TIOCMBIC: info->mctrl &= ~arg; break; + case TIOCMSET: info->mctrl = arg; break; + default: ret = -EINVAL; break; + } + if (old != info->mctrl) + info->ops->set_mctrl(info->port, info->mctrl); + spin_unlock_irq(&info->lock); + return ret; +} + +static void uart_break_ctl(struct tty_struct *tty, int break_state) +{ + struct uart_info *info = tty->driver_data; + unsigned long flags; + + if (info->port->type != PORT_UNKNOWN) { + spin_lock_irqsave(&info->lock, flags); + info->ops->break_ctl(info->port, break_state); + spin_unlock_irqrestore(&info->lock, flags); + } +} + +static int uart_do_autoconfig(struct uart_info *info) +{ + struct uart_port *port = info->port; + int flags, ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + /* + * Take the 'count' lock. This prevents count + * from incrementing, and hence any extra opens + * of the port while we're auto-configging. + */ + down(&info->state->count_sem); + + ret = -EBUSY; + if (info->state->count == 1) { + uart_shutdown(info); + + /* + * 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 & ASYNC_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(info); + } + up(&info->state->count_sem); + return ret; +} + +/* + * Called from userspace. We can use spin_lock_irq() here. + */ +static int uart_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct uart_info *info = tty->driver_data; + struct uart_icount cprev, cnow; + struct serial_icounter_struct icount; + int ret = -ENOIOCTLCMD; + + 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: + ret = uart_get_modem_info(info, (unsigned int *)arg); + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + ret = uart_set_modem_info(info, cmd, + (unsigned int *)arg); + break; + + case TIOCGSERIAL: + ret = uart_get_info(info, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = uart_set_info(info, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = uart_do_autoconfig(info); + break; + + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(info, (unsigned int *)arg); + break; + + /* + * 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: + spin_lock_irq(&info->lock); + /* note the counters on entry */ + cprev = info->port->icount; + /* Force modem status interrupts on */ + info->ops->enable_ms(info->port); + spin_unlock_irq(&info->lock); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + spin_lock_irq(&info->lock); + cnow = info->port->icount; /* atomic copy */ + spin_unlock_irq(&info->lock); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + ret = -EIO; /* no change => error */ + break; + } + 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; + } + cprev = cnow; + } + break; + + /* + * 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: + spin_lock_irq(&info->lock); + cnow = info->port->icount; + spin_unlock_irq(&info->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; + + ret = copy_to_user((void *)arg, &icount, sizeof(icount)) + ? -EFAULT : 0; + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; + + default: + if (info->ops->ioctl) + ret = info->ops->ioctl(info->port, cmd, arg); + break; + } + return ret; +} + +static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct uart_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; + + uart_change_speed(info, old_termios); + + spin_lock_irqsave(&info->lock, flags); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { + info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR); + info->ops->set_mctrl(info->port, info->mctrl); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + info->mctrl |= TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + info->mctrl |= TIOCM_RTS; + info->ops->set_mctrl(info->port, info->mctrl); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { + tty->hw_stopped = 0; + __uart_start(tty); + } + spin_unlock_irqrestore(&info->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(&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_info *info = tty->driver_data; + struct uart_state *state; + unsigned long flags; + + if (!info) + return; + + state = info->state; + +#ifdef DEBUG + printk("uart_close() called\n"); +#endif + + /* + * This is safe, as long as the BKL exists in + * do_tty_hangup(), and we're protected by the BKL. + */ + if (tty_hung_up_p(filp)) + goto done; + + down(&state->count_sem); + spin_lock_irqsave(&info->lock, flags); + 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, info->port->line, state->count); + state->count = 0; + } + if (state->count) { + spin_unlock_irqrestore(&info->lock, flags); + up(&state->count_sem); + goto done; + } + info->flags |= ASYNC_CLOSING; + spin_unlock_irqrestore(&info->lock, flags); + up(&state->count_sem); + + /* + * 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) { + info->ops->stop_rx(info->port); + /* + * 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, info->timeout); + } + uart_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); + set_current_state(TASK_RUNNING); + } + wake_up_interruptible(&info->open_wait); + } else { +#ifdef CONFIG_PM + /* + * Put device into D3 state. + */ + pm_send(info->state->pm, PM_SUSPEND, (void *)3); +#else + if (info->ops->pm) + info->ops->pm(info->port, 3, 0); +#endif + } + + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + +done: + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); +} + +static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct uart_info *info = (struct uart_info *) tty->driver_data; + unsigned long char_time, expire; + + if (info->port->type == PORT_UNKNOWN || + 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; +#ifdef DEBUG + printk("uart_wait_until_sent(%d), jiff=%lu, expire=%lu...\n", + MINOR(tty->device) - tty->driver.minor_start, jiffies, + expire); +#endif + while (!info->ops->tx_empty(info->port)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +} + +/* + * This is called with the BKL in effect + * linux/drivers/char/tty_io.c:do_tty_hangup() + */ +static void uart_hangup(struct tty_struct *tty) +{ + struct uart_info *info = tty->driver_data; + struct uart_state *state = info->state; + + uart_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + uart_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 uart_block_til_ready(struct tty_struct *tty, struct file *filp, + struct uart_info *info) +{ + DECLARE_WAITQUEUE(wait, current); + struct uart_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. Note that + * we have set TTY_IO_ERROR for a non-enabled port. + */ + 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); + down(&state->count_sem); + spin_lock_irqsave(&info->lock, flags); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + spin_unlock_irqrestore(&info->lock, flags); + info->blocked_open++; + up(&state->count_sem); + while (1) { + spin_lock_irqsave(&info->lock, flags); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) { + info->mctrl |= TIOCM_DTR | TIOCM_RTS; + info->ops->set_mctrl(info->port, info->mctrl); + } + spin_unlock_irqrestore(&info->lock, 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 || + (info->ops->get_mctrl(info->port) & TIOCM_CAR))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + down(&state->count_sem); + if (extra_count) + state->count++; + info->blocked_open--; + up(&state->count_sem); + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static struct uart_info *uart_get(struct uart_driver *drv, int line) +{ + struct uart_state *state = drv->state + line; + struct uart_info *info; + + down(&state->count_sem); + state->count++; + if (state->info) + goto out; + + info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); + if (info) { + memset(info, 0, sizeof(struct uart_info)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->port = state->port; + info->flags = info->port->flags; + info->ops = info->port->ops; + info->state = state; + tasklet_init(&info->tlet, uart_tasklet_action, + (unsigned long)info); + } + if (state->info) + kfree(info); + else + state->info = info; +out: + up(&state->count_sem); + return state->info; +} + +/* + * Make sure we have the temporary buffer allocated. Note + * that we set retval appropriately above, and we rely on + * this. + */ +static inline int uart_alloc_tmpbuf(void) +{ + if (!tmp_buf) { + unsigned long buf = get_zeroed_page(GFP_KERNEL); + if (!tmp_buf) { + if (buf) + tmp_buf = (u_char *)buf; + else + return -ENOMEM; + } else + free_page(buf); + } + return 0; +} + +/* + * 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_info *info; + int retval, line = MINOR(tty->device) - tty->driver.minor_start; + +#ifdef DEBUG + printk("uart_open(%d) called\n", line); +#endif + + retval = -ENODEV; + if (line >= tty->driver.num) + goto fail; + + if (!try_inc_mod_count(drv->owner)) + goto fail; + + info = uart_get(drv, line); + retval = -ENOMEM; + if (!info) + goto out; + + /* + * Set the tty driver_data. If we fail from this point on, + * the generic tty layer will cause uart_close(), which will + * decrement the module use count. + */ + tty->driver_data = info; + info->tty = tty; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + if (uart_alloc_tmpbuf()) + goto fail; + + /* + * 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); + retval = (info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + goto fail; + } + + /* + * Make sure the device is in D0 state. + */ + if (info->state->count == 1) +#ifdef CONFIG_PM + pm_send(info->state->pm, PM_RESUME, (void *)0); +#else + if (info->ops->pm) + info->ops->pm(info->port, 0, 3); +#endif + + /* + * Start up the serial port + */ + retval = uart_startup(info); + if (retval) + goto fail; + + retval = uart_block_til_ready(tty, filp, info); + if (retval) + goto fail; + + if (info->state->count == 1) { + int changed_termios = 0; + + if (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; + changed_termios = 1; + } + +#ifdef CONFIG_SERIAL_CORE_CONSOLE + /* + * Copy across the serial console cflag setting + */ + { + struct console *c = drv->cons; + if (c && c->cflag && c->index == line) { + tty->termios->c_cflag = c->cflag; + c->cflag = 0; + changed_termios = 1; + } + } +#endif + if (changed_termios) + uart_change_speed(info, NULL); + } + + info->session = current->session; + info->pgrp = current->pgrp; + return 0; + +out: + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); +fail: + return retval; +} + +#ifdef CONFIG_PROC_FS + +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; +} + +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]; + u_int status; + int ret; + + 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 (state->info && state->info->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; +} + +/** + * 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) +{ + u_int cflag = CREAD | HUPCL | CLOCAL; + u_int quot; + + /* + * 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; + default: cflag |= B38400; baud = 38400; break; + case 57600: cflag |= B57600; break; + case 115200: cflag |= B115200; break; + case 230400: cflag |= B230400; break; + case 460800: cflag |= B460800; break; + } + + if (bits == 7) + cflag |= CS7; + else + cflag |= CS8; + + switch (parity) { + case 'o': case 'O': + cflag |= PARODD; + /*fall through*/ + case 'e': case 'E': + cflag |= PARENB; + break; + } + + co->cflag = cflag; + quot = (port->uartclk / (16 * baud)); + port->ops->change_speed(port, cflag, 0, quot); + + return 0; +} + +extern void ambauart_console_init(void); +extern void anakin_console_init(void); +extern void clps711xuart_console_init(void); +extern void rs285_console_init(void); +extern void sa1100_rs_console_init(void); +extern void serial8250_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_21285_CONSOLE + rs285_console_init(); +#endif +#ifdef CONFIG_SERIAL_SA1100_CONSOLE + sa1100_rs_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 */ + +#ifdef CONFIG_PM +/* + * Serial port power management. + * + * This is pretty coarse at the moment - either all on or all off. We + * should probably some day do finer power management here some day. + * + * We don't actually save any state; the serial driver has enough + * state held internally to re-setup the port when we come out of D3. + */ +static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstate) +{ + struct uart_port *port = state->port; + struct uart_ops *ops = port->ops; + int running = state->info && + state->info->flags & ASYNC_INITIALIZED; + + if (port->type == PORT_UNKNOWN) + return 0; + +//printk("pm: %08x: %d -> %d, %srunning\n", port->iobase, dev->state, pm_state, running ? "" : "not "); + if (pm_state == 0) { + if (ops->pm) + ops->pm(port, pm_state, oldstate); + if (running) { + ops->set_mctrl(port, 0); + ops->startup(port, state->info); + uart_change_speed(state->info, NULL); + ops->set_mctrl(port, state->info->mctrl); + ops->start_tx(port, 1, 0); + } + + /* + * Re-enable the console device after suspending. + */ + if (state->cons && state->cons->index == port->line) + state->cons->flags |= CON_ENABLED; + } else if (pm_state == 1) { + if (ops->pm) + ops->pm(port, pm_state, oldstate); + } else { + /* + * Disable the console device before suspending. + */ + if (state->cons && state->cons->index == port->line) + state->cons->flags &= ~CON_ENABLED; + + if (running) { + ops->stop_tx(port, 0); + ops->set_mctrl(port, 0); + ops->stop_rx(port); + ops->shutdown(port, state->info); + } + if (ops->pm) + ops->pm(port, pm_state, oldstate); + } + 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; + + switch (rqst) { + case PM_SUSPEND: + case PM_RESUME: + err = uart_pm_set_state(state, (int)data, dev->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 SERIAL_IO_PORT: + printk("I/O 0x%x", port->iobase); + break; + case SERIAL_IO_HUB6: + printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6); + break; + case SERIAL_IO_MEM: + printk("MEM 0x%lx", port->mapbase); + break; + } + printk(" (irq = %d) is a %s\n", port->irq, uart_type(port)); +} + +static void +uart_setup_port(struct uart_driver *drv, struct uart_state *state) +{ + struct uart_port *port = state->port; + int flags = UART_CONFIG_TYPE; + + init_MUTEX(&state->count_sem); + + state->close_delay = 5 * HZ / 10; + state->closing_wait = 30 * HZ; + + port->type = PORT_UNKNOWN; + +#ifdef CONFIG_PM + state->cons = drv->cons; + state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); + if (state->pm) + state->pm->data = state; +#endif + + /* + * If there isn't a port here, don't do anything further. + */ + if (!port->iobase && !port->mapbase) + return; + + /* + * Now do the auto configuration stuff. Note that config_port + * is expected to claim the resources and map the port for us. + */ + if (port->flags & ASYNC_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (port->flags & ASYNC_BOOT_AUTOCONF) + port->ops->config_port(port, flags); + + /* + * Only register this port if it is detected. + */ + if (port->type != PORT_UNKNOWN) { + tty_register_devfs(drv->normal_driver, 0, drv->minor + + state->port->line); + tty_register_devfs(drv->callout_driver, 0, drv->minor + + state->port->line); + uart_report_port(drv, port); + } + +#ifdef CONFIG_PM + /* + * Power down all ports by default, except the console if we have one. + */ + if (state->pm && (!drv->cons || port->line != drv->cons->index)) + pm_send(state->pm, PM_SUSPEND, (void *)3); +#endif +} + +/* + * Register a set of ports with the core driver. Note that we don't + * printk any information about the ports; that is up to the low level + * driver to do if they so wish. + */ +int uart_register_driver(struct uart_driver *drv) +{ + struct tty_driver *normal, *callout; + int i, retval; + + if (drv->state) + panic("drv->state already allocated\n"); + + /* + * 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 = B38400 | 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; + + 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->port = drv->port + i; + state->port->line = i; + + uart_setup_port(drv, state); + } + + retval = tty_register_driver(normal); + if (retval) + goto out; + + retval = tty_register_driver(callout); + if (retval) + tty_unregister_driver(normal); + +out: + if (retval && drv->state) + kfree(drv->state); + return retval; +} + +void uart_unregister_driver(struct uart_driver *drv) +{ + int i; + + for (i = 0; i < drv->nr; i++) { + struct uart_state *state = drv->state + i; + + if (state->info && state->info->tty) + tty_hangup(state->info->tty); + + pm_unregister(state->pm); + + if (state->port->type != PORT_UNKNOWN) + state->port->ops->release_port(state->port); + if (state->info) { + tasklet_kill(&state->info->tlet); + kfree(state->info); + } + } + + tty_unregister_driver(drv->normal_driver); + tty_unregister_driver(drv->callout_driver); + + kfree(drv->state); +} + +static int uart_match_port(struct uart_port *port1, struct uart_port *port2) +{ + if (port1->iotype != port2->iotype) + return 0; + switch (port1->iotype) { + case SERIAL_IO_PORT: return (port1->iobase == port2->iobase); + case SERIAL_IO_MEM: return (port1->membase == port2->membase); + } + return 0; +} + +/** + * uart_register_port: register a port with the generic uart driver + * @reg: pointer to the uart low level driver structure for this port + * @port: uart port structure describing the port + * + * Register a UART with the specified low level driver. Detect the + * type of the port if ASYNC_BOOT_AUTOCONF is set, and detect the IRQ + * if ASYNC_AUTO_IRQ is set. + * + * Returns negative error, or positive line number. + */ +int uart_register_port(struct uart_driver *drv, struct uart_port *port) +{ + struct uart_state *state = NULL; + int i, flags = UART_CONFIG_TYPE; + + /* + * 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. + */ + down(&port_sem); + for (i = 0; i < drv->nr; i++) { + if (uart_match_port(drv->state[i].port, port)) { + down(&drv->state[i].count_sem); + state = &drv->state[i]; + break; + } + } + + /* + * If we didn't find a matching entry, look for the first + * free entry. We look for one which hasn't been previously + * used (indicated by zero iobase). + */ + if (!state) { + for (i = 0; i < drv->nr; i++) { + if (drv->state[i].port->type == PORT_UNKNOWN && + drv->state[i].port->iobase == 0) { + down(&drv->state[i].count_sem); + if (drv->state[i].count == 0) { + state = &drv->state[i]; + break; + } + } + } + } + + /* + * Ok, that also failed. Find the first unused entry, which + * may be previously in use. + */ + if (!state) { + for (i = 0; i < drv->nr; i++) { + if (drv->state[i].port->type == PORT_UNKNOWN) { + down(&drv->state[i].count_sem); + if (drv->state[i].count == 0) { + state = &drv->state[i]; + break; + } + } + } + } + + up(&port_sem); + + if (!state) + return -ENOSPC; + + /* + * 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 (state->count != 0 || + (state->info && state->info->blocked_open != 0)) { + up(&state->count_sem); + return -EBUSY; + } + + /* + * We're holding the lock for this port. Copy the relevant data + * into the port structure. + */ + 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; + +#if 0 //def CONFIG_PM + /* we have already registered the power management handlers */ + state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); + if (state->pm) { + state->pm->data = state; + + /* + * Power down all ports by default, except + * the console if we have one. + */ + if (!drv->cons || state->port->line != drv->cons->index) + pm_send(state->pm, PM_SUSPEND, (void *)3); + } +#endif + + if (state->port->flags & ASYNC_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (state->port->flags & ASYNC_BOOT_AUTOCONF) + state->port->ops->config_port(state->port, flags); + + tty_register_devfs(drv->normal_driver, 0, drv->minor + + state->port->line); + tty_register_devfs(drv->callout_driver, 0, drv->minor + + state->port->line); + + uart_report_port(drv, state->port); + + up(&state->count_sem); + return i; +} + +/* + * Unregister the specified port index on the specified driver. + */ +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(&state->count_sem); + /* + * The port has already gone. We have to hang up the line + * to kill all usage of this port. + */ + if (state->info && state->info->tty) + tty_hangup(state->info->tty); + + /* + * Free the ports resources, if any. + */ + state->port->ops->release_port(state->port); + + /* + * Indicate that there isn't a port here anymore. + */ + state->port->type = PORT_UNKNOWN; + +#if 0 // not yet + /* + * No point in doing power management for hardware that + * isn't present. + */ + pm_unregister(state->pm); +#endif + + /* + * Remove the devices from devfs + */ + tty_unregister_devfs(drv->normal_driver, drv->minor + line); + tty_unregister_devfs(drv->callout_driver, drv->minor + line); + up(&state->count_sem); +} + +EXPORT_SYMBOL(uart_event); +EXPORT_SYMBOL(uart_register_driver); +EXPORT_SYMBOL(uart_unregister_driver); +EXPORT_SYMBOL(uart_register_port); +EXPORT_SYMBOL(uart_unregister_port); + +static int __init uart_init(void) +{ + return 0; +} + +static void __exit uart_exit(void) +{ + free_page((unsigned long)tmp_buf); + tmp_buf = NULL; +} + +module_init(uart_init); +module_exit(uart_exit); + +MODULE_DESCRIPTION("Serial driver core"); +MODULE_LICENSE("GPL"); diff -Naur linux-2.4.32.orig/drivers/serial/sa1100.c linux-2.4.32/drivers/serial/sa1100.c --- linux-2.4.32.orig/drivers/serial/sa1100.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/sa1100.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,818 @@ +/* + * 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.3 2001/11/27 17:35:39 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 + +#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(port) __raw_readl((port)->membase + UTCR0) +#define UART_GET_UTCR1(port) __raw_readl((port)->membase + UTCR1) +#define UART_GET_UTCR2(port) __raw_readl((port)->membase + UTCR2) +#define UART_GET_UTCR3(port) __raw_readl((port)->membase + UTCR3) +#define UART_GET_UTSR0(port) __raw_readl((port)->membase + UTSR0) +#define UART_GET_UTSR1(port) __raw_readl((port)->membase + UTSR1) +#define UART_GET_CHAR(port) __raw_readl((port)->membase + UTDR) + +#define UART_PUT_UTCR0(port,v) __raw_writel((v),(port)->membase + UTCR0) +#define UART_PUT_UTCR1(port,v) __raw_writel((v),(port)->membase + UTCR1) +#define UART_PUT_UTCR2(port,v) __raw_writel((v),(port)->membase + UTCR2) +#define UART_PUT_UTCR3(port,v) __raw_writel((v),(port)->membase + UTCR3) +#define UART_PUT_UTSR0(port,v) __raw_writel((v),(port)->membase + UTSR0) +#define UART_PUT_UTSR1(port,v) __raw_writel((v),(port)->membase + UTSR1) +#define UART_PUT_CHAR(port,v) __raw_writel((v),(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 *, struct uart_info *); +static void (*sa1100_close)(struct uart_port *, struct uart_info *); +#ifdef SUPPORT_SYSRQ +static struct console sa1100_console; +#endif + +/* + * interrupts disabled on entry + */ +static void sa1100_stop_tx(struct uart_port *port, u_int from_tty) +{ + u32 utcr3 = UART_GET_UTCR3(port); + UART_PUT_UTCR3(port, utcr3 & ~UTCR3_TIE); + 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, u_int nonempty, u_int from_tty) +{ + if (nonempty) { + unsigned long flags; + u32 utcr3; + + local_irq_save(flags); + utcr3 = UART_GET_UTCR3(port); + port->read_status_mask |= UTSR0_TO_SM(UTSR0_TFS); + UART_PUT_UTCR3(port, utcr3 | UTCR3_TIE); + local_irq_restore(flags); + } +} + +/* + * Interrupts enabled + */ +static void sa1100_stop_rx(struct uart_port *port) +{ + u32 utcr3 = UART_GET_UTCR3(port); + UART_PUT_UTCR3(port, utcr3 & ~UTCR3_RIE); +} + +/* + * No modem control lines + */ +static void sa1100_enable_ms(struct uart_port *port) +{ +} + +static void +sa1100_rx_chars(struct uart_info *info, struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + unsigned int status, ch, flg, ignored = 0; + struct uart_port *port = info->port; + + status = UTSR1_TO_SM(UART_GET_UTSR1(port)) | UTSR0_TO_SM(UART_GET_UTSR0(port)); + while (status & UTSR1_TO_SM(UTSR1_RNE)) { + 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 & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) + goto handle_error; + + if (uart_handle_sysrq_char(info, 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(port)) | UTSR0_TO_SM(UART_GET_UTSR0(port)); + } +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + if (status & UTSR1_TO_SM(UTSR1_PRE)) + port->icount.parity++; + else if (status & UTSR1_TO_SM(UTSR1_FRE)) + port->icount.frame++; + if (status & UTSR1_TO_SM(UTSR1_ROR)) + port->icount.overrun++; + + if (status & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + status &= 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 + info->sysrq = 0; +#endif + goto error_return; +} + +static void sa1100_tx_chars(struct uart_info *info) +{ + struct uart_port *port = info->port; + + 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) { + sa1100_stop_tx(info->port, 0); + return; + } + + /* + * Tried using FIFO (not checking TNF) for fifo fill: + * still had the '4 bytes repeated' problem. + */ + while (UART_GET_UTSR1(port) & UTSR1_TNF) { + 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) + sa1100_stop_tx(info->port, 0); +} + +static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info = dev_id; + struct uart_port *port = info->port; + unsigned int status, pass_counter = 0; + + status = UART_GET_UTSR0(port); + status &= (SM_TO_UTSR0(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(port, UTSR0_RID); + sa1100_rx_chars(info, regs); + } + + /* Clear the relevent break bits */ + if (status & (UTSR0_RBB | UTSR0_REB)) + UART_PUT_UTSR0(port, status & (UTSR0_RBB | UTSR0_REB)); + + if (status & UTSR0_RBB) + port->icount.brk++; + + if (status & UTSR0_REB) { +#ifdef SUPPORT_SYSRQ + if (port->line == sa1100_console.index && + !info->sysrq) { + info->sysrq = jiffies + HZ*5; + } +#endif + } + if (status & UTSR0_TFS) + sa1100_tx_chars(info); + if (pass_counter++ > SA1100_ISR_PASS_LIMIT) + break; + status = UART_GET_UTSR0(port); + status &= (SM_TO_UTSR0(port->read_status_mask) | ~UTSR0_TFS); + } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID)); +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static u_int sa1100_tx_empty(struct uart_port *port) +{ + return UART_GET_UTSR1(port) & UTSR1_TBY ? 0 : TIOCSER_TEMT; +} + +static u_int sa1100_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void sa1100_set_mctrl(struct uart_port *port, u_int mctrl) +{ +} + +/* + * Interrupts always disabled. + */ +static void sa1100_break_ctl(struct uart_port *port, int break_state) +{ + u_int utcr3; + + utcr3 = UART_GET_UTCR3(port); + if (break_state == -1) + utcr3 |= UTCR3_BRK; + else + utcr3 &= ~UTCR3_BRK; + UART_PUT_UTCR3(port, utcr3); +} + +static int sa1100_startup(struct uart_port *port, struct uart_info *info) +{ + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, sa1100_int, 0, "serial_sa1100", info); + if (retval) + return retval; + + /* + * If there is a specific "open" function (to register + * control line interrupts) + */ + if (sa1100_open) { + retval = sa1100_open(port, info); + if (retval) { + free_irq(port->irq, info); + return retval; + } + } + + /* + * Finally, clear and enable interrupts + */ + UART_PUT_UTSR0(port, -1); + UART_PUT_UTCR3(port, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE); + + return 0; +} + +static void sa1100_shutdown(struct uart_port *port, struct uart_info *info) +{ + /* + * Free the interrupt + */ + free_irq(port->irq, info); + + /* + * If there is a specific "close" function (to unregister + * control line interrupts) + */ + if (sa1100_close) + sa1100_close(port, info); + + /* + * Disable all interrupts, port and break condition. + */ + UART_PUT_UTCR3(port, 0); +} + +static void sa1100_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) +{ + unsigned long flags; + u_int utcr0, old_utcr3; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS7: utcr0 = 0; break; + default: utcr0 = UTCR0_DSS; break; + } + if (cflag & CSTOPB) + utcr0 |= UTCR0_SBS; + if (cflag & PARENB) { + utcr0 |= UTCR0_PE; + if (!(cflag & PARODD)) + utcr0 |= UTCR0_OES; + } + + port->read_status_mask &= UTSR0_TO_SM(UTSR0_TFS); + port->read_status_mask |= UTSR1_TO_SM(UTSR1_ROR); + if (iflag & INPCK) + port->read_status_mask |= UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); + if (iflag & IGNBRK) { + 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) + port->ignore_status_mask |= UTSR1_TO_SM(UTSR1_ROR); + } + + /* first, disable interrupts and drain transmitter */ + local_irq_save(flags); + old_utcr3 = UART_GET_UTCR3(port); + UART_PUT_UTCR3(port, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)); + local_irq_restore(flags); + while (UART_GET_UTSR1(port) & UTSR1_TBY); + + /* then, disable everything */ + UART_PUT_UTCR3(port, 0); + + /* set the parity, stop bits and data size */ + UART_PUT_UTCR0(port, utcr0); + + /* set the baud rate */ + quot -= 1; + UART_PUT_UTCR1(port, ((quot & 0xf00) >> 8)); + UART_PUT_UTCR2(port, (quot & 0xff)); + + UART_PUT_UTSR0(port, -1); + + UART_PUT_UTCR3(port, old_utcr3); +} + +static const char *sa1100_type(struct uart_port *port) +{ + return port->type == PORT_SA1100 ? "SA1100" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void sa1100_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 sa1100_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, + "serial_sa1100") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void sa1100_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE && sa1100_request_port(port) == 0) + 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) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100) + 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 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 uart_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 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].uartclk = 3686400; + sa1100_ports[i].ops = &sa1100_pops; + sa1100_ports[i].fifosize = 8; + } + + /* + * 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 __FUNCTION__ ": bad index number %d\n", idx); + return; + } + + switch (port) { + case 1: + sa1100_ports[idx].membase = (void *)&Ser1UTCR0; + sa1100_ports[idx].mapbase = _Ser1UTCR0; + sa1100_ports[idx].irq = IRQ_Ser1UART; + sa1100_ports[idx].iotype = SERIAL_IO_MEM; + sa1100_ports[idx].flags = ASYNC_BOOT_AUTOCONF; + break; + + case 2: + sa1100_ports[idx].membase = (void *)&Ser2UTCR0; + sa1100_ports[idx].mapbase = _Ser2UTCR0; + sa1100_ports[idx].irq = IRQ_Ser2ICP; + sa1100_ports[idx].iotype = SERIAL_IO_MEM; + sa1100_ports[idx].flags = ASYNC_BOOT_AUTOCONF; + break; + + case 3: + sa1100_ports[idx].membase = (void *)&Ser3UTCR0; + sa1100_ports[idx].mapbase = _Ser3UTCR0; + sa1100_ports[idx].irq = IRQ_Ser3UART; + sa1100_ports[idx].iotype = SERIAL_IO_MEM; + sa1100_ports[idx].flags = ASYNC_BOOT_AUTOCONF; + break; + + default: + printk(KERN_ERR __FUNCTION__ ": bad port number %d\n", port); + } +} + + +#ifdef CONFIG_SERIAL_SA1100_CONSOLE + +/* + * Interrupts are disabled on entering + */ +static void sa1100_console_write(struct console *co, const char *s, u_int count) +{ + struct uart_port *port = sa1100_ports + co->index; + u_int old_utcr3, status, i; + + /* + * First, save UTCR3 and then disable interrupts + */ + old_utcr3 = UART_GET_UTCR3(port); + UART_PUT_UTCR3(port, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) | UTCR3_TXE); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = UART_GET_UTSR1(port); + } while (!(status & UTSR1_TNF)); + UART_PUT_CHAR(port, s[i]); + if (s[i] == '\n') { + do { + status = UART_GET_UTSR1(port); + } while (!(status & UTSR1_TNF)); + UART_PUT_CHAR(port, '\r'); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore UTCR3 + */ + do { + status = UART_GET_UTSR1(port); + } while (status & UTSR1_TBY); + UART_PUT_UTCR3(port, old_utcr3); +} + +static kdev_t sa1100_console_device(struct console *co) +{ + return MKDEV(SERIAL_SA1100_MAJOR, MINOR_START + co->index); +} + +static int sa1100_console_wait_key(struct console *co) +{ + struct uart_port *port = sa1100_ports + co->index; + unsigned long flags; + u_int old_utcr3, status, ch; + + /* + * Save UTCR3 and disable interrupts + */ + save_flags(flags); + cli(); + old_utcr3 = UART_GET_UTCR3(port); + UART_PUT_UTCR3(port, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)); + restore_flags(flags); + + /* + * Wait for a character + */ + do { + status = UART_GET_UTSR1(port); + } while (!(status & UTSR1_RNE)); + ch = UART_GET_CHAR(port); + + /* + * Restore UTCR3 + */ + UART_PUT_UTCR3(port, old_utcr3); + + return ch; +} + +/* + * 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 uart_port *port, int *baud, int *parity, int *bits) +{ + u_int utcr3; + + utcr3 = UART_GET_UTCR3(port) & (UTCR3_RXE | UTCR3_TXE); + if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) { + /* ok, the port was enabled */ + u_int utcr0, quot; + + utcr0 = UART_GET_UTCR0(port); + + *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(port) | UART_GET_UTCR1(port) << 8; + quot &= 0xfff; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init +sa1100_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + 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. + */ + port = uart_get_console(sa1100_ports, NR_PORTS, co); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + sa1100_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console sa1100_console = { + name: "ttySA", + write: sa1100_console_write, + device: sa1100_console_device, + wait_key: sa1100_console_wait_key, + 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, + port: sa1100_ports, + cons: SA1100_CONSOLE, +}; + +static int __init sa1100_serial_init(void) +{ + sa1100_init_ports(); + return uart_register_driver(&sa1100_reg); +} + +static void __exit sa1100_serial_exit(void) +{ + uart_unregister_driver(&sa1100_reg); +} + +module_init(sa1100_serial_init); +module_exit(sa1100_serial_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("SA1100 generic serial port driver"); +MODULE_LICENSE("GPL"); + diff -Naur linux-2.4.32.orig/drivers/serial/serial_21285.c linux-2.4.32/drivers/serial/serial_21285.c --- linux-2.4.32.orig/drivers/serial/serial_21285.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/serial_21285.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,557 @@ +/* + * 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: serial_21285.c,v 1.4 2001/10/14 20:10:03 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) + +#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 + +#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 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) { + 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; + + 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"); + +#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; + + 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; +} + +#ifdef CONFIG_SERIAL_21285_OLD +static struct console rs285_old_cons = +{ + SERIAL_21285_OLD_NAME, + rs285_console_write, + NULL, + rs285_console_device, + NULL, + rs285_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; +#endif + +static struct console rs285_cons = +{ + name: SERIAL_21285_NAME, + write: rs285_console_write, + device: rs285_console_device, + setup: rs285_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +void __init rs285_console_init(void) +{ +#ifdef CONFIG_SERIAL_21285_OLD + if (!machine_is_ebsa285() && !machine_is_netwinder()) + register_console(&rs285_old_cons); +#endif + register_console(&rs285_cons); +} + +#endif /* CONFIG_SERIAL_21285_CONSOLE */ + +EXPORT_NO_SYMBOLS; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver"); diff -Naur linux-2.4.32.orig/drivers/serial/serial_8250.c linux-2.4.32/drivers/serial/serial_8250.c --- linux-2.4.32.orig/drivers/serial/serial_8250.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/serial_8250.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,1931 @@ +/* + * linux/drivers/char/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: serial_8250.c,v 1.14.2.6 2002/02/04 17:51:22 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 "serial_8250.h" + +/* + * 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_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS 1 +#endif +#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 + +#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]; +#ifdef CONFIG_SERIAL_8250_CONSOLE +static struct console serial8250_console; +static unsigned int lsr_break_flag; +#endif +static struct uart_info *IRQ_ports[NR_IRQS]; + +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; + +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif /* CONFIG_SERIAL_RSA */ + +#define port_acr unused[0] /* 8bit */ +#define port_ier unused[1] /* 8bit */ +#define port_rev unused[2] /* 8bit */ +#define port_lcr unused[3] /* 8bit */ + +/* + * 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_port *port, int offset) +{ + offset <<= port->regshift; + + switch (port->iotype) { +#ifdef CONFIG_SERIAL_8250_HUB6 + case SERIAL_IO_HUB6: + outb(port->hub6 - 1 + offset, port->iobase); + return inb(port->iobase + 1); +#endif + + case SERIAL_IO_MEM: + return readb((unsigned long)port->membase + offset); + + default: + return inb(port->iobase + offset); + } +} + +static _INLINE_ void +serial_out(struct uart_port *port, int offset, int value) +{ + offset <<= port->regshift; + + switch (port->iotype) { +#ifdef CONFIG_SERIAL_8250_HUB6 + case SERIAL_IO_HUB6: + outb(port->hub6 - 1 + offset, port->iobase); + outb(value, port->iobase + 1); + break; +#endif + + case SERIAL_IO_MEM: + writeb(value, (unsigned long)port->membase + offset); + break; + + default: + outb(value, 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(port, offset) serial_in(port, offset) +#define serial_outp(port, offset, value) serial_out(port, offset, value) + + +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_port *port, int offset, int value) +{ + serial_out(port, UART_SCR, offset); + serial_out(port, UART_ICR, value); +} + +static unsigned int serial_icr_read(struct uart_port *port, int offset) +{ + unsigned int value; + + serial_icr_write(port, UART_ACR, port->port_acr | UART_ACR_ICRRD); + serial_out(port, UART_SCR, offset); + value = serial_in(port, UART_ICR); + serial_icr_write(port, UART_ACR, port->port_acr); + + return value; +} + +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +static int enable_rsa(struct uart_port *port) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(port, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(port, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(port, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + restore_flags(flags); + return result; +} + +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +static int disable_rsa(struct uart_port *port) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(port, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(port, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(port, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + restore_flags(flags); + return result; +} +#endif /* CONFIG_SERIAL_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_port *port) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(port, UART_FCR); + old_mcr = serial_inp(port, UART_MCR); + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(port, UART_MCR, UART_MCR_LOOP); + serial_outp(port, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(port, UART_DLL); + old_dlm = serial_inp(port, UART_DLM); + serial_outp(port, UART_DLL, 0x01); + serial_outp(port, UART_DLM, 0x00); + serial_outp(port, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(port, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(port, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(port, UART_RX); + serial_outp(port, UART_FCR, old_fcr); + serial_outp(port, UART_MCR, old_mcr); + serial_outp(port, UART_LCR, UART_LCR_DLAB); + serial_outp(port, UART_DLL, old_dll); + serial_outp(port, UART_DLM, old_dlm); + + return count; +} + +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void +autoconfig_startech_uarts(struct uart_port *port) +{ + unsigned char scratch, scratch2, scratch3, scratch4; + + /* + * First we check to see if it's an Oxford Semiconductor UART. + * + * If we have to do this here because some non-National + * Semiconductor clone chips lock up if you try writing to the + * LSR register (which serial_icr_read does) + */ + if (port->type == PORT_16550A) { + /* + * 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). + */ + port->port_acr = 0; + serial_out(port, UART_LCR, 0xBF); + serial_out(port, UART_EFR, 0x10); + serial_out(port, UART_LCR, 0x00); + /* Check for Oxford Semiconductor 16C950 */ + scratch = serial_icr_read(port, UART_ID1); + scratch2 = serial_icr_read(port, UART_ID2); + scratch3 = serial_icr_read(port, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + port->type = PORT_16C950; + port->port_rev = serial_icr_read(port, UART_REV) | + (scratch3 << 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. + */ + + /* Save the DLL and DLM */ + + serial_outp(port, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(port, UART_DLL); + scratch4 = serial_inp(port, UART_DLM); + + serial_outp(port, UART_DLL, 0); + serial_outp(port, UART_DLM, 0); + scratch2 = serial_inp(port, UART_DLL); + scratch = serial_inp(port, UART_DLM); + serial_outp(port, UART_LCR, 0); + + if (scratch == 0x10 || scratch == 0x12 || scratch == 0x14) { + if (scratch == 0x10) + port->port_rev = scratch2; + port->type = PORT_16850; + return; + } + + /* Restore the DLL and DLM */ + + serial_outp(port, UART_LCR, UART_LCR_DLAB); + serial_outp(port, UART_DLL, scratch3); + serial_outp(port, UART_DLM, scratch4); + serial_outp(port, UART_LCR, 0); + + /* + * We distinguish between the '654 and the '650 by counting + * how many bytes are in the FIFO. I'm using this for now, + * since that's the technique that was sent to me in the + * serial driver update, but I'm not convinced this works. + * I've had problems doing this in the past. -TYT + */ + if (size_fifo(port) == 64) + port->type = PORT_16654; + else + port->type = PORT_16650V2; +} + +/* + * This routine is called by rs_init() to initialize a specific serial + * port. It determines what type of UART chip this serial port is + * using: 8250, 16450, 16550, 16550A. The important question is + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. + */ +static void autoconfig(struct uart_port *port, unsigned int probeflags) +{ + unsigned char status1, status2, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyS%d (0x%04x, 0x%08lx)...\n", + port->line, port->iobase, port->membase); +#endif + + if (!port->iobase && !port->membase) + return; + + save_flags(flags); cli(); + + if (!(port->flags & ASYNC_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. + */ + scratch = serial_inp(port, UART_IER); + serial_outp(port, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(port, UART_IER); + serial_outp(port, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(port, UART_IER); + serial_outp(port, UART_IER, scratch); + if (scratch2 || scratch3 != 0x0F) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: simple autoconfig failed " + "(%02x, %02x)\n", port->line, + scratch2, scratch3); +#endif + restore_flags(flags); + return; /* We failed; there's nothing here */ + } + } + + save_mcr = serial_in(port, UART_MCR); + save_lcr = serial_in(port, 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 (!(port->flags & ASYNC_SKIP_TEST)) { + serial_outp(port, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(port, UART_MSR) & 0xF0; + serial_outp(port, UART_MCR, save_mcr); + if (status1 != 0x90) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: no UART loopback failed\n", + port->line); +#endif + restore_flags(flags); + return; + } + } + serial_outp(port, UART_LCR, 0xBF); /* set up for StarTech test */ + serial_outp(port, UART_EFR, 0); /* EFR is the same as FCR */ + serial_outp(port, UART_LCR, 0); + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(port, UART_IIR) >> 6; + switch (scratch) { + case 0: + port->type = PORT_16450; + break; + case 1: + port->type = PORT_UNKNOWN; + break; + case 2: + port->type = PORT_16550; + break; + case 3: + port->type = PORT_16550A; + break; + } + if (port->type == PORT_16550A) { + /* Check for Startech UART's */ + serial_outp(port, UART_LCR, UART_LCR_DLAB); + if (serial_in(port, UART_EFR) == 0) { + port->type = PORT_16650; + } else { + serial_outp(port, UART_LCR, 0xBF); + if (serial_in(port, UART_EFR) == 0) + autoconfig_startech_uarts(port); + } + } + if (port->type == PORT_16550A) { + /* Check for TI 16750 */ + serial_outp(port, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(port, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(port, UART_IIR) >> 5; + if (scratch == 7) { + /* + * If this is a 16750, and not a cheap UART + * clone, then it should only go into 64 byte + * mode if the UART_FCR7_64BYTE bit was set + * while UART_LCR_DLAB was latched. + */ + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(port, UART_LCR, 0); + serial_outp(port, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(port, UART_IIR) >> 5; + if (scratch == 6) + port->type = PORT_16750; + } + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + } +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + /* + * Only probe for RSA ports if we got the region. + */ + if (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] != port->iobase) || + check_region(port->iobase + UART_RSA_BASE, 16)) && + (force_rsa[i] != port->iobase)) + continue; + if (!enable_rsa(port)) + continue; + port->type = PORT_RSA; + port->uartclk = SERIAL_RSA_BAUD_BASE * 16; + break; + } + } +#endif + serial_outp(port, UART_LCR, save_lcr); + if (port->type == PORT_16450) { + scratch = serial_in(port, UART_SCR); + serial_outp(port, UART_SCR, 0xa5); + status1 = serial_in(port, UART_SCR); + serial_outp(port, UART_SCR, 0x5a); + status2 = serial_in(port, UART_SCR); + serial_outp(port, UART_SCR, scratch); + + if ((status1 != 0xa5) || (status2 != 0x5a)) + port->type = PORT_8250; + } + port->fifosize = uart_config[port->type].dfl_xmit_fifo_size; + + if (port->type == PORT_UNKNOWN) { + restore_flags(flags); + return; + } + +#ifdef CONFIG_SERIAL_RSA + if (port->iobase && port->type == PORT_RSA) { + release_region(port->iobase, 8); + request_region(port->iobase + UART_RSA_BASE, 16, + "serial_rsa"); + } +#endif + + /* + * Reset the UART. + */ +#ifdef CONFIG_SERIAL_RSA + if (port->type == PORT_RSA) + serial_outp(port, UART_RSA_FRR, 0); +#endif + serial_outp(port, UART_MCR, save_mcr); + serial_outp(port, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(port, UART_FCR, 0); + (void)serial_in(port, UART_RX); + serial_outp(port, UART_IER, 0); + + restore_flags(flags); +} + +static void autoconfig_irq(struct uart_port *port) +{ + unsigned char save_mcr, save_ier; + unsigned long irqs; + int irq; + +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned char save_ICP = 0; + unsigned short ICP = 0; + + if (port->flags & ASYNC_FOURPORT) { + ICP = (port->iobase & 0xfe0) | 0x1f; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(port, UART_MCR); + save_ier = serial_inp(port, UART_IER); + serial_outp(port, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(port, UART_MCR, 0); + udelay (10); + if (port->flags & ASYNC_FOURPORT) { + serial_outp(port, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(port, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(port, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(port, UART_LSR); + (void)serial_inp(port, UART_RX); + (void)serial_inp(port, UART_IIR); + (void)serial_inp(port, UART_MSR); + serial_outp(port, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(port, UART_MCR, save_mcr); + serial_outp(port, UART_IER, save_ier); +#ifdef CONFIG_SERIAL_MANY_PORTS + if (port->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); +#endif + port->irq = (irq > 0)? irq : 0; +} + +static void serial8250_stop_tx(struct uart_port *port, u_int from_tty) +{ + if (port->port_ier & UART_IER_THRI) { + port->port_ier &= ~UART_IER_THRI; + serial_out(port, UART_IER, port->port_ier); + } + if (port->type == PORT_16C950) { + port->port_acr |= UART_ACR_TXDIS; + serial_icr_write(port, UART_ACR, port->port_acr); + } +} + +static void serial8250_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) +{ + if (nonempty && !(port->port_ier & UART_IER_THRI)) { + port->port_ier |= UART_IER_THRI; + serial_out(port, UART_IER, port->port_ier); + } + /* + * We only do this from uart_start + */ + if (from_tty && port->type == PORT_16C950) { + port->port_acr &= ~UART_ACR_TXDIS; + serial_icr_write(port, UART_ACR, port->port_acr); + } +} + +static void serial8250_stop_rx(struct uart_port *port) +{ + port->port_ier &= ~UART_IER_RLSI; + port->read_status_mask &= ~UART_LSR_DR; + serial_out(port, UART_IER, port->port_ier); +} + +static void serial8250_enable_ms(struct uart_port *port) +{ + port->port_ier |= UART_IER_MSI; + serial_out(port, UART_IER, port->port_ier); +} + +static _INLINE_ void +receive_chars(struct uart_info *info, int *status, struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + struct uart_port *port = info->port; + unsigned char ch; + int max_count = 256; + + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *)tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(port, UART_RX); + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = TTY_NORMAL; + 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); + 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. + */ + uart_handle_break(info, &serial8250_console); + } else if (*status & UART_LSR_PE) + port->icount.parity++; + else if (*status & UART_LSR_FE) + port->icount.frame++; + if (*status & UART_LSR_OE) + port->icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + *status &= port->read_status_mask; + +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (port->line == serial8250_console.index) { + /* Recover the break flag from console xmit */ + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + if (uart_handle_sysrq_char(info, ch, regs)) + goto ignore_char; + if ((*status & 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(port, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static _INLINE_ void transmit_chars(struct uart_info *info, int *intr_done) +{ + struct uart_port *port = info->port; + int count; + + if (port->x_char) { + serial_outp(port, UART_TX, port->x_char); + port->icount.tx++; + port->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + serial8250_stop_tx(port, 0); + return; + } + + count = port->fifosize; + do { + serial_out(port, UART_TX, 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_event(info, EVT_WRITE_WAKEUP); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (intr_done) + *intr_done = 0; + + if (info->xmit.head == info->xmit.tail) + serial8250_stop_tx(info->port, 0); +} + +static _INLINE_ void check_modem_status(struct uart_info *info) +{ + struct uart_port *port = info->port; + int status; + + status = serial_in(port, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + if (status & UART_MSR_TERI) + port->icount.rng++; + if (status & UART_MSR_DDSR) + port->icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(info, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(info, status & UART_MSR_CTS); + + wake_up_interruptible(&info->delta_msr_wait); + } +} + +/* + * This handles the interrupt from one port. + */ +static inline void +serial8250_handle_port(struct uart_info *info, struct pt_regs *regs) +{ + int status = serial_inp(info->port, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + check_modem_status(info); + if (status & UART_LSR_THRE) + transmit_chars(info, 0); +} + +#ifdef CONFIG_SERIAL_8250_SHARE_IRQ +/* + * This is the serial driver's generic interrupt routine + */ +static void rs_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info, *end_mark = NULL; + int pass_counter = 0; +#ifdef CONFIG_SERIAL_8250_MULTIPORT + int first_multi = 0; + unsigned long port_monitor = rs_multiport[irq].port_monitor; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt(%d)...", irq); +#endif + + info = *(struct uart_info **)dev_id; + if (!info) + return; + +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (port_monitor) + first_multi = inb(port_monitor); +#endif + + do { + if (!info->tty || + (serial_in(info->port, UART_IIR) & UART_IIR_NO_INT)) { + if (!end_mark) + end_mark = info; + goto next; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info->port, UART_IIR)); +#endif + end_mark = NULL; + + serial8250_handle_port(info, regs); + + next: + info = info->next_info; + if (info) + continue; + info = *(struct uart_info **)dev_id; + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs loop break\n"); +#endif + break; /* Prevent infinite loops */ + } + } while (end_mark != info); +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (port_monitor) + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", + info->port->irq, first_multi, inb(port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +#endif + +/* + * This is the serial driver's interrupt routine for a single port + */ +static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info; + int pass_counter = 0; +#ifdef CONFIG_SERIAL_8250_MULTIPORT + int first_multi = 0; + unsigned long port_monitor = rs_multiport[irq].port_monitor; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = *(struct uart_info **)dev_id; + if (!info || !info->tty) + return; + +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (port_monitor) + first_multi = inb(port_monitor); +#endif + + do { + serial8250_handle_port(info, regs); + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs_single loop break.\n"); +#endif + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info->port, UART_IIR)); +#endif + } while (!(serial_in(info->port, UART_IIR) & UART_IIR_NO_INT)); +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (port_monitor) + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", + info->port->irq, first_multi, inb(port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +#ifdef CONFIG_SERIAL_8250_MULTIPORT +/* + * This is the serial driver's interrupt routine for multiport boards + */ +static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_info *info; + int pass_counter = 0; + struct rs_multiport_struct *multi = &rs_multiport[irq]; + int first_multi = 0; + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_multi(%d)...", irq); +#endif + + info = *(struct uart_info **)dev_id; + if (!info) + return; + + if (!multi->port1) { + /* should never happen */ + printk("rs_interrupt_multi: port1 NULL!\n"); + return; + } + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); + + while (1) { + if (!info->tty || + (serial_in(info->port, UART_IIR) & UART_IIR_NO_INT)) + goto next; + + serial8250_handle_port(info, regs); + + next: + info = info->next; + if (info) + continue; + info = *(struct uart_info **)dev_id; + + /* + * The user was a bonehead, and misconfigured their + * multiport info. Rather than lock up the kernel + * in an infinite loop, if we loop too many times, + * print a message and break out of the loop. + */ + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("Misconfigured multiport serial info " + "for irq %d. Breaking out irq loop\n", irq); + break; + } + if (multi->port_monitor) + printk("rs port monitor irq %d: 0x%x, 0x%x\n", + info->port->irq, first_multi, + inb(multi->port_monitor)); + if ((inb(multi->port1) & multi->mask1) != multi->match1) + continue; + if (!multi->port2) + break; + if ((inb(multi->port2) & multi->mask2) != multi->match2) + continue; + if (!multi->port3) + break; + if ((inb(multi->port3) & multi->mask3) != multi->match3) + continue; + if (!multi->port4) + break; + if ((inb(multi->port4) & multi->mask4) != multi->match4) + continue; + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +#endif + +static u_int serial8250_tx_empty(struct uart_port *port) +{ + return serial_in(port, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +} + +static u_int serial8250_get_mctrl(struct uart_port *port) +{ + unsigned long flags; + unsigned char status; + unsigned int ret; + + save_flags(flags); cli(); + status = serial_in(port, UART_MSR); + restore_flags(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, u_int mctrl) +{ + 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; + + serial_out(port, UART_MCR, mcr); +} + +static void serial8250_break_ctl(struct uart_port *port, int break_state) +{ + if (break_state == -1) + port->port_lcr |= UART_LCR_SBC; + else + port->port_lcr &= ~UART_LCR_SBC; + serial_out(port, UART_LCR, port->port_lcr); +} + +static int serial8250_startup(struct uart_port *port, struct uart_info *info) +{ + void (*handler)(int, void *, struct pt_regs *); + unsigned long flags; + int retval; + + if (port->type == PORT_16C950) { + /* Wake up and initialize UART */ + port->port_acr = 0; + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, UART_EFR_ECB); + serial_outp(port, UART_IER, 0); + serial_outp(port, UART_LCR, 0); + serial_icr_write(port, UART_CSR, 0); /* Reset the UART */ + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, UART_EFR_ECB); + serial_outp(port, UART_LCR, 0); + } + +#ifdef CONFIG_SERIAL_RSA + /* + * If this is an RSA port, see if we can kick it up to the + * higher speed clock. + */ + if (port->type == PORT_RSA) { + if (port->uartclk != SERIAL_RSA_BAUD_BASE * 16 && + enable_rsa(port)) + port->uartclk = SERIAL_RSA_BAUD_BASE * 16; + if (port->uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_outp(port, UART_RSA_FRR, 0); + } +#endif + + /* + * Clear the FIFO buffers and disable them. + * (they will be reeanbled in change_speed()) + */ + if (uart_config[port->type].flags & UART_CLEAR_FIFO) { + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(port, UART_FCR, 0); + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(port, UART_LSR); + (void) serial_inp(port, UART_RX); + (void) serial_inp(port, UART_IIR); + (void) serial_inp(port, 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 (!(port->flags & ASYNC_BUGGY_UART) && + (serial_inp(port, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", port->line); + return -ENODEV; + } + + /* + * Allocate the IRQ if necessary + */ + if (port->irq && (!IRQ_ports[port->irq] || + !IRQ_ports[port->irq]->next_info)) { + handler = rs_interrupt_single; + if (IRQ_ports[port->irq]) { +#ifdef CONFIG_SERIAL_8250_SHARE_IRQ + handler = rs_interrupt; + free_irq(port->irq, &IRQ_ports[port->irq]); +#ifdef CONFIG_SERIAL_8250_MULTIPORT + if (rs_multiport[port->irq].port1) + handler = serial8250_interrupt_multi; +#endif +#else + return -EBUSY; +#endif /* CONFIG_SERIAL_8250_SHARE_IRQ */ + } + + retval = request_irq(port->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[port->irq]); + if (retval) + return retval; + } + + /* + * Insert serial port into IRQ chain. + */ + info->next_info = IRQ_ports[port->irq]; + IRQ_ports[port->irq] = info; + + /* + * Now, initialize the UART + */ + serial_outp(port, UART_LCR, UART_LCR_WLEN8); + +#ifdef CONFIG_SERIAL_MANY_PORTS + if (port->flags & ASYNC_FOURPORT) { + if (port->irq == 0) + info->mctrl |= TIOCM_OUT1; + } else +#endif + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + if (port->irq != 0) + info->mctrl |= TIOCM_OUT2; + + /* FIXME: ALPHA_KLUDGE_MCR; */ + serial8250_set_mctrl(port, info->mctrl); + + /* + * 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. + */ + port->port_ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(port, UART_IER, port->port_ier); + +#ifdef CONFIG_SERIAL_MANY_PORTS + if (port->flags & ASYNC_FOURPORT) { + unsigned int ICP; + /* + * Enable interrupts on the AST Fourport board + */ + ICP = (port->iobase & 0xfe0) | 0x01f; + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + + /* + * And clear the interrupt registers again for luck. + */ + (void) serial_inp(port, UART_LSR); + (void) serial_inp(port, UART_RX); + (void) serial_inp(port, UART_IIR); + (void) serial_inp(port, UART_MSR); + + return 0; +} + +static void serial8250_shutdown(struct uart_port *port, struct uart_info *info) +{ + struct uart_info **infop; + unsigned long flags; + int retval; + + /* + * First, disable all intrs from the port. + */ + port->port_ier = 0; + serial_outp(port, UART_IER, 0); + + synchronize_irq(); + + /* + * unlink the serial port from the IRQ chain... + */ + for (infop = &IRQ_ports[port->irq]; *infop; infop = &(*infop)->next_info) + if (*infop == info) + break; + + if (*infop == info) + *infop = info->next_info; + + /* + * Free the IRQ, if necessary + */ + if (port->irq && (!IRQ_ports[port->irq] || + !IRQ_ports[port->irq]->next_info)) { + free_irq(port->irq, &IRQ_ports[port->irq]); + if (IRQ_ports[port->irq]) { + retval = request_irq(port->irq, rs_interrupt_single, + SA_SHIRQ, "serial", &IRQ_ports[port->irq]); + if (retval) + printk("serial shutdown: request_irq: error %d" + " couldn't reacquire IRQ.\n", retval); + } + } + +#ifdef CONFIG_SERIAL_MANY_PORTS + if (port->flags & ASYNC_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((port->iobase & 0xfe0) | 0x1f); + info->mctrl |= TIOCM_OUT1; + } else +#endif + info->mctrl &= ~TIOCM_OUT2; + + /* FIXME: ALPHA_KLUDGE_MCR; */ + serial8250_set_mctrl(port, info->mctrl); + + /* + * Disable break condition and FIFOs + */ + serial_out(port, UART_LCR, serial_inp(port, UART_LCR) & ~UART_LCR_SBC); + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_outp(port, UART_FCR, 0); + +#ifdef CONFIG_SERIAL_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + if (port->type == PORT_RSA && + port->uartclk == SERIAL_RSA_BAUD_BASE * 16 && + disable_rsa(port)) + port->uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; +#endif + + /* + * Read data port to reset things + */ + (void) serial_in(port, UART_RX); +} + +static void serial8250_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) +{ + 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 && port->type == PORT_16C950 && + port->port_rev == 0x5201) + quot ++; + + if (uart_config[port->type].flags & UART_USE_FIFO) { + if ((port->uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_RSA + else if (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 (port->type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (iflag & IGNPAR) + port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (iflag & IGNBRK) { + 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) + port->ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + port->port_ier &= ~UART_IER_MSI; + if (port->flags & ASYNC_HARDPPS_CD || cflag & CRTSCTS || + !(cflag & CLOCAL)) + port->port_ier |= UART_IER_MSI; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + save_flags(flags); cli(); + serial_out(port, UART_IER, port->port_ier); + + if (uart_config[port->type].flags & UART_STARTECH) { + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0); + } + serial_outp(port, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ + serial_outp(port, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(port, UART_DLM, quot >> 8); /* MS of divisor */ + if (port->type == PORT_16750) + serial_outp(port, UART_FCR, fcr); /* set fcr */ + serial_outp(port, UART_LCR, cval); /* reset DLAB */ + port->port_lcr = cval; /* Save LCR */ + if (port->type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(port, UART_FCR, fcr); /* set fcr */ + } + restore_flags(flags); +} + +static void serial8250_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + if (state) { + /* sleep */ + if (uart_config[port->type].flags & UART_STARTECH) { + /* Arrange to enter sleep mode */ + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, UART_EFR_ECB); + serial_outp(port, UART_LCR, 0); + serial_outp(port, UART_IER, UART_IERX_SLEEP); + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, 0); + serial_outp(port, UART_LCR, 0); + } + if (port->type == PORT_16750) { + /* Arrange to enter sleep mode */ + serial_outp(port, UART_IER, UART_IERX_SLEEP); + } + } else { + /* wake */ + if (uart_config[port->type].flags & UART_STARTECH) { + /* Wake up UART */ + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, UART_EFR_ECB); + /* + * Turn off LCR == 0xBF so we actually set the IER + * register on the XR16C850 + */ + serial_outp(port, UART_LCR, 0); + serial_outp(port, UART_IER, 0); + /* + * Now reset LCR so we can turn off the ECB bit + */ + serial_outp(port, UART_LCR, 0xBF); + serial_outp(port, UART_EFR, 0); + /* + * For a XR16C850, we need to set the trigger levels + */ + if (port->type == PORT_16850) { + unsigned char fctr; + + fctr = serial_inp(port, UART_FCTR) & + ~(UART_FCTR_RX | UART_FCTR_TX); + serial_outp(port, UART_FCTR, fctr | + UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(port, UART_TRG, UART_TRG_96); + serial_outp(port, UART_FCTR, fctr | + UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(port, UART_TRG, UART_TRG_96); + } + serial_outp(port, UART_LCR, 0); + } + + if (port->type == PORT_16750) { + /* Wake up UART */ + serial_outp(port, 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_port *port, struct resource **res) +{ + unsigned int size = 8 << port->regshift; + int ret = 0; + + switch (port->iotype) { + case SERIAL_IO_MEM: + if (port->mapbase) { + *res = request_mem_region(port->mapbase, size, "serial"); + if (!*res) + ret = -EBUSY; + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + *res = request_region(port->iobase, size, "serial"); + if (!*res) + ret = -EBUSY; + break; + } + return ret; +} + +static int +serial8250_request_rsa_resource(struct uart_port *port, struct resource **res) +{ + unsigned long start, size = 8 << port->regshift; + int ret = 0; + + switch (port->iotype) { + case SERIAL_IO_MEM: + if (port->mapbase) { + start = port->mapbase; + start += UART_RSA_BASE << port->regshift; + *res = request_mem_region(start, size, "serial-rsa"); + if (!*res) + ret = -EBUSY; + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + start = port->iobase; + start += UART_RSA_BASE << 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) +{ + unsigned long start, offset = 0, size = 0; + + if (port->type == PORT_RSA) { + offset = UART_RSA_BASE << port->regshift; + size = 8; + } + + offset <<= port->regshift; + size <<= port->regshift; + + switch (port->iotype) { + case SERIAL_IO_MEM: + if (port->mapbase) { + /* + * Unmap the area. + */ + iounmap(port->membase); + port->membase = NULL; + + start = port->mapbase; + + if (size) + release_mem_region(start + offset, size); + release_mem_region(start, 8 << port->regshift); + } + break; + + case SERIAL_IO_HUB6: + case SERIAL_IO_PORT: + start = port->iobase; + + if (size) + release_region(start + offset, size); + release_region(start + offset, 8 << port->regshift); + break; + + default: + break; + } +} + +static int serial8250_request_port(struct uart_port *port) +{ + struct resource *res = NULL, *res_rsa = NULL; + int ret = -EBUSY; + + if (port->type == PORT_RSA) { + ret = serial8250_request_rsa_resource(port, &res_rsa); + if (ret) + return ret; + } + + ret = serial8250_request_std_resource(port, &res); + + /* + * If we have a mapbase, then request that as well. + */ + if (res != NULL && port->iotype == SERIAL_IO_MEM && + port->mapbase) { + int size = res->end - res->start + 1; + + port->membase = ioremap(port->mapbase, size); + if (!port->membase) + ret = -ENOMEM; + } + + if (ret) { + 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 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 (port->flags & ASYNC_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. + */ + ret = serial8250_request_std_resource(port, &res_std); + if (ret) + return; + + ret = serial8250_request_rsa_resource(port, &res_rsa); + if (ret) + probeflags &= ~PROBE_RSA; + + if (flags & UART_CONFIG_TYPE) + autoconfig(port, probeflags); + if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(port); + + /* + * If the port wasn't an RSA port, release the resource. + */ + if (port->type != PORT_RSA && res_rsa) + release_resource(res_rsa); + + if (port->type == PORT_UNKNOWN) + 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 >= PORT_MAX_8250) + 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_port serial8250_ports[UART_NR]; + +static void __init serial8250_isa_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < ARRAY_SIZE(old_serial_port); i++) { + serial8250_ports[i].iobase = old_serial_port[i].port; + serial8250_ports[i].irq = irq_cannonicalize(old_serial_port[i].irq); + serial8250_ports[i].uartclk = old_serial_port[i].base_baud * 16; + serial8250_ports[i].flags = old_serial_port[i].flags; + serial8250_ports[i].ops = &serial8250_pops; + } +} + +#ifdef CONFIG_SERIAL_8250_CONSOLE +#ifdef used_and_not_const_char_pointer +static int serial8250_console_read(struct uart_port *port, char *s, u_int count) +{ +} +#endif + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int status, tmout = 1000000; + + do { + status = serial_in(port, UART_LSR); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait for flow control if necessary */ + if (port->flags & ASYNC_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(port, UART_MSR) & UART_MSR_CTS) == 0)); + } +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void serial8250_console_write(struct console *co, const char *s, u_int count) +{ + struct uart_port *port = serial8250_ports + co->index; + unsigned int ier; + int i; + + /* + * First save the UER then disable the interrupts + */ + ier = serial_in(port, UART_IER); + serial_out(port, UART_IER, 0); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(port); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(port, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(port); + serial_out(port, UART_TX, 13); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(port); + serial_out(port, 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. + */ + port = uart_get_console(serial8250_ports, UART_NR, co); + + 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, +#ifdef used_and_not_const_char_pointer + read: serial8250_console_read, +#endif + 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: ARRAY_SIZE(old_serial_port), + port: serial8250_ports, + cons: SERIAL8250_CONSOLE, +}; + +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +int register_serial(struct serial_struct *req) +{ + 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 | ASYNC_BOOT_AUTOCONF; + + if (HIGH_BITS_OFFSET) + port.iobase |= 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); +} + +void unregister_serial(int line) +{ + uart_unregister_port(&serial8250_reg, line); +} + +static int __init serial8250_init(void) +{ + serial8250_isa_init_ports(); + return uart_register_driver(&serial8250_reg); +} + +static void __exit serial8250_exit(void) +{ + uart_unregister_driver(&serial8250_reg); +} + +module_init(serial8250_init); +module_exit(serial8250_exit); + +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); + diff -Naur linux-2.4.32.orig/drivers/serial/serial_8250.h linux-2.4.32/drivers/serial/serial_8250.h --- linux-2.4.32.orig/drivers/serial/serial_8250.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/serial_8250.h 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,101 @@ +/* + * linux/drivers/char/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: serial_8250.h,v 1.1.1.1 2001/07/08 22:07:04 rmk Exp $ + */ + +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); + +struct old_serial_port { + unsigned int uart; + unsigned int base_baud; + unsigned int port; + unsigned int irq; + unsigned int flags; +}; + +#undef SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +#ifdef CONFIG_PCI +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#endif + +#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +#ifndef ENABLE_SERIAL_PNP +#define ENABLE_SERIAL_PNP +#endif +#endif + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_PCI +#undef SERIAL_DEBUG_AUTOCONF + +/* Sanity checks */ + +#ifdef CONFIG_SERIAL_MULTIPORT +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef CONFIG_HUB6 +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef MODULE +#undef CONFIG_SERIAL_CONSOLE +#endif + +#define CONFIG_SERIAL_RSA + +#define RS_ISR_PASS_LIMIT 256 + +#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) + + diff -Naur linux-2.4.32.orig/drivers/serial/serial_8250_pci.c linux-2.4.32/drivers/serial/serial_8250_pci.c --- linux-2.4.32.orig/drivers/serial/serial_8250_pci.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32/drivers/serial/serial_8250_pci.c 2006-03-19 21:31:57.000000000 +0000 @@ -0,0 +1,1081 @@ +/* + * 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: serial_8250_pci.c,v 1.8 2001/11/14 23:48:43 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 "serial_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_c