diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/config.in uClinux-2.4.22-uc1-smc/arch/armnommu/config.in --- uClinux-2.4.22-uc1-virgin/arch/armnommu/config.in 2004-01-05 14:25:19.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/config.in 2004-05-02 17:36:56.000000000 +0200 @@ -315,6 +315,7 @@ S3C4530-HEI CONFIG_BOARD_EVS3C4530HEI \ S3C2500-REF-RGP CONFIG_BOARD_S3C2500REFRGP \ SMDK2500 CONFIG_BOARD_SMDK2500 \ + SMC7004ABRV2 CONFIG_BOARD_SMC7004ABRV2 \ S3C4510-SNDS100 CONFIG_BOARD_SNDS100" S3C4510-SNDS100 fi @@ -380,6 +381,21 @@ define_hex FLASH1_SIZE 0x00200000 fi +if [ "$CONFIG_BOARD_SMC7004ABRV2" = "y" ]; then + define_string CONFIG_CPU_NAME "S5N8947" + define_bool CONFIG_NO_PGT_CACHE y + define_bool CONFIG_CPU_32 y + define_bool CONFIG_CPU_26 n + define_bool CONFIG_CPU_S5N8947 y + define_bool CONFIG_CPU_ARM710 y + define_bool CONFIG_CPU_WITH_CACHE y + define_bool CONFIG_CPU_WITH_MCR_INSTRUCTION n + define_hex DRAM_BASE 0x01000000 + define_hex DRAM_SIZE 0x00800000 + define_hex FLASH_MEM_BASE 0x00000000 + define_hex FLASH_SIZE 0x00100000 +fi + if [ "$CONFIG_BOARD_SNDS100" = "y" ]; then define_bool CONFIG_NO_PGT_CACHE y define_bool CONFIG_CPU_32 y @@ -550,6 +566,7 @@ "$CONFIG_ARCH_NETARM" = "y" -o \ "$CONFIG_ARCH_SAMSUNG" = "y" -o \ "$CONFIG_ARCH_TA7S" = "y" -o \ + "$CONFIG_BOARD_SMC7004ABRV2" = "y" -o \ "$CONFIG_ARCH_TA7V" = "y" ]; then bool 'Compiled-in Kernel Boot Parameter' CONFIG_CMDLINE_BOOL if [ "$CONFIG_CMDLINE_BOOL" = "y" ]; then diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/kernel/entry-armv.S uClinux-2.4.22-uc1-smc/arch/armnommu/kernel/entry-armv.S --- uClinux-2.4.22-uc1-virgin/arch/armnommu/kernel/entry-armv.S 2003-10-01 16:42:47.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/kernel/entry-armv.S 2004-05-02 17:36:56.000000000 +0200 @@ -16,6 +16,7 @@ * added some NET+ARM code by Joe deBlaquiere --rp * added S3C4510 code by Mac Wang * added S3C4530 code by Arcturus Networks Inc. + * added S5N8947 code by Petr Novak * */ #include @@ -870,7 +871,7 @@ .byte 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 .endm -#elif defined(CONFIG_CPU_S3C4510) || defined(CONFIG_CPU_S3C4530) +#elif defined(CONFIG_CPU_S3C4510) || defined(CONFIG_CPU_S3C4530) || defined(CONFIG_CPU_S5N8947) .macro disable_fiq .endm @@ -1373,6 +1374,12 @@ bic r13, r13, #MODE_MASK @ preserve F and T bits orr r13, r13, #MODE_SVC|I_BIT msr spsr_c, r13 @ switch to SVC_32 mode +#if defined(CONFIG_BOARD_SMC7004ABRV2) +/* The stub in SMC7004ABR V2 flash uses the IRQ mode stack (2 words), so we need to set it up for the next time + we will be entering the IRQ mode. +*/ + ldr r13, .LCsirq +#endif and lr, lr, #15 ldr lr, [pc, lr, lsl #2] @@ -1417,6 +1424,12 @@ bic r13, r13, #MODE_MASK @ preserve F and T bits orr r13, r13, #I_BIT | MODE_SVC msr spsr_c, r13 @ switch to SVC_32 mode +#if defined(CONFIG_BOARD_SMC7004ABRV2) +/* The stub in SMC7004ABR V2 flash uses the ABT mode stack (2 words), so we need to set it up for the next time + we will be entering the IRQ mode. +*/ + ldr r13, .LCsabt +#endif and lr, lr, #15 ldr lr, [pc, lr, lsl #2] @@ -1461,6 +1474,12 @@ bic r13, r13, #MODE_MASK @ preserve F and T bits orr r13, r13, #I_BIT | MODE_SVC msr spsr_c, r13 @ switch to SVC_32 mode +#if defined(CONFIG_BOARD_SMC7004ABRV2) +/* The stub in SMC7004ABR V2 flash uses the ABT mode stack (2 words), so we need to set it up for the next time + we will be entering the IRQ mode. +*/ + ldr r13, .LCsabt +#endif ands lr, lr, #15 ldr lr, [pc, lr, lsl #2] @@ -1504,6 +1523,12 @@ bic r13, r13, #MODE_MASK @ preserve F and T bits orr r13, r13, #I_BIT | MODE_SVC msr spsr_c, r13 @ switch to SVC_32 mode +#if defined(CONFIG_BOARD_SMC7004ABRV2) +/* The stub in SMC7004ABR V2 flash uses the ABT mode stack (2 words), so we need to set it up for the next time + we will be entering the IRQ mode. +*/ + ldr r13, .LCsund +#endif and lr, lr, #15 ldr lr, [pc, lr, lsl #2] @@ -1539,6 +1564,7 @@ * get out of that mode without clobbering one register. */ ENTRY(vector_FIQ) +fiq_infinite: b fiq_infinite @ FIXME !!! disable_fiq subs pc, lr, #4 @@ -1600,6 +1626,50 @@ ldmia r1, {r1 - r8} @ Read our ISR vectors stmia r0, {r1 - r8} @ Write vectors in special RAM table. ldmfd sp!, {r4 - r9, pc} +#elif defined(CONFIG_BOARD_SMC7004ABRV2) + /* The SMC7004ABRV2 has flash mapped to 0x0 and the RAM starts at + 0x01000000 the bootloader contains stubs which call real + vector handlers via a table at the start of the RAM. + So, instead of placing 'b' instructions at the address 0x0, we + place just the handler addresses at the start of RAM at 0x01000000. + We therefore ignore the value in r0, which should be 0 for + our architecture. */ + +.LCvectors: .word 0 + .word vector_undefinstr + .word vector_swi + .word vector_prefetch + .word vector_data + .word vector_addrexcptn + .word vector_IRQ + .word vector_FIQ + +ENTRY(__trap_init) + stmfd sp !,{r4 - r9, lr} @ Save link register + + mrs r0, cpsr + bic r1, r0, #MODE_MASK + + orr r2, r1, #I_BIT|F_BIT|MODE_IRQ + msr cpsr, r2 + ldr r13, .LCsirq + + orr r2, r1, #I_BIT|F_BIT|0x17 @ ABT_MODE + msr cpsr, r2 + ldr r13, .LCsabt + + orr r2, r1, #I_BIT|F_BIT|0x1b @ UND_MODE + msr cpsr, r2 + ldr r13, .LCsund + + msr cpsr, r0 + + mov r0, #0x01000000 @ addr of RAM based vector table. + adr r1, .LCvectors + ldmia r1, {r1 - r8} @ Read our ISR vectors + stmia r0, {r1 - r8} @ Write vectors in special RAM table. + ldmfd sp!, {r4 - r9, pc} + #else /* Normal case: lower memory is writable. */ .equ __real_stubs_start, .LCvectors + 0x200 @@ -1749,14 +1819,35 @@ /* * Do not reorder these, and do not insert extra data between... + * (well, I had to, sorry.... P.Novak */ +#if defined(CONFIG_BOARD_SMC7004ABRV2) + .word 0 @ this is for double safety, should not be ever needed + .word 0 @ this will hold the address of vector_IRQ + .word 0 @ this will hold the saved r0 +#endif + __temp_irq: .word 0 @ saved lr_irq .word 0 @ saved spsr_irq .word -1 @ old_r0 + +#if defined(CONFIG_BOARD_SMC7004ABRV2) + .word 0 @ this is for double safety, should not be ever needed + .word 0 @ this will hold the address of vector_und + .word 0 @ this will hold the saved r0 +#endif + __temp_und: .word 0 @ Saved lr_und .word 0 @ Saved spsr_und .word -1 @ old_r0 + +#if defined(CONFIG_BOARD_SMC7004ABRV2) + .word 0 @ this is for double safety, should not be ever needed + .word 0 @ this will hold the address of abort vector + .word 0 @ this will hold the saved r0 +#endif + __temp_abt: .word 0 @ Saved lr_abt .word 0 @ Saved spsr_abt .word -1 @ old_r0 diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/kernel/head-armv.S uClinux-2.4.22-uc1-smc/arch/armnommu/kernel/head-armv.S --- uClinux-2.4.22-uc1-virgin/arch/armnommu/kernel/head-armv.S 2003-10-01 16:42:47.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/kernel/head-armv.S 2004-05-02 17:36:56.000000000 +0200 @@ -12,6 +12,7 @@ * added S3C4510 code by Mac Wang * added S3C4530 code by Arcturus Networks Inc. * added S3C2500 code by Arcturus Networks Inc. + * added S5N8947 code by Petr Novak * */ #include @@ -141,6 +142,7 @@ orr r12, r12, #5 << 12 __entry: #endif + #if defined(CONFIG_ARCH_L7200) /* * FIXME - No bootloader, so manually set 'r1' with our architecture number. @@ -164,6 +166,8 @@ mov r1, #MACH_TYPE_SMDK2500 #elif defined(CONFIG_BOARD_S3C2500REFRGP) mov r1, #MACH_TYPE_S3C2500REFRGP +#elif defined(CONFIG_BOARD_SMC7004ABRV2) + mov r1, #MACH_TYPE_SMC7004ABRV2 #endif mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode @@ -255,6 +259,41 @@ .long 0x36365036 #endif +#if defined(CONFIG_BOARD_SMC7004ABRV2) + + adr r5, LC0 + ldmia r5, {r5, r6, r8, r9, sp} @ Setup stack + + /* Copy data sections to their new home. */ + + + /* Clear BSS */ + mov r4, #0 +1: cmp r5, r8 + strcc r4, [r5],#4 + bcc 1b + + /* Pretend we know what our processor code is (for arm_id) */ + + ldr r2, S5N8947_PROCESSOR_TYPE + + str r2, [r6] + mov r2, #MACH_TYPE_SMC7004ABRV2 + str r2, [r9] + + mov fp, #0 + b start_kernel + +LC0: .long __bss_start + .long processor_id + .long _end + .long __machine_arch_type + .long init_task_union+8192 + +S5N8947_PROCESSOR_TYPE: + .long 0x89470000 +#endif + #if defined(CONFIG_ARCH_TA7S) || defined(CONFIG_ARCH_TA7V) b a7Start diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/kernel/traps.c uClinux-2.4.22-uc1-smc/arch/armnommu/kernel/traps.c --- uClinux-2.4.22-uc1-virgin/arch/armnommu/kernel/traps.c 2003-09-09 23:32:29.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/kernel/traps.c 2004-05-02 17:36:56.000000000 +0200 @@ -301,10 +301,17 @@ * Dump out the vectors and stub routines. Maybe a better solution * would be to dump them out only if we detect that they are corrupted. */ +#if defined(CONFIG_BOARD_SMC7004ABRV2) + printk(KERN_CRIT "Vectors and ROM handlers:\n"); + dump_mem(vectors, 0xB0); + printk(KERN_CRIT "Indirection pointers:\n"); + dump_mem(0x1000000, 0x1000040); +#else printk(KERN_CRIT "Vectors:\n"); dump_mem(vectors, 0x40); printk(KERN_CRIT "Stubs:\n"); dump_mem(vectors + 0x200, 0x4b8); +#endif set_fs(fs); diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/mach-SMC7004ABRV2/arch.c uClinux-2.4.22-uc1-smc/arch/armnommu/mach-SMC7004ABRV2/arch.c --- uClinux-2.4.22-uc1-virgin/arch/armnommu/mach-SMC7004ABRV2/arch.c 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/mach-SMC7004ABRV2/arch.c 2004-05-02 17:36:56.000000000 +0200 @@ -0,0 +1,25 @@ +/* + * linux/arch/arm/mach-SMC7004ABRV2/arch.c + * + * Architecture specific fixups. This is where any + * parameters in the params struct are fixed up, or + * any additional architecture specific information + * is pulled from the params struct. + */ +#include +#include +#include +#include + +#include +#include +#include +#include + +extern void genarch_init_irq(void); + +MACHINE_START(SMC7004ABRV2, "SMC7004ABRV2") + MAINTAINER("Petr Novak") + BOOT_PARAMS(0x01000c00) + INITIRQ(genarch_init_irq) +MACHINE_END diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/mach-SMC7004ABRV2/irq.c uClinux-2.4.22-uc1-smc/arch/armnommu/mach-SMC7004ABRV2/irq.c --- uClinux-2.4.22-uc1-virgin/arch/armnommu/mach-SMC7004ABRV2/irq.c 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/mach-SMC7004ABRV2/irq.c 2004-05-02 17:36:56.000000000 +0200 @@ -0,0 +1,46 @@ +/* +* linux/arch/armnommu/mach-snds100/irq.c +* 2001 Mac Wang +*/ +#include + +#include +#include +#include +#include +#include + +void s5n8947_mask_irq(unsigned int irq) +{ + INT_DISABLE(irq); +} + +void s5n8947_unmask_irq(unsigned int irq) +{ + INT_ENABLE(irq); +} + +void s5n8947_mask_ack_irq(unsigned int irq) +{ + INT_DISABLE(irq); +} + +void s5n8947_int_init(void) +{ + IntPend = 0x7FFFFF; + IntMode = INT_MODE_IRQ; + INT_ENABLE(INT_GLOBAL); +} + +void s5n8947_gpio_irq_mode(unsigned int irq, unsigned int mode) +{ + register unsigned int shift; + + if (irq < 3) { + shift = 5*irq + 8; + IopCon0 = (IopCon0 & ((~0x1f)< +#include +#include +#include +#include +#include + +unsigned long samsung_gettimeoffset (void) +{ + register unsigned long value = CSR_READ(TCNT0); + return (CLOCK_TICK_RATE-value) / fMCLK; +} + +void samsung_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + do_timer(regs); +} diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/Makefile uClinux-2.4.22-uc1-smc/arch/armnommu/Makefile --- uClinux-2.4.22-uc1-virgin/arch/armnommu/Makefile 2003-10-01 16:42:46.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/Makefile 2004-05-02 17:36:56.000000000 +0200 @@ -214,6 +214,12 @@ MACHINE = snds100 endif +ifeq ($(CONFIG_BOARD_SMC7004ABRV2),y) +#TEXTADDR = 0x01000400 +TEXTADDR = 0x01001000 +MACHINE = SMC7004ABRV2 +endif + ifeq ($(CONFIG_BOARD_EVS3C4530HEI),y) TEXTADDR = 0x00020000 MACHINE = evS3C4530HEI diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/mm/init.c uClinux-2.4.22-uc1-smc/arch/armnommu/mm/init.c --- uClinux-2.4.22-uc1-virgin/arch/armnommu/mm/init.c 2003-10-01 16:42:47.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/mm/init.c 2004-05-02 17:36:57.000000000 +0200 @@ -381,6 +381,9 @@ machine_is_smdk2500() || machine_is_S3C2500REFRGP() ) reserve_bootmem_node(pgdat, 0, __pa(&_stext)); + if (machine_is_SMC7004ABRV2() ) + reserve_bootmem_node(pgdat, PAGE_OFFSET, __pa(&_stext)-PAGE_OFFSET); + /* * These should likewise go elsewhere. They pre-reserve diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/mm/proc-arm6,7.S uClinux-2.4.22-uc1-smc/arch/armnommu/mm/proc-arm6,7.S --- uClinux-2.4.22-uc1-virgin/arch/armnommu/mm/proc-arm6,7.S 2003-10-01 16:42:47.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/mm/proc-arm6,7.S 2004-05-02 17:36:57.000000000 +0200 @@ -427,6 +427,7 @@ .asciz "ARM7TDMI" cpu_s3c3410_manu_name: cpu_s3c4510b_manu_name: +cpu_s5n8947_manu_name: cpu_s3c4530_manu_name: cpu_s3c44b0x_manu_name: .asciz "Samsung" @@ -438,6 +439,8 @@ .asciz "S3C4530A01" cpu_s3c44b0x_name: .asciz "S3C44B0X" +cpu_s5n8947_name: + .asciz "S5N8947" .align .section ".text.init", #alloc, #execinstr @@ -644,6 +647,12 @@ .long cpu_s3c4510b_name .size cpu_s3c4510b_info, . - cpu_s3c4510b_info + .type cpu_s5n8947_info, #object +cpu_s5n8947_info: + .long cpu_s5n8947_manu_name + .long cpu_s5n8947_name + .size cpu_s5n8947_info, . - cpu_s5n8947_info + .type cpu_s3c4530_info, #object cpu_s3c4530_info: .long cpu_s3c4530_manu_name @@ -816,3 +825,15 @@ .long arm7_processor_functions @ info .size __s3c44b0x_proc_info, . - __s3c44b0x_proc_info +__s5n8947_proc_info: + .long 0x89470000 @ cpu_val + .long 0xffff0000 @ cpu_mask + .long 0x00000c1e @ __cpu_mmu_flags + b __arm7_setup @ __cpu_flush + .long cpu_arch_name @ arch_name + .long cpu_elf_name @ elf_name + .long HWCAP_SWP | HWCAP_26BIT @ elf_hwcap + .long cpu_s5n8947_info @ info + .long arm7_processor_functions @ info + .size __s5n8947_proc_info, . - __s5n8947_proc_info + diff -urN uClinux-2.4.22-uc1-virgin/arch/armnommu/tools/mach-types uClinux-2.4.22-uc1-smc/arch/armnommu/tools/mach-types --- uClinux-2.4.22-uc1-virgin/arch/armnommu/tools/mach-types 2003-10-01 16:42:47.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/arch/armnommu/tools/mach-types 2004-05-02 17:36:57.000000000 +0200 @@ -111,3 +111,4 @@ smdk2500 BOARD_SMDK2500 SMDK2500 166 S3C2500REFRGP BOARD_S3C2500REFRGP S3C2500REFRGP 167 S3C44B0X BOARD_MBA44 S3C44B0X 168 +SMC7004ABRV2 BOARD_SMC7004ABRV2 SMC7004ABRV2 111 diff -urN uClinux-2.4.22-uc1-virgin/drivers/char/Config.in uClinux-2.4.22-uc1-smc/drivers/char/Config.in --- uClinux-2.4.22-uc1-virgin/drivers/char/Config.in 2003-12-11 08:13:19.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/char/Config.in 2004-05-02 17:36:57.000000000 +0200 @@ -114,6 +114,13 @@ fi fi +if [ "$CONFIG_BOARD_SMC7004ABRV2" = "y" ]; then + bool 'SMC7004ABRV2 serial port (on SMSC chip) support' CONFIG_SERIAL_SMC7004ABRV2 + if [ "$CONFIG_SERIAL_SMC7004ABRV2" = "y" ]; then + bool ' Support for console on SMC7004ABRV2 serial port' CONFIG_SERIAL_SMC7004ABRV2_CONSOLE + fi +fi + if [ "$CONFIG_BOARD_SNDS100" = "y" ]; then bool 'Samsung serial port support' CONFIG_SERIAL_SAMSUNG if [ "$CONFIG_SERIAL_SAMSUNG" = "y" ]; then diff -urN uClinux-2.4.22-uc1-virgin/drivers/char/Makefile uClinux-2.4.22-uc1-smc/drivers/char/Makefile --- uClinux-2.4.22-uc1-virgin/drivers/char/Makefile 2003-12-11 08:13:19.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/char/Makefile 2004-05-02 17:36:57.000000000 +0200 @@ -235,6 +235,8 @@ obj-$(CONFIG_SERIAL_ATMEL) += serial_atmel.o obj-$(CONFIG_SERIAL_NETARM) += serial_netarm.o obj-$(CONFIG_SERIAL_SAMSUNG) += serial_samsung.o +obj-$(CONFIG_SERIAL_S5N8947) += serial_s5n8947.o +obj-$(CONFIG_SERIAL_SMC7004ABRV2) += serial_smc7004abrv2.o obj-$(CONFIG_SERIAL_S3C2500) += serial_s3c2500.o obj-$(CONFIG_SERIAL_S3C3410) += serial_s3c3410.o obj-$(CONFIG_SERIAL_S3C44B0X) += serial_s3c44b0x.o diff -urN uClinux-2.4.22-uc1-virgin/drivers/char/serial_s5n8947.c uClinux-2.4.22-uc1-smc/drivers/char/serial_s5n8947.c --- uClinux-2.4.22-uc1-virgin/drivers/char/serial_s5n8947.c 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/char/serial_s5n8947.c 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,4619 @@ +/* + * linux/drivers/char/serial.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o + * + * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now + * much more extensible to support other serial cards based on the + * 16450/16550A UART's. Added support for the AST FourPort and the + * Accent Async board. + * + * set_serial_info fixed to set the flags, custom divisor, and uart + * type fields. Fix suggested by Michael K. Johnson 12/12/92. + * + * 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis + * + * 03/96: Modularised by Angelo Haritsis + * + * rs_set_termios fixed to look also for changes of the input + * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. + * Bernd Anhäupl 05/17/96. + * + * 1/97: Extended dumb serial ports are a config option now. + * Saves 4k. Michael A. Griffith + * + * 8/97: Fix bug in rs_set_termios with RTS + * Stanislav V. Voronyi + * + * 3/98: Change the IRQ detection, use of probe_irq_o*(), + * suppress TIOCSERGWILD and TIOCSERSWILD + * Etienne Lorrain + * + * 4/98: Added changes to support the ARM architecture proposed by + * Russell King + * + * 5/99: Updated to include support for the XR16C850 and ST16C654 + * uarts. Stuart MacDonald + * + * 8/99: Generalized PCI support added. Theodore Ts'o + * + * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a + * few races on freeing buffers too. + * Alan Modra + * + * 5/00: Support for the RSA-DV II/S card added. + * Kiyokazu SUTO + * + * 6/00: Remove old-style timer, use timer_list + * Andrew Morton + * + * 7/00: Support Timedia/Sunix/Exsys PCI cards + * + * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT. + * Arnaldo Carvalho de Melo + * + * 10/00: add in optional software flow control for serial console. + * Kanoj Sarcar (Modified by Theodore Ts'o) + * + */ + +//static char *serial_version = "5.05c"; +static char *serial_version = "0.9"; +static char *serial_revdate = "2001-12-27"; + +/* + * Serial driver configuration section. Here are the various options: + * + * CONFIG_HUB6 + * Enables support for the venerable Bell Technologies + * HUB6 card. + * + * CONFIG_SERIAL_MANY_PORTS + * Enables support for ports beyond the standard, stupid + * COM 1/2/3/4. + * + * CONFIG_SERIAL_MULTIPORT + * Enables support for special multiport board support. + * + * CONFIG_SERIAL_SHARE_IRQ + * Enables support for multiple serial ports on one IRQ + * + * CONFIG_SERIAL_DETECT_IRQ + * Enable the autodetection of IRQ on standart ports + * + * SERIAL_PARANOIA_CHECK + * Check the magic number for the async_structure where + * ever possible. + */ + +#include +#include + +#undef SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +#if 0 +/* These defines are normally controlled by the autoconf.h */ +#define CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_DETECT_IRQ +#define CONFIG_SERIAL_MULTIPORT +#define CONFIG_HUB6 +#endif + +#ifdef CONFIG_PCI +#define ENABLE_SERIAL_PCI +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#endif + +#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +#ifndef ENABLE_SERIAL_PNP +#define ENABLE_SERIAL_PNP +#endif +#endif + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#undef SERIAL_DEBUG_PCI +#undef SERIAL_DEBUG_AUTOCONF + +/* Sanity checks */ + +#ifdef CONFIG_SERIAL_MULTIPORT +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef CONFIG_HUB6 +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef MODULE +#undef CONFIG_SERIAL_CONSOLE +#endif + +#define CONFIG_SERIAL_RSA + +#define RS_STROBE_TIME (10*HZ) +#define RS_ISR_PASS_LIMIT 256 + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif + +/* + * End of serial driver configuration section. + */ + +#include + +#include +#ifdef LOCAL_HEADERS +#include "serial_local.h" +#else +#include +#include +//#include +#include +#define LOCAL_VERSTRING "" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= 131343) +#include +#endif +#if (LINUX_VERSION_CODE >= 131336) +#include +#endif +#include +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE +#include +#endif +#ifdef ENABLE_SERIAL_PCI +#include +#endif +#ifdef ENABLE_SERIAL_PNP +#include +#endif +#ifdef CONFIG_MAGIC_SYSRQ +#include +#endif +#ifdef CONFIG_LEDMAN +#include +#endif + +/* + * All of the compatibilty code so we can compile serial.c against + * older kernels is hidden in serial_compat.h + */ +#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ +#include "serial_compat.h" +#endif + +#include +#include +#include +#include + +#ifdef CONFIG_MAC_SERIAL +#define SERIAL_DEV_OFFSET 2 +#else +#define SERIAL_DEV_OFFSET 0 +#endif + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +static char *serial_name = "Samsung S5N8947 Serial driver"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +static struct timer_list serial_timer; + +/* serial subtype definitions */ +#ifndef SERIAL_TYPE_NORMAL +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#endif + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* + * IRQ_timeout - How long the timeout should be for each IRQ + * should be after the IRQ has been active. + */ + +static struct async_struct *IRQ_ports[NR_IRQS]; +#ifdef CONFIG_SERIAL_MULTIPORT +static struct rs_multiport_struct rs_multiport[NR_IRQS]; +#endif +static int IRQ_timeout[NR_IRQS]; +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE +static struct console sercons; +static int lsr_break_flag; +#endif +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +static unsigned long break_pressed; /* break, really ... */ +#endif + +static unsigned detect_uart_irq (struct serial_state * state); +static void autoconfig(struct serial_state * state); +static void change_speed(struct async_struct *info, struct termios *old); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * Here we define the default xmit fifo size used for each type of + * UART + */ +static struct serial_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "cirrus", 1, 0 }, // usurped by cyclades.c + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "Startech", 1, 0}, // usurped by cyclades.c + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "S5N8947", 1, 0}, /* Samsung S5N8947 uart support */ + { 0, 0} +}; +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; + +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif /* CONFIG_SERIAL_RSA */ + + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS // Defined in serial.h +}; + + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) +#define NR_PCI_BOARDS 8 + +static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; + +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_IO) +#endif +#ifndef IS_PCI_REGION_IOMEM +#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_MEM) +#endif +#ifndef PCI_IRQ_RESOURCE +#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +#endif +#ifndef pci_get_subvendor +#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +#define pci_get_subdevice(dev) ((dev)->subsystem_device) +#endif +#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ + +#ifndef PREPARE_FUNC +#define PREPARE_FUNC(dev) (dev->prepare) +#define ACTIVATE_FUNC(dev) (dev->activate) +#define DEACTIVATE_FUNC(dev) (dev->deactivate) +#endif + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) +#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ + kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s) +#else +#define DBG_CNT(s) +#endif + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf; +#ifdef DECLARE_MUTEX +static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + +static inline int serial_paranoia_check(struct async_struct *info, + kdev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null async_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + return CSR_READ(info->port + offset); +} + +static _INLINE_ void serial_out(struct async_struct *info, int offset, + int value) +{ + CSR_WRITE(info->port+offset, value); +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(info, offset) serial_in(info, offset) +#define serial_outp(info, offset, value) serial_out(info, offset, value) + + +/* + * For the 16C950 + */ +/* +void serial_icr_write(struct async_struct *info, int offset, int value) +{ + serial_out(info, UART_SCR, offset); + serial_out(info, UART_ICR, value); +} + +unsigned int serial_icr_read(struct async_struct *info, int offset) +{ + int value; + + serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD); + serial_out(info, UART_SCR, offset); + value = serial_in(info, UART_ICR); + serial_icr_write(info, UART_ACR, info->ACR); + return value; +} +*/ +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rs_stop(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; + + save_flags(flags); cli(); +/* + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + if (info->state->type == PORT_16C950) { + info->ACR |= UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + disable_uart_tx_interrupt(info->line); + restore_flags(flags); +} + +static void rs_start(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_start")) + return; + + save_flags(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf) { +/* + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt(info->line); + } +/* + if (info->state->type == PORT_16C950) { + info->ACR &= ~UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + restore_flags(flags); +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static _INLINE_ void rs_sched_event(struct async_struct *info, + int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(struct async_struct *info, + int *status, struct pt_regs * regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + struct async_icount *icount; + int max_count = 256; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_RX : LEDMAN_COM2_RX); +#endif + + icount = &info->state->icount; + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *) tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(info, UART_RX); + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + // + // For statistics only + // + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + // + // We do the SysRQ and SAK checking + // here because otherwise the break + // may get masked by ignore_status_mask + // or read_status_mask. + // +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (info->line == sercons.index) { + if (!break_pressed) { + break_pressed = jiffies; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + // + // Mask off conditions which should be ignored. + // + *status &= info->read_status_mask; + +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + if (info->line == sercons.index) { + // Recover the break flag from console xmit + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (break_pressed && info->line == sercons.index) { + if (ch != 0 && + time_before(jiffies, break_pressed + HZ*5)) { + handle_sysrq(ch, regs, NULL, NULL); + break_pressed = 0; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if ((*status & info->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((*status & UART_LSR_OE) && + (tty->flip.count < TTY_FLIPBUF_SIZE)) { + // + // Overrun is special, since it's reported + // immediately, and doesn't affect the current + // character + // + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + } +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + ignore_char: +#endif + *status = serial_inp(info, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); +#if (LINUX_VERSION_CODE > 131394) // 2.1.66 + tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif +} + +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) +{ + int count; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_TX : LEDMAN_COM2_TX); +#endif + + if (info->x_char) { + serial_outp(info, UART_TX, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { +/* + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + disable_uart_tx_interrupt(info->line); + return; + } + + count = info->xmit_fifo_size; + do { + serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (intr_done) + *intr_done = 0; + + if (info->xmit.head == info->xmit.tail) { +/* + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + disable_uart_tx_interrupt(info->line); + } +} + +/* +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + // update input line counters + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + icount->cts++; + wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttys%d CD now %s...", info->line, + (status & UART_MSR_DCD) ? "on" : "off"); +#endif + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SERIAL_DEBUG_OPEN + printk("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & UART_MSR_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + } + } +} +*/ +#ifdef CONFIG_SERIAL_SHARE_IRQ +/* + * This is the serial driver's generic interrupt routine + */ +/* +static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + int status; + struct async_struct * info; + int pass_counter = 0; + struct async_struct *end_mark = 0; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + + do { + if (!info->tty || + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { + if (!end_mark) + end_mark = info; + goto next; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + end_mark = 0; + + info->last_active = jiffies; + + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + check_modem_status(info); + if (status & UART_LSR_THRE) + transmit_chars(info, 0); + + next: + info = info->next_port; + if (!info) { + info = IRQ_ports[irq]; + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs loop break\n"); +#endif + break; // Prevent infinite loops + } + continue; + } + } while (end_mark != info); +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +*/ +#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ + + +/* + * This is the serial driver's interrupt routine for a single port + */ +static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) +{ + int status; + int pass_counter = 0; + struct async_struct * info; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info || !info->tty) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + + do { + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); +// 12/27/2001 Mac Wang Samsung 4510 doesn't have modem relate registers +// 12/27/2001 Mac Wang Samsung S5N8947 doesn't have modem relate registers +// check_modem_status(info); + if (status & UART_LSR_THRE) + transmit_chars(info, 0); + 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, UART_IIR)); +#endif + } while (!(status & (UART_LSR_DR | UART_LSR_THRE))); +// } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + info->last_active = jiffies; +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +#ifdef CONFIG_SERIAL_MULTIPORT +/* + * This is the serial driver's for multiport boards + */ +/* +static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs) +{ + int status; + struct async_struct * info; + int pass_counter = 0; + int first_multi= 0; + struct rs_multiport_struct *multi; + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_multi(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info) + return; + multi = &rs_multiport[irq]; + if (!multi->port1) { + // Should never happen + printk("rs_interrupt_multi: NULL port1!\n"); + return; + } + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); + + while (1) { + if (!info->tty || + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) + goto next; + + info->last_active = jiffies; + + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + check_modem_status(info); + if (status & UART_LSR_THRE) + transmit_chars(info, 0); + + next: + info = info->next_port; + if (info) + continue; + + info = IRQ_ports[irq]; + // + // The user was a bonehead, and misconfigured their + // multiport info. Rather than lock up the kernel + // in an infinite loop, if we loop too many times, + // print a message and break out of the loop. + // + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("Misconfigured multiport serial info " + "for irq %d. Breaking out irq loop\n", irq); + break; + } + if (multi->port_monitor) + printk("rs port monitor irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); + if ((inb(multi->port1) & multi->mask1) != multi->match1) + continue; + if (!multi->port2) + break; + if ((inb(multi->port2) & multi->mask2) != multi->match2) + continue; + if (!multi->port3) + break; + if ((inb(multi->port3) & multi->mask3) != multi->match3) + continue; + if (!multi->port4) + break; + if ((inb(multi->port4) & multi->mask4) != multi->match4) + continue; + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +*/ +#endif + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void do_serial_bh(void) +{ + run_task_queue(&tq_serial); +} + +static void do_softint(void *private_) +{ + struct async_struct *info = (struct async_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } +} + +/* + * This subroutine is called when the RS_TIMER goes off. It is used + * by the serial driver to handle ports that do not have an interrupt + * (irq=0). This doesn't work very well for 16450's, but gives barely + * passable results for a 16550A. (Although at the expense of much + * CPU overhead). + */ +static void rs_timer(unsigned long dummy) +{ + static unsigned long last_strobe; + struct async_struct *info; + unsigned int i; + unsigned long flags; + + if ((jiffies - last_strobe) >= RS_STROBE_TIME) { + for (i=0; i < NR_IRQS; i++) { + info = IRQ_ports[i]; + if (!info) + continue; + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + if (info->next_port) { + do { + serial_out(info, UART_IER, 0); + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + info = info->next_port; + } while (info); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[i].port1) + rs_interrupt_multi(i, NULL, NULL); + else +#endif + rs_interrupt(i, NULL, NULL); + } else +#endif // CONFIG_SERIAL_SHARE_IRQ + rs_interrupt_single(i, NULL, NULL); + restore_flags(flags); + } + } + last_strobe = jiffies; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + if (IRQ_ports[0]) { + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + rs_interrupt(0, NULL, NULL); +#else + rs_interrupt_single(0, NULL, NULL); +#endif + restore_flags(flags); + + mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); + } +} +/* + * --------------------------------------------------------------- + * Low level utility subroutines for the serial driver: routines to + * figure out the appropriate timeout for an interrupt chain, routines + * to initialize and startup a serial port, and routines to shutdown a + * serial port. Useful stuff like that. + * --------------------------------------------------------------- + */ + +/* + * This routine figures out the correct timeout for a particular IRQ. + * It uses the smallest timeout of all of the serial ports in a + * particular interrupt chain. Now only used for IRQ 0.... + */ +static void figure_IRQ_timeout(int irq) +{ + struct async_struct *info; + int timeout = 60*HZ; // 60 seconds === a long time :-) + + info = IRQ_ports[irq]; + if (!info) { + IRQ_timeout[irq] = 60*HZ; + return; + } + while (info) { + if (info->timeout < timeout) + timeout = info->timeout; + info = info->next_port; + } + if (!irq) + timeout = timeout / 2; + IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1; +} + +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +/* +static int enable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + restore_flags(flags); + return result; +} +*/ +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +/* +static int disable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + restore_flags(flags); + return result; +} +*/ +#endif /* CONFIG_SERIAL_RSA */ + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state= info->state; + unsigned long page; +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned short ICP; +#endif + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttys%d (irq %d)...", info->line, state->irq); +#endif +/* + if (uart_config[state->type].flags & UART_STARTECH) { + // Wake up UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + // + // Turn off LCR == 0xBF so we actually set the IER + // register on the XR16C850 + // + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, 0); + // + // Now reset LCR so we can turn off the ECB bit + // + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + // + // For a XR16C850, we need to set the trigger levels + // + if (state->type == PORT_16850) { + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(info, UART_TRG, UART_TRG_96); + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(info, UART_TRG, UART_TRG_96); + } + serial_outp(info, UART_LCR, 0); + } + + if (state->type == PORT_16750) { + // Wake up UART + serial_outp(info, UART_IER, 0); + } + + if (state->type == PORT_16C950) { + // Wake up and initialize UART + info->ACR = 0; + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_IER, 0); + serial_outp(info, UART_LCR, 0); + serial_icr_write(info, UART_CSR, 0); // Reset the UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + } +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // If this is an RSA port, see if we can kick it up to the + // higher speed clock. + // + if (state->type == PORT_RSA) { + if (state->baud_base != SERIAL_RSA_BAUD_BASE && + enable_rsa(info)) + state->baud_base = SERIAL_RSA_BAUD_BASE; + if (state->baud_base == SERIAL_RSA_BAUD_BASE) + serial_outp(info, UART_RSA_FRR, 0); + } +#endif +*/ +/* + // + // Clear the FIFO buffers and disable them + // (they will be reenabled in change_speed()) + // + if (uart_config[state->type].flags & UART_CLEAR_FIFO) { + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + } +*/ +/* + // + // Clear the interrupt registers. + // + (void) serial_inp(info, UART_LSR); + (void) serial_inp(info, UART_RX); + (void) serial_inp(info, UART_IIR); + (void) serial_inp(info, UART_MSR); +*/ + CLEAR_PEND_INT(state->irq); /* clear serial UART rx interrupt */ + CLEAR_PEND_INT(state->irq-1); /* clear serial UART tx interrupt */ + +/* + // + // At this point there's no way the LSR could still be 0xFF; + // if it is, then bail out, because there's likely no UART + // here. + // + if (!(info->flags & ASYNC_BUGGY_UART) && + (serial_inp(info, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", state->line); + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + } else + retval = -ENODEV; + goto errout; + } +*/ + // + // Allocate the IRQ if necessary + // + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { +#ifdef CONFIG_SERIAL_SHARE_IRQ + free_irq(state->irq, &IRQ_ports[state->irq]); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[state->irq].port1) + handler = rs_interrupt_multi; + else +#endif + handler = rs_interrupt; +#else + retval = -EBUSY; + goto errout; +#endif // CONFIG_SERIAL_SHARE_IRQ + } else + handler = rs_interrupt_single; + + // Mac Wang register rx and tx seperatly + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + + retval = request_irq(state->irq - 1, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq-1]); + + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + // + // Insert serial port into IRQ chain. + // + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + IRQ_ports[state->irq - 1] = info; + figure_IRQ_timeout(state->irq - 1); + + // + // Now, initialize the UART + // +/* + serial_outp(info, UART_LCR, UART_LCR_WLEN8); // reset DLAB + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + if (state->irq == 0) + info->MCR |= UART_MCR_OUT1; + } else +#endif + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_outp(info, UART_MCR, info->MCR); +*/ + serial_outp(info, UART_LCR, UART_LCR_WLEN8); + serial_outp(info, UART_GCR, UART_GCR_RX_INT|UART_GCR_TX_INT|UART_GCR_RX_STAT_INT); + + // + // Finally, enable interrupts + // +/* + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); // enable interrupts +*/ + enable_uart_rx_interrupt(info->line) ; + +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + // Enable interrupts on the AST Fourport board + ICP = (info->port & 0xFE0) | 0x01F; + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + + // + // And clear the interrupt registers again for luck. + // +/* + (void)serial_inp(info, UART_LSR); + (void)serial_inp(info, UART_RX); + (void)serial_inp(info, UART_IIR); + (void)serial_inp(info, UART_MSR); +*/ + CLEAR_PEND_INT(state->irq); /* clear serial UART rx interrupt */ + CLEAR_PEND_INT(state->irq-1); /* clear serial UART tx interrupt */ + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + // + // Set up serial timers... + // + mod_timer(&serial_timer, jiffies + 2*HZ/100); + + // + // Set up the tty->alt_speed kludge + // +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } +#endif + + // + // and set the speed of the serial port + // + change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct async_struct * info) +{ + unsigned long flags; + struct serial_state *state; + int retval; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); // Disable interrupts + + // + // clear delta_msr_wait queue to avoid mem leaks: we may free the irq + // here so the queue might never be waken up + // + wake_up_interruptible(&info->delta_msr_wait); + + // + // First unlink the serial port from the IRQ chain... + // + if (info->next_port) + info->next_port->prev_port = info->prev_port; + if (info->prev_port) + info->prev_port->next_port = info->next_port; + else + IRQ_ports[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + // + // Free the IRQ, if necessary + // + // rx + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } + // tx + + if (state->irq - 1 && (!IRQ_ports[state->irq - 1] || + !IRQ_ports[state->irq - 1]->next_port)) { + free_irq(state->irq-1, &IRQ_ports[state->irq-1]); + } +/* + if (state->irq - 1 && (!IRQ_ports[state->irq - 1] || + !IRQ_ports[state->irq - 1]->next_port)) { + if (IRQ_ports[state->irq - 1]) { + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + retval = request_irq(state->irq - 1, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq - 1]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + } +*/ + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + +/* + info->IER = 0; + serial_outp(info, UART_IER, 0x00); // disable all intrs +*/ + disable_uart_rx_interrupt(info->line); + disable_uart_tx_interrupt(info->line); +/* +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + // reset interrupts on the AST Fourport board + (void) inb((info->port & 0xFE0) | 0x01F); + info->MCR |= UART_MCR_OUT1; + } else +#endif + info->MCR &= ~UART_MCR_OUT2; + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask +*/ +/* + // disable break condition + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(info, UART_MCR, info->MCR); + + // disable FIFO's + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // Reset the RSA board back to 115kbps compat mode. + // + if ((state->type == PORT_RSA) && + (state->baud_base == SERIAL_RSA_BAUD_BASE && + disable_rsa(info))) + state->baud_base = SERIAL_RSA_BAUD_BASE_LO; +#endif +*/ + + (void)serial_in(info, UART_RX); // read data port to reset things + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + // Arrange to enter sleep mode + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, UART_IERX_SLEEP); + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + serial_outp(info, UART_LCR, 0); + } + if (info->state->type == PORT_16750) { + // Arrange to enter sleep mode + serial_outp(info, UART_IER, UART_IERX_SLEEP); + } +*/ + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, + 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 0 }; +/* +static int tty_get_baud_rate(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned int cflag, i; + + cflag = tty->termios->c_cflag; + + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 2) + tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 2; + } + return baud_table[i]; +} +*/ +#endif + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(struct async_struct *info, + struct termios *old_termios) +{ + int quot = 0, baud_base, baud; +// unsigned cflag, cval, fcr = 0; + unsigned cflag, cval; + int bits; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!CONFIGURED_SERIAL_PORT(info)) + return; + + // byte size and parity + switch (cflag & CSIZE) { + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; + // Never happens, but GCC is too dumb to figure it out + default: cval = 0x00; bits = 7; break; + } + if (cflag & CSTOPB) { + cval |= 0x04; + bits++; + } + if (cflag & PARENB) { + bits++; + } + if (cflag & PARODD) + cval |= UART_LCR_OPAR; + else if (cflag & PARENB) + cval |= UART_LCR_EPAR; + else + cval |= UART_LCR_NPAR; +/* + if (cflag & PARENB) { + cval |= UART_LCR_PARITY; + bits++; + } + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif +*/ + // Determine divisor based on baud rate + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; // B0 transition handled in rs_set_termios +/* +#ifdef CONFIG_SERIAL_RSA + if ((info->state->type == PORT_RSA) && + (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && + enable_rsa(info)) + info->state->baud_base = SERIAL_RSA_BAUD_BASE; +#endif +*/ + baud_base = info->state->baud_base; +/* + if (info->state->type == PORT_16C950) { + if (baud <= baud_base) + serial_icr_write(info, UART_TCR, 0); + else if (baud <= 2*baud_base) { + serial_icr_write(info, UART_TCR, 0x8); + baud_base = baud_base * 2; + } else if (baud <= 4*baud_base) { + serial_icr_write(info, UART_TCR, 0x4); + baud_base = baud_base * 4; + } else + serial_icr_write(info, UART_TCR, 0); + } +*/ +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + quot = 1; //tricky + + // If the quotient is zero refuse the change + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + } + // As a last resort, if the quotient is zero, default to 9600 bps + if (!quot) + quot = baud_base / 9600; + // + // Work around a bug in the Oxford Semiconductor 952 rev B + // chip which causes it to seriously miscalculate baud rates + // when DLL is 0. + // +/* + if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && + (info->state->revision == 0x5201)) + quot++; +*/ + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; // Add .02 seconds of slop + +/* + // Set up FIFO's + if (uart_config[info->state->type].flags & UART_USE_FIFO) { + if ((info->state->baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_RSA + else if (info->state->type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (info->state->type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + // CTS flow control flag and modem status interrupts + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); +*/ + + // + // Set up parity check flag + // +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + // + // Characters to ignore + // + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + // + // If we're ignore parity and break indicators, ignore + // overruns too. (For real raw support). + // + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_OE; + } +#if 0 // breaks serial console during boot stage + // + // !!! ignore all characters if CREAD is not set + // + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; +#endif + save_flags(flags); cli(); +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, + (cflag & CRTSCTS) ? UART_EFR_CTS : 0); + } +*/ +/* + serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); // set DLAB + serial_outp(info, UART_DLL, quot & 0xff); // LS of divisor + serial_outp(info, UART_DLM, quot >> 8); // MS of divisor +*/ +/* + if (info->state->type == PORT_16750) + serial_outp(info, UART_FCR, fcr); // set fcr +*/ +// serial_outp(info, UART_LCR, cval); // reset DLAB + info->LCR = cval; // Save LCR +/* + if (info->state->type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + // emulated UARTs (Lucent Venus 167x) need two steps + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(info, UART_FCR, fcr); // set fcr + } +*/ + serial_outp(info, UART_BDR, baudrate_div(baud)); + serial_outp(info, UART_LCR, cval); + restore_flags(flags); +} + +static void rs_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_put_char")) + return; + + if (!tty || !info->xmit.buf) + return; + + save_flags(flags); cli(); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + restore_flags(flags); + return; + } + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + restore_flags(flags); +} + +static void rs_flush_chars(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) + return; + + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + save_flags(flags); cli(); +/* + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt(info->line); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped) { +/* + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt(info->line); + } + return ret; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) + return 0; + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void rs_flush_buffer(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + 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 rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + info->x_char = ch; + if (ch) { + // Make sure transmit interrupts are on +/* + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt(info->line); + } +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_throttle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + rs_send_xchar(tty, STOP_CHAR(tty)); +/* + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; +*/ + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + rs_send_xchar(tty, START_CHAR(tty)); + } +/* + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; +*/ + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ +static int get_serial_info(struct async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + if (HIGH_BITS_OFFSET) + tmp.port_high = state->port >> HIGH_BITS_OFFSET; + else + tmp.port_high = 0; + tmp.irq = state->irq; + tmp.flags = state->flags; + tmp.xmit_fifo_size = state->xmit_fifo_size; + tmp.baud_base = state->baud_base; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tmp.hub6 = state->hub6; + tmp.io_type = state->io_type; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct async_struct * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct serial_state old_state, *state; + unsigned int i,change_irq,change_port; + int retval = 0; + unsigned long new_port; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + state = info->state; + old_state = *state; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != state->irq; + change_port = (new_port != ((int) state->port)) || + (new_serial.hub6 != state->hub6); + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != state->baud_base) || + (new_serial.type != state->type) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != state->xmit_fifo_size) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || + (new_serial.type == PORT_STARTECH)) { + return -EINVAL; + } + + if ((new_serial.type != state->type) || + (new_serial.xmit_fifo_size <= 0)) + new_serial.xmit_fifo_size = + uart_config[new_serial.type].dfl_xmit_fifo_size; + + // Make sure address is not already in use + if (new_serial.type) { + for (i = 0 ; i < NR_PORTS; i++) + if ((state != &rs_table[i]) && + (rs_table[i].port == new_port) && + rs_table[i].type) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + // + // OK, past this point, all the error checking has been done. + // At this point, we start making changes..... + // + + state->baud_base = new_serial.baud_base; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (old_state.type == PORT_RSA) + release_region(state->port + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(state->port,8); + } + state->type = new_serial.type; + if (change_port || change_irq) { + // + // We need to shutdown the serial port at the old + // port/irq combination. + // + shutdown(info); + state->irq = new_serial.irq; + info->port = state->port = new_port; + info->hub6 = state->hub6 = new_serial.hub6; + if (info->hub6) + info->io_type = state->io_type = SERIAL_IO_HUB6; + else if (info->io_type == SERIAL_IO_HUB6) + info->io_type = state->io_type = SERIAL_IO_PORT; + } + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(state->port + UART_RSA_BASE, + 16, "serial_rsa(set)"); + else +#endif +*/ + request_region(state->port,8,"serial(set)"); + } + + +check_and_exit: + if (!state->port || !state->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; +#endif + change_speed(info, 0); + } + } else + retval = startup(info); + return retval; +} + + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info(struct async_struct * info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + save_flags(flags); cli(); + status = serial_in(info, UART_LSR); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + + // + // If we're about to load something into the transmit + // register, we'll pretend the transmitter isn't empty to + // avoid a race condition (depending on when the transmit + // interrupt happens). + // + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SERIAL_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +/* +static int get_modem_info(struct async_struct * info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} +*/ +/* +static int set_modem_info(struct async_struct * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR |= UART_MCR_LOOP; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR &= ~UART_MCR_LOOP; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_LOOP | + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + return 0; +} +*/ +static int do_autoconfig(struct async_struct * info) +{ + int irq, retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (info->state->count > 1) + return -EBUSY; + + shutdown(info); + + autoconfig(info->state); + if ((info->state->flags & ASYNC_AUTO_IRQ) && + (info->state->port != 0) && + (info->state->type != PORT_UNKNOWN)) { + irq = detect_uart_irq(info->state); + if (irq > 0) + info->state->irq = irq; + } + + retval = startup(info); + if (retval) + return retval; + return 0; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +/* +static void send_break( struct async_struct * info, int duration) +{ + if (!CONFIGURED_SERIAL_PORT(info)) + return; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + cli(); + info->LCR |= UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + schedule(); + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + sti(); +} +*/ +#else +static void rs_break(struct tty_struct *tty, int break_state) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + + if (!CONFIGURED_SERIAL_PORT(info)) + return; + save_flags(flags); cli(); + if (break_state == -1) + info->LCR |= UART_LCR_SBC; + else + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + restore_flags(flags); +} +#endif + +#ifdef CONFIG_SERIAL_MULTIPORT +/* +static int get_multiport_struct(struct async_struct * info, + struct serial_multiport_struct *retinfo) +{ + struct serial_multiport_struct ret; + struct rs_multiport_struct *multi; + + multi = &rs_multiport[info->state->irq]; + + ret.port_monitor = multi->port_monitor; + + ret.port1 = multi->port1; + ret.mask1 = multi->mask1; + ret.match1 = multi->match1; + + ret.port2 = multi->port2; + ret.mask2 = multi->mask2; + ret.match2 = multi->match2; + + ret.port3 = multi->port3; + ret.mask3 = multi->mask3; + ret.match3 = multi->match3; + + ret.port4 = multi->port4; + ret.mask4 = multi->mask4; + ret.match4 = multi->match4; + + ret.irq = info->state->irq; + + if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_multiport_struct(struct async_struct * info, + struct serial_multiport_struct *in_multi) +{ + struct serial_multiport_struct new_multi; + struct rs_multiport_struct *multi; + struct serial_state *state; + int was_multi, now_multi; + int retval; + void (*handler)(int, void *, struct pt_regs *); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + state = info->state; + + if (copy_from_user(&new_multi, in_multi, + sizeof(struct serial_multiport_struct))) + return -EFAULT; + + if (new_multi.irq != state->irq || state->irq == 0 || + !IRQ_ports[state->irq]) + return -EINVAL; + + multi = &rs_multiport[state->irq]; + was_multi = (multi->port1 != 0); + + multi->port_monitor = new_multi.port_monitor; + + if (multi->port1) + release_region(multi->port1,1); + multi->port1 = new_multi.port1; + multi->mask1 = new_multi.mask1; + multi->match1 = new_multi.match1; + if (multi->port1) + request_region(multi->port1,1,"serial(multiport1)"); + + if (multi->port2) + release_region(multi->port2,1); + multi->port2 = new_multi.port2; + multi->mask2 = new_multi.mask2; + multi->match2 = new_multi.match2; + if (multi->port2) + request_region(multi->port2,1,"serial(multiport2)"); + + if (multi->port3) + release_region(multi->port3,1); + multi->port3 = new_multi.port3; + multi->mask3 = new_multi.mask3; + multi->match3 = new_multi.match3; + if (multi->port3) + request_region(multi->port3,1,"serial(multiport3)"); + + if (multi->port4) + release_region(multi->port4,1); + multi->port4 = new_multi.port4; + multi->mask4 = new_multi.mask4; + multi->match4 = new_multi.match4; + if (multi->port4) + request_region(multi->port4,1,"serial(multiport4)"); + + now_multi = (multi->port1 != 0); + + if (IRQ_ports[state->irq]->next_port && + (was_multi != now_multi)) { + free_irq(state->irq, &IRQ_ports[state->irq]); + if (now_multi) + handler = rs_interrupt_multi; + else + handler = rs_interrupt; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + printk("Couldn't reallocate serial interrupt " + "driver!!\n"); + } + } + return 0; +} +*/ +#endif +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct async_icount cprev, cnow; // kernel counter temps + struct serial_icounter_struct icount; + unsigned long flags; +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + int retval, tmp; +#endif + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + case TCSBRK: // SVID version: non-zero arg --> no break + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); // 1/4 second + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: // support for POSIX tcsendbreak() + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCGSOFTCAR: + tmp = C_CLOCAL(tty) ? 1 : 0; + if (copy_to_user((void *)arg, &tmp, sizeof(int))) + return -EFAULT; + return 0; + case TIOCSSOFTCAR: + if (copy_from_user(&tmp, (void *)arg, sizeof(int))) + return -EFAULT; + + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (tmp ? CLOCAL : 0)); + return 0; +#endif +/* + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); +*/ + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERCONFIG: + return do_autoconfig(info); + + case TIOCSERGETLSR: // Get line status register + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + +#ifdef CONFIG_SERIAL_MULTIPORT + case TIOCSERGETMULTI: + return get_multiport_struct(info, + (struct serial_multiport_struct *) arg); + case TIOCSERSETMULTI: + return set_multiport_struct(info, + (struct serial_multiport_struct *) arg); +#endif + + // + // Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + // - mask passed in arg for lines of interest + // (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + // Caller should use TIOCGICOUNT to see which one it was + // + case TIOCMIWAIT: + save_flags(flags); cli(); + // note the counters on entry + cprev = info->state->icount; + restore_flags(flags); + // Force modem status interrupts on +/* + info->IER |= UART_IER_MSI; + serial_out(info, UART_IER, info->IER); +*/ + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + // see if a signal did it + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->state->icount; // atomic copy + restore_flags(flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; // no change => error + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + // NOTREACHED + + // + // Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + // Return: write counters to the user passed counter struct + // NB: both 1->0 and 0->1 transitions are counted except for + // RI where only 0->1 is counted. + // + case TIOCGICOUNT: + save_flags(flags); cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + // "setserial -W" is called in Debian boot + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; +// unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ( (cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info, old_termios); + +/* + // Handle transition to B0 status + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + // Handle transition away from B0 status + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } +*/ + + // Handle turning off CRTSCTS + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } + +#if 0 + // + // No need to wake up processes in open wait, since they + // sample the CLOCAL flag once, and don't recheck it. + // XXX It's not clear whether the current behavior is correct + // or not. Hence, this may change..... + // + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + // + // Uh, oh. tty->count is 1, which means that the tty + // structure will be freed. state->count should always + // be one in these conditions. If it's greater than + // one, we've got real problems, since it means the + // serial port won't be shutdown. + // + printk("rs_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + // + // Save the termios structure, since this port may have + // separate termios for callout and dialin. + // + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->callout_termios = *tty->termios; + // + // Now we wait for the transmit buffer to clear; and we notify + // the line discipline to only process XON/XOFF characters. + // + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + // + // At this point we stop accepting input. To do this, we + // disable the receive line status interrupts, and tell the + // interrupt driver to stop checking the data ready bit in the + // line status register. + // + //info->IER &= ~UART_IER_RLSI; + info->read_status_mask &= ~UART_LSR_DR; + if (info->flags & ASYNC_INITIALIZED) { + //serial_out(info, UART_IER, info->IER); + enable_uart_rx_interrupt(info->line); + // + // Before we drop DTR, make sure the UART transmitter + // has completely drained; this is especially + // important if there is a transmit FIFO! + // + rs_wait_until_sent(tty, info->timeout); + } + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + + if (info->state->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; // Just in case.... + + orig_jiffies = jiffies; + // + // Set the check interval to be 1/5 of the estimated time to + // send a single character, and make it at least 1. The check + // interval should also be less than the timeout. + // + // Note: we have to use pretty tight timings here to satisfy + // the NIST-PCTS. + // + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + // + // If the transmitter hasn't cleared in twice the approximate + // amount of time to send the entire FIFO, it probably won't + // ever clear. This assumes the UART isn't doing flow + // control, which is currently the case. Hence, if it ever + // takes longer than info->timeout, this is probably due to a + // UART bug of some kind. So, we clamp the timeout parameter at + // 2*info->timeout. + // + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_hangup(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + state = info->state; + + rs_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct async_struct *info) +{ + DECLARE_WAITQUEUE(wait, current); + struct serial_state *state = info->state; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + // + // If the device is in the middle of being closed, then block + // until it's done, and then try again. + // + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + // + // If this is a callout device, then just make sure the normal + // device isn't being used. + // + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + // + // If non-blocking mode is set, or the port is not enabled, + // then make the check up front and then exit. + // + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (state->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + // + // Block waiting for the carrier detect and the line to become + // free (i.e., not in use by the callout). While we are in + // this loop, state->count is dropped by one, so that + // rs_close() knows when to free things. We restore it upon + // exit, either normal or abnormal. + // + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + state->line, state->count); +#endif + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); +/* + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); +*/ + restore_flags(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } +/* + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) + break; +*/ + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } + memset(info, 0, sizeof(struct async_struct)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->io_type = sstate->io_type; + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; + info->xmit_fifo_size = sstate->xmit_fifo_size; + info->line = line; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->state = sstate; + if (sstate->info) { + kfree(info); + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct async_struct *info; + int retval, line; + unsigned long page; + + MOD_INC_USE_COUNT; + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + retval = get_async_struct(line, &info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + tty->driver_data = info; + info->tty = tty; + if (serial_paranoia_check(info, tty->device, "rs_open")) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + // + // This relies on lock_kernel() stuff so wants tidying for 2.5 + // + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + // + // If the port is the middle of closing, bail out now + // + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + MOD_DEC_USE_COUNT; +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + // + // Start up serial port + // + retval = startup(info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + MOD_DEC_USE_COUNT; + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info, 0); + } +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info, 0); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ +static inline int line_info(char *buf, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; +// char stat_buf[30], control, status; + char stat_buf[30]; + int ret; + //unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, uart_config[state->type].name, + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + // + // Figure out the current RS-232 lines + // + if (!info) { + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->hub6 = state->hub6; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + info->quot = 0; + info->tty = 0; + } + stat_buf[0] = 0; + stat_buf[1] = 0; +/* + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); +*/ + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + // + // Last thing is the RS-232 status lines + // + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +int rs_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", + serial_version, LOCAL_VERSTRING, serial_revdate); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * rs_init() and friends + * + * rs_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static char serial_options[] __initdata = +#ifdef CONFIG_HUB6 + " HUB-6" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MANY_PORTS + " MANY_PORTS" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MULTIPORT + " MULTIPORT" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_SHARE_IRQ + " SHARE_IRQ" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_DETECT_IRQ + " DETECT_IRQ" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PCI + " SERIAL_PCI" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PNP + " ISAPNP" +#define SERIAL_OPT +#endif +#ifdef SERIAL_OPT + " enabled\n"; +#else + " no serial options enabled\n"; +#endif +#undef SERIAL_OPT + +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, + serial_version, LOCAL_VERSTRING, serial_revdate, + serial_options); +} + +/* + * This routine detect the IRQ of a serial port by clearing OUT2 when + * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at + * each time, as long as no other device permanently request the IRQ. + * If no IRQ is detected, or multiple IRQ appear, this function returns 0. + * The variable "state" and the field "state->port" should not be null. + */ +static unsigned detect_uart_irq (struct serial_state * state) +{ +/* + int irq; + unsigned long irqs; + unsigned char save_mcr, save_ier; + struct async_struct scr_info; // serial_{in,out} because HUB6 + +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned char save_ICP=0; // no warning + unsigned short ICP=0; + + if (state->flags & ASYNC_FOURPORT) { + ICP = (state->port & 0xFE0) | 0x01F; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + scr_info.magic = SERIAL_MAGIC; + scr_info.state = state; + scr_info.port = state->port; + scr_info.flags = state->flags; +#ifdef CONFIG_HUB6 + scr_info.hub6 = state->hub6; +#endif + scr_info.io_type = state->io_type; + scr_info.iomem_base = state->iomem_base; + scr_info.iomem_reg_shift = state->iomem_reg_shift; + + // forget possible initially masked and pending IRQ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(&scr_info, UART_MCR); + save_ier = serial_inp(&scr_info, UART_IER); + serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(&scr_info, UART_MCR, 0); + udelay (10); + if (state->flags & ASYNC_FOURPORT) { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(&scr_info, UART_IER, 0x0f); // enable all intrs + (void)serial_inp(&scr_info, UART_LSR); + (void)serial_inp(&scr_info, UART_RX); + (void)serial_inp(&scr_info, UART_IIR); + (void)serial_inp(&scr_info, UART_MSR); + serial_outp(&scr_info, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(&scr_info, UART_MCR, save_mcr); + serial_outp(&scr_info, UART_IER, save_ier); +#ifdef CONFIG_SERIAL_MANY_PORTS + if (state->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); +#endif + return (irq > 0)? irq : 0; +*/ + return 0; +} + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +/* +static int size_fifo(struct async_struct *info) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(info, UART_FCR); + old_mcr = serial_inp(info, UART_MCR); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(info, UART_MCR, UART_MCR_LOOP); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(info, UART_DLL); + old_dlm = serial_inp(info, UART_DLM); + serial_outp(info, UART_DLL, 0x01); + serial_outp(info, UART_DLM, 0x00); + serial_outp(info, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(info, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(info, UART_RX); + serial_outp(info, UART_FCR, old_fcr); + serial_outp(info, UART_MCR, old_mcr); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, old_dll); + serial_outp(info, UART_DLM, old_dlm); + + return count; +} +*/ +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +/* +static void autoconfig_startech_uarts(struct async_struct *info, + struct serial_state *state, + unsigned long flags) +{ + unsigned char scratch, scratch2, scratch3, scratch4; + + // + // First we check to see if it's an Oxford Semiconductor UART. + // + // If we have to do this here because some non-National + // Semiconductor clone chips lock up if you try writing to the + // LSR register (which serial_icr_read does) + // + if (state->type == PORT_16550A) { + // + // EFR [4] must be set else this test fails + // + // This shouldn't be necessary, but Mike Hudson + // (Exoray@isys.ca) claims that it's needed for 952 + // dual UART's (which are not recommended for new designs). + // + info->ACR = 0; + serial_out(info, UART_LCR, 0xBF); + serial_out(info, UART_EFR, 0x10); + serial_out(info, UART_LCR, 0x00); + // Check for Oxford Semiconductor 16C950 + scratch = serial_icr_read(info, UART_ID1); + scratch2 = serial_icr_read(info, UART_ID2); + scratch3 = serial_icr_read(info, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + state->type = PORT_16C950; + state->revision = serial_icr_read(info, UART_REV) | + (scratch3 << 8); + return; + } + } + + // + // We check for a XR16C850 by setting DLL and DLM to 0, and + // then reading back DLL and DLM. If DLM reads back 0x10, + // then the UART is a XR16C850 and the DLL contains the chip + // revision. If DLM reads back 0x14, then the UART is a + // XR16C854. + // + // + + // Save the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(info, UART_DLL); + scratch4 = serial_inp(info, UART_DLM); + + serial_outp(info, UART_DLL, 0); + serial_outp(info, UART_DLM, 0); + scratch2 = serial_inp(info, UART_DLL); + scratch = serial_inp(info, UART_DLM); + serial_outp(info, UART_LCR, 0); + + if (scratch == 0x10 || scratch == 0x14) { + if (scratch == 0x10) + state->revision = scratch2; + state->type = PORT_16850; + return; + } + + // Restore the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, scratch3); + serial_outp(info, UART_DLM, scratch4); + serial_outp(info, UART_LCR, 0); + // + // We distinguish between the '654 and the '650 by counting + // how many bytes are in the FIFO. I'm using this for now, + // since that's the technique that was sent to me in the + // serial driver update, but I'm not convinced this works. + // I've had problems doing this in the past. -TYT + // + if (size_fifo(info) == 64) + state->type = PORT_16654; + else + state->type = PORT_16650V2; +} +*/ +/* + * This routine is called by rs_init() to initialize a specific serial + * port. It determines what type of UART chip this serial port is + * using: 8250, 16450, 16550, 16550A. The important question is + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. + */ +static void autoconfig(struct serial_state * state) +{ +// unsigned char status1, status2, scratch, scratch2, scratch3; +// unsigned char save_lcr, save_mcr; + struct async_struct *info, scr_info; + unsigned long flags; + + state->type = PORT_UNKNOWN; + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line, + state->port, (unsigned) state->iomem_base); +#endif + + if (!CONFIGURED_SERIAL_PORT(state)) + return; + + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + + save_flags(flags); cli(); + +/* + if (!(state->flags & ASYNC_BUGGY_UART) && + !state->iomem_base) { + // + // Do a simple existence test first; if we fail this, + // there's no point trying anything else. + // + // 0x80 is used as a nonsense port to prevent against + // false positives due to ISA bus float. The + // assumption is that 0x80 is a non-existent port; + // which should be safe since include/asm/io.h also + // makes this assumption. + // + scratch = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, scratch); + if (scratch2 || scratch3 != 0x0F) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: simple autoconfig failed " + "(%02x, %02x)\n", state->line, + scratch2, scratch3); +#endif + restore_flags(flags); + return; // We failed; there's nothing here + } + } +*/ +/* + save_mcr = serial_in(info, UART_MCR); + save_lcr = serial_in(info, UART_LCR); +*/ + // + // Check to see if a UART is really there. Certain broken + // internal modems based on the Rockwell chipset fail this + // test, because they apparently don't implement the loopback + // test mode. So this test is skipped on the COM 1 through + // COM 4 ports. This *should* be safe, since no board + // manufacturer would be stupid enough to design a board + // that conflicts with COM 1-4 --- we hope! + // +/* + if (!(state->flags & ASYNC_SKIP_TEST)) { + serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(info, UART_MSR) & 0xF0; + serial_outp(info, UART_MCR, save_mcr); + if (status1 != 0x90) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: no UART loopback failed\n", + state->line); +#endif + restore_flags(flags); + return; + } + } + serial_outp(info, UART_LCR, 0xBF); // set up for StarTech test + serial_outp(info, UART_EFR, 0); // EFR is the same as FCR + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(info, UART_IIR) >> 6; + switch (scratch) { + case 0: + state->type = PORT_16450; + break; + case 1: + state->type = PORT_UNKNOWN; + break; + case 2: + state->type = PORT_16550; + break; + case 3: + state->type = PORT_16550A; + break; + } + if (state->type == PORT_16550A) { + // Check for Startech UART's + serial_outp(info, UART_LCR, UART_LCR_DLAB); + if (serial_in(info, UART_EFR) == 0) { + state->type = PORT_16650; + } else { + serial_outp(info, UART_LCR, 0xBF); + if (serial_in(info, UART_EFR) == 0) + autoconfig_startech_uarts(info, state, flags); + } + } + if (state->type == PORT_16550A) { + // Check for TI 16750 + serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 7) { + // + // If this is a 16750, and not a cheap UART + // clone, then it should only go into 64 byte + // mode if the UART_FCR7_64BYTE bit was set + // while UART_LCR_DLAB was latched. + // + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 6) + state->type = PORT_16750; + } + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + if (state->type == PORT_16550A) { + int i; + + for (i = 0 ; i < PORT_RSA_MAX ; ++i) { + if (!probe_rsa[i] && !force_rsa[i]) + break; + if (((probe_rsa[i] != state->port) || + check_region(state->port + UART_RSA_BASE, 16)) && + (force_rsa[i] != state->port)) + continue; + if (!enable_rsa(info)) + continue; + state->type = PORT_RSA; + state->baud_base = SERIAL_RSA_BAUD_BASE; + break; + } + } +#endif + serial_outp(info, UART_LCR, save_lcr); + if (state->type == PORT_16450) { + scratch = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0xa5); + status1 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0x5a); + status2 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, scratch); + + if ((status1 != 0xa5) || (status2 != 0x5a)) + state->type = PORT_8250; + } +*/ + state->type = PORT_S5N8947; + state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; + + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + return; + } + + if (info->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(info->port + UART_RSA_BASE, 16, + "serial_rsa(auto)"); + else +#endif +*/ + request_region(info->port,8,"serial(auto)"); + } + + // + // Reset the UART. + // +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + serial_outp(info, UART_RSA_FRR, 0); +#endif + serial_outp(info, UART_MCR, save_mcr); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ + serial_outp(info, UART_LCR, UART_LCR_WLEN8); + serial_outp(info, UART_GCR, UART_GCR_RX_INT|UART_GCR_TX_INT); + (void)serial_in(info, UART_RX); +// serial_outp(info, UART_IER, 0); + + restore_flags(flags); +} + + +int register_serial(struct serial_struct *req); +void unregister_serial(int line); + +#if (LINUX_VERSION_CODE > 0x20100) +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); +#else +static struct symbol_table serial_syms = { +#include + X(register_serial), + X(unregister_serial), +#include +}; +#endif + +/* + * The serial driver boot-time initialization code! + */ +static int __init rs_init(void) +{ + int i; + struct serial_state * state; + + init_bh(SERIAL_BH, do_serial_bh); + init_timer(&serial_timer); + serial_timer.function = rs_timer; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; +#ifdef CONFIG_SERIAL_MULTIPORT + memset(&rs_multiport[i], 0, + sizeof(struct rs_multiport_struct)); +#endif + } +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + // + // The interrupt of the serial console port + // can't be shared. + // + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif + show_serial_version(); + + // Initialize the tty_driver structure + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) + serial_driver.driver_name = "serial"; +#endif +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + serial_driver.name = "tts/%d"; +#else + serial_driver.name = "ttyS"; +#endif + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; + serial_driver.num = NR_PORTS; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_open; + serial_driver.close = rs_close; + serial_driver.write = rs_write; + serial_driver.put_char = rs_put_char; + serial_driver.flush_chars = rs_flush_chars; + serial_driver.write_room = rs_write_room; + serial_driver.chars_in_buffer = rs_chars_in_buffer; + serial_driver.flush_buffer = rs_flush_buffer; + serial_driver.ioctl = rs_ioctl; + serial_driver.throttle = rs_throttle; + serial_driver.unthrottle = rs_unthrottle; + serial_driver.set_termios = rs_set_termios; + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + serial_driver.break_ctl = rs_break; +#endif +#if (LINUX_VERSION_CODE >= 131343) + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; +#endif + + // + // The callout device is just like normal device except for + // major number and the subtype code. + // + callout_driver = serial_driver; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + callout_driver.name = "cua/%d"; +#else + callout_driver.name = "cua"; +#endif + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if (LINUX_VERSION_CODE >= 131343) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +#endif + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout driver\n"); + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); + if (state->hub6) + state->io_type = SERIAL_IO_HUB6; + if (state->port && check_region(state->port,8)) + continue; +#ifdef CONFIG_MCA + if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) + continue; +#endif + if (state->flags & ASYNC_BOOT_AUTOCONF) + autoconfig(state); + } + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) + continue; + if ( (state->flags & ASYNC_BOOT_AUTOCONF) + && (state->flags & ASYNC_AUTO_IRQ) + && (state->port != 0)) + state->irq = detect_uart_irq(state); + printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", + state->port, state->irq, + uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + } +#ifdef ENABLE_SERIAL_PCI + probe_serial_pci(); +#endif +#ifdef ENABLE_SERIAL_PNP + probe_serial_pnp(); +#endif + return 0; +} +/* + * This is for use by architectures that know their serial console + * attributes only at run time. Not to be invoked after rs_init(). + */ +/* +int __init early_serial_setup(struct serial_struct *req) +{ + int i = req->line; + + if (i >= NR_IRQS) + return(-ENOENT); + rs_table[i].magic = 0; + rs_table[i].baud_base = req->baud_base; + rs_table[i].port = req->port; + if (HIGH_BITS_OFFSET) + rs_table[i].port += (unsigned long) req->port_high << + HIGH_BITS_OFFSET; + rs_table[i].irq = req->irq; + rs_table[i].flags = req->flags; + rs_table[i].close_delay = req->close_delay; + rs_table[i].io_type = req->io_type; + rs_table[i].hub6 = req->hub6; + rs_table[i].iomem_base = req->iomem_base; + rs_table[i].iomem_reg_shift = req->iomem_reg_shift; + rs_table[i].type = req->type; + rs_table[i].xmit_fifo_size = req->xmit_fifo_size; + rs_table[i].custom_divisor = req->custom_divisor; + rs_table[i].closing_wait = req->closing_wait; + return(0); +} +*/ +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +/* +int register_serial(struct serial_struct *req) +{ + int i; + unsigned long flags; + struct serial_state *state; + struct async_struct *info; + unsigned long port; + + port = req->port; + if (HIGH_BITS_OFFSET) + port += (unsigned long) req->port_high << HIGH_BITS_OFFSET; + + save_flags(flags); cli(); + for (i = 0; i < NR_PORTS; i++) { + if ((rs_table[i].port == port) && + (rs_table[i].iomem_base == req->iomem_base)) + break; + } +#ifdef __i386__ + if (i == NR_PORTS) { + for (i = 4; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } +#endif + if (i == NR_PORTS) { + for (i = 0; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } + if (i == NR_PORTS) { + restore_flags(flags); + return -1; + } + state = &rs_table[i]; + if (rs_table[i].count) { + restore_flags(flags); + printk("Couldn't configure serial #%d (port=%ld,irq=%d): " + "device already open\n", i, port, req->irq); + return -1; + } + state->irq = req->irq; + state->port = port; + state->flags = req->flags; + state->io_type = req->io_type; + state->iomem_base = req->iomem_base; + state->iomem_reg_shift = req->iomem_reg_shift; + if (req->baud_base) + state->baud_base = req->baud_base; + if ((info = state->info) != NULL) { + info->port = port; + info->flags = req->flags; + info->io_type = req->io_type; + info->iomem_base = req->iomem_base; + info->iomem_reg_shift = req->iomem_reg_shift; + } + autoconfig(state); + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + printk("register_serial(): autoconfig failed\n"); + return -1; + } + restore_flags(flags); + + if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) + state->irq = detect_uart_irq(state); + + printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + state->iomem_base ? "iomem" : "port", + state->iomem_base ? (unsigned long)state->iomem_base : + state->port, state->irq, uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + return state->line + SERIAL_DEV_OFFSET; +} +*/ +/** + * unregister_serial - deconfigure a 16x50 serial port + * @line: line to deconfigure + * + * The port specified is deconfigured and its resources are freed. Any + * user of the port is disconnected as if carrier was dropped. Line is + * the port number returned by register_serial(). + */ +/* +void unregister_serial(int line) +{ + unsigned long flags; + struct serial_state *state = &rs_table[line]; + + save_flags(flags); cli(); + if (state->info && state->info->tty) + tty_hangup(state->info->tty); + state->type = PORT_UNKNOWN; + printk(KERN_INFO "tty%02d unloaded\n", state->line); + // These will be hidden, because they are devices that will no longer + // be available to the system. (ie, PCMCIA modems, once ejected) + // + tty_unregister_devfs(&serial_driver, + serial_driver.minor_start + state->line); + tty_unregister_devfs(&callout_driver, + callout_driver.minor_start + state->line); + restore_flags(flags); +} +*/ +static void __exit rs_fini(void) +{ + unsigned long flags; + int e1, e2; + int i; + struct async_struct *info; + + // printk("Unloading %s: version %s\n", serial_name, serial_version); + del_timer_sync(&serial_timer); + save_flags(flags); cli(); + remove_bh(SERIAL_BH); + if ((e1 = tty_unregister_driver(&serial_driver))) + printk("serial: failed to unregister serial driver (%d)\n", + e1); + if ((e2 = tty_unregister_driver(&callout_driver))) + printk("serial: failed to unregister callout driver (%d)\n", + e2); + restore_flags(flags); + + for (i = 0; i < NR_PORTS; i++) { + if ((info = rs_table[i].info)) { + rs_table[i].info = NULL; + kfree(info); + } + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (rs_table[i].type == PORT_RSA) + release_region(rs_table[i].port + + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(rs_table[i].port, 8); + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + if (rs_table[i].iomem_base) + iounmap(rs_table[i].iomem_base); +#endif +*/ + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + for (i=0; i < NR_PCI_BOARDS; i++) { + struct pci_board_inst *brd = &serial_pci_board[i]; + + if (serial_pci_board[i].dev == 0) + continue; + if (brd->board.init_fn) + (brd->board.init_fn)(brd->dev, &brd->board, 0); + if (DEACTIVATE_FUNC(brd->dev)) + (DEACTIVATE_FUNC(brd->dev))(brd->dev); + } +#endif +*/ + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } +/* +#ifdef ENABLE_SERIAL_PCI + if (serial_pci_driver.name[0]) + pci_unregister_driver (&serial_pci_driver); +#endif +*/ +} + +module_init(rs_init); +module_exit(rs_fini); +MODULE_DESCRIPTION("Standard/generic (dumb) serial driver"); +MODULE_AUTHOR("Theodore Ts'o "); +MODULE_LICENSE("GPL"); + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static struct async_struct async_sercons; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct async_struct *info) +{ + unsigned int status, tmout = 1000000; + + do { + status = serial_in(info, UART_LSR); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while((status & BOTH_EMPTY) != BOTH_EMPTY); +} + + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console must be locked when we get here. + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + static struct async_struct *info = &async_sercons; + unsigned i; + + /* + * First save the IER then disable the interrupts + */ + //ier = serial_in(info, UART_IER); + //serial_out(info, UART_IER, 0x00); +// disable_uart_tx_interrupt(info->line); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(info, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(info); + serial_out(info, UART_TX, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); + //serial_out(info, UART_IER, ier); +// enable_uart_tx_interrupt(info->line); +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity/flow control. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init serial_console_setup(struct console *co, char *options) +{ + static struct async_struct *info; + struct serial_state *state; + unsigned cval; + int baud = 19200; + int bits = 8; + int parity = 'n'; + int doflow = 0; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s++ - '0'; + if (*s) doflow = (*s++ == 'r'); + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + /* + * Set this to a sane value to prevent a divide error + */ + baud = 9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* + * Divisor, bytesize and parity + */ + state = rs_table + co->index; + if (doflow) + state->flags |= ASYNC_CONS_FLOW; + info = &async_sercons; + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + quot = state->baud_base / baud; + cval = cflag & (CSIZE | CSTOPB); +#if defined(__powerpc__) || defined(__alpha__) + cval >>= 8; +#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + { + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + } + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + /* + serial_out(info, UART_LCR, cval | UART_LCR_DLAB); // set DLAB + serial_out(info, UART_DLL, quot & 0xff); // LS of divisor + serial_out(info, UART_DLM, quot >> 8); // MS of divisor + serial_out(info, UART_LCR, cval); // reset DLAB + serial_out(info, UART_IER, 0); + serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + */ + /* + * If we read 0xff from the LSR, there is no UART here. + */ + /* + if (serial_in(info, UART_LSR) == 0xff) + return -1; + */ + serial_out(info, UART_BDR, baudrate_div(baud)); + serial_out(info, UART_LCR, cval); + + return 0; +} + +static struct console sercons = { + name: "ttyS", + write: serial_console_write, + device: serial_console_device, + setup: serial_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +/* + * Register console. + */ +void __init samsung_console_init(void) +{ + register_console(&sercons); +} +#endif + +/* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + End: +*/ diff -urN uClinux-2.4.22-uc1-virgin/drivers/char/serial_smc7004abrv2.c uClinux-2.4.22-uc1-smc/drivers/char/serial_smc7004abrv2.c --- uClinux-2.4.22-uc1-virgin/drivers/char/serial_smc7004abrv2.c 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/char/serial_smc7004abrv2.c 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,1401 @@ +/**************************************************************************** +* +* Name: serial_smc7004abrv2.c +* Based on: cnxtserial.c (has builtin 16550 compatible) +* +* Description: Provides serial port character I/O (16550) +* The UART is in SMSC SP37E760 chip on EXTERNAL IO bank 0 +* The access is 16bit halfword and the registers are on even +* addresses only (this is similar to the Conexant setup). +* +* Copyright: (c) 2004, 2003 Petr Novak +* Copyright: (c) 2002 Conexant Systems Inc. +* +***************************************************************************** + + This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + + This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + + You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., 59 +Temple Place, Suite 330, Boston, MA 02111-1307 USA + +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "serial_smc7004abrv2.h" +#include +#include + +static struct smc7004abrv2_serial uart_info; + +static int console_initialized = 0; + +DECLARE_TASK_QUEUE(tq_serial); + +struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +static struct tty_struct *serial_table[2]; +static struct termios *serial_termios[2]; +static struct termios *serial_termios_locked[2]; + +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 0 }; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the memcpy_fromfs blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char tmp_buf[SERIAL_XMIT_SIZE]; /* This is cheating */ +DECLARE_MUTEX(tmp_buf_sem); + +static inline int serial_paranoia_check(struct smc7004abrv2_serial *info, + dev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%d, %d) in %s\n"; + static const char *badinfo = + "Warning: null m68k_serial for (%d, %d) in %s\n"; + + if (!info) { + printk(badinfo, MAJOR(device), MINOR(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, MAJOR(device), MINOR(device), routine); + return 1; + } +#endif + return 0; +} + + + + +/* + * 1. Generic uart stuff + */ + + + +static void tx_enable(struct uart_regs *uart){ + uart->irqe |= IER_Tx_Holding_Reg; +} + + +static void tx_start(struct uart_regs *uart, int ints) +{ + if(ints) + tx_enable(uart); +} + + +static void start_rx(void) +{ + struct uart_regs *uart = uart_info.uart; + +#if 0 + if(uart_info.use_ints) +#endif + { + /* enable IRQs */ + uart->irqe |= IER_Rx_Holding_Reg; + } +} + + +static void wait_EOT(struct uart_regs *uart) +{ + volatile struct uart_regs* puart; + unsigned long timeout = 10000; + puart = (volatile struct uart_regs*)uart; + while(1){ + if(timeout-- == 0) { + printk("eot timeout\n"); + break; + } + if(puart->line_status_reg & LSR_Tx_Fifo_Empty) + break; + } +} + +static void xmit_string(struct smc7004abrv2_serial * info, char *p, int len) +{ + struct uart_regs *uart = info->uart; + uart->fifo = (char)*p; + tx_start(uart, info->use_ints); +} + + +static void smc7004abrv2_put_char(char ch) +{ + int flags = 0; + + if(uart_info.use_ints) + { + uart_info.uart->fifo=ch; + } + else + { + save_flags(flags); + cli(); + wait_EOT(uart_info.uart); + uart_info.uart->fifo=ch; + restore_flags(flags); + } +} + + +static void rs_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_put_char")) + { + return; + } + + smc7004abrv2_put_char(ch); +} + + +static void uart_speed(void) +{ + struct uart_regs *uart = uart_info.uart; + + uart->line_ctrl = LCR_Divisor_Latch; + uart->fifo = (char)UART_115200; /* lower baud rate div */ + uart->irqe = (char)(UART_115200>>8); /* upper baud rate div */ + uart->line_ctrl = 0; + uart->line_ctrl = LCR_8_Bit_Word_1; + uart->iir = (FCR_Fifo_Enable | FCR_Rx_Fifo_Reset | FCR_Tx_Fifo_Reset ); /* fifo ctrl */ + uart->irqe = IER_Rx_Holding_Reg; /* enable rx intr */ + tx_start(uart, uart_info.use_ints); + start_rx(); +} + + +/* + * 2. Kernel log console + */ + +static void smc7004abrv2_init_console(void) +{ + memset(&uart_info, 0, sizeof(struct smc7004abrv2_serial)); + + uart_info.uart = (struct uart_regs*)UART0_Base_Addr; + uart_info.tty = 0; + uart_info.irqmask = (1<index); +} + + +/* + * Output a single character, using UART polled mode. + * This is ised for console output. + */ +void smc7004abrv2_console_write(struct console *cp, const char *p, unsigned len) +{ + if (!console_initialized) + { + smc7004abrv2_init_console(); + uart_speed(); + } + while (len-- > 0) + { + if (*p == '\n') + { + smc7004abrv2_put_char('\r'); + } + smc7004abrv2_put_char(*p++); + } +} + + +struct console smc7004abrv2_console = { + name: "ttyS0", + write: smc7004abrv2_console_write, + read: NULL, + device: smc7004abrv2_console_device, + unblank: NULL, + setup: smc7004abrv2_console_setup, + flags: CON_PRINTBUFFER, + index: -1, + cflag: 0, + next: NULL +}; + +void smc7004abrv2_console_init(void) +{ + register_console(&smc7004abrv2_console); +} + +/* + * 3. serial tty stuff + */ + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(struct smc7004abrv2_serial *info) +{ + unsigned cflag; + int i; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + + /* set the baudrate */ + i = cflag & CBAUD; + + info->baud = baud_table[i]; + uart_speed(); + start_rx(); + tx_start(info->uart, info->use_ints); + return; +} + + +static void do_serial_hangup(void *private_) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + tty_hangup(tty); +} + + +static int startup(struct smc7004abrv2_serial * info) +{ + unsigned long flags; + + if (info->flags & S_INITIALIZED) + return 0; + + if (!info->xmit_buf) { + info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!info->xmit_buf) + return -ENOMEM; + } + + save_flags(flags); cli(); + +#if 0 + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + + USTCNT = USTCNT_UEN; + info->xmit_fifo_size = 1; + USTCNT = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN; + (void)URX; + + /* + * Finally, enable sequencing and interrupts + */ + +#ifdef USE_INTS + USTCNT = USTCNT_UEN | USTCNT_RXEN | + USTCNT_RX_INTR_MASK | USTCNT_TX_INTR_MASK; +#else + USTCNT = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK; +#endif +#endif + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * and set the speed of the serial port + */ +#if 0 + change_speed(info); +#endif + info->flags |= S_INITIALIZED; + restore_flags(flags); + return 0; +} + + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct smc7004abrv2_serial *info) +{ + DECLARE_WAITQUEUE(wait, current); + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & S_CLOSING) { + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + if (info->flags & S_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; +#else + return -EAGAIN; +#endif + + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & S_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & S_CALLOUT_ACTIVE) && + (info->flags & S_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & S_CALLOUT_ACTIVE) && + (info->flags & S_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= S_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & S_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= S_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & S_CALLOUT_ACTIVE) { + if (info->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, info->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); + + info->count--; + info->blocked_open++; + while (1) { + cli(); + if (!(info->flags & S_CALLOUT_ACTIVE)) + //m68k_rtsdtr(info, 1); + + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(info->flags & S_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & S_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & S_CALLOUT_ACTIVE) && + !(info->flags & S_CLOSING) && do_clocal) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; + + if (retval) + return retval; + info->flags |= S_NORMAL_ACTIVE; + return 0; +} + + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its S structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +int rs_open(struct tty_struct *tty, struct file * filp) +{ + int retval, line; + struct smc7004abrv2_serial *info; + + line = MINOR(tty->device) - tty->driver.minor_start; + + + if (line != 0) /* we have exactly one */ + return -ENODEV; + + info = &uart_info; + + if (serial_paranoia_check(info, tty->device, "rs_open")) + return -ENODEV; + + info->count++; + tty->driver_data = info; + info->tty = tty; + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) + return retval; + + retval = block_til_ready(tty, filp, info); + if (retval) { + + printk("rs_open returning after block_til_ready with %d\n", + retval); + return retval; + } + + if ((info->count == 1) && (info->flags & S_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + change_speed(info); + } + + info->session = current->session; + info->pgrp = current->pgrp; + + s5n8947_gpio_irq_mode(GPIOINT_UART1, SMC7004ABRV2_GPIO_INT_UART); + s5n8947_unmask_irq(GPIOINT_UART1); + return 0; +} + + + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct smc7004abrv2_serial * info) +{ + unsigned long flags; + + + if (!(info->flags & S_INITIALIZED)) + return; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....\n", info->line, + info->irq); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~S_INITIALIZED; + restore_flags(flags); +} + + +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + struct smc7004abrv2_serial * info = (struct smc7004abrv2_serial *)tty->driver_data; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk("rs_close: bad serial port count for ttyS%d: %d\n", + info->line, info->count); + info->count = 0; + } + if (info->count) { + restore_flags(flags); + return; + } + // closing port so disable interrupts + //set_ints_mode(0); + info->use_ints = 0; + + info->flags |= S_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & S_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & S_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != S_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + + 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 = 0; + if (tty->ldisc.num != ldiscs[N_TTY].num) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + tty->ldisc = ldiscs[N_TTY]; + tty->termios->c_line = N_TTY; + if (tty->ldisc.open) + (tty->ldisc.open)(tty); + } + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(S_NORMAL_ACTIVE|S_CALLOUT_ACTIVE| + S_CLOSING); + wake_up_interruptible(&info->close_wait); + restore_flags(flags); +} + +static void rs_flush_buffer(struct tty_struct *tty) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) + return; + cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + sti(); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +void rs_hangup(struct tty_struct *tty) +{ + struct smc7004abrv2_serial * info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info,tty->device, "rs_hangup")) + return; + + rs_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~(S_NORMAL_ACTIVE|S_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, total = 0; + unsigned long flags; + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info,tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit_buf) + return 0; + + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + down(&tmp_buf_sem); + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + up(&tmp_buf_sem); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped ){ + /* Enable transmitter */ + cli(); + + if(!info->use_ints){ + while(info->xmit_cnt) { + wait_EOT(info->uart); + /* Send char */ + info->uart->fifo = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + } + }else{ + if (info->xmit_cnt){ + /* Send char */ + wait_EOT(info->uart); + xmit_string(info,&info->xmit_buf[info->xmit_tail], info->xmit_cnt); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt=0; + } + } + restore_flags(flags); + } + + + restore_flags(flags); + return total; +} + +static int rs_write_room(struct tty_struct *tty) +{ + int ret; + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + if (serial_paranoia_check(info,tty->device, "rs_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) + return 0; + return info->xmit_cnt; +} + +static void rs_flush_chars(struct tty_struct *tty) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) + return; + if(!info->use_ints){ + for(;;) { + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + /* Enable transmitter */ + save_flags(flags); cli(); + tx_start(info->uart,info->use_ints); + } + }else{ + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + /* Enable transmitter */ + save_flags(flags); cli(); + tx_start(info->uart, info->use_ints); + } + + if(!info->use_ints) + wait_EOT(info->uart); + /* Send char */ + //xmit_char(info->xmit_buf[info->xmit_tail++]); + xmit_string(info,(char *)&info->xmit_buf[info->xmit_tail++],1); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + + restore_flags(flags); +} + + + +static void do_serial_bh(void) +{ + run_task_queue(&tq_serial); +} + +static void do_softint(void *private_) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; +#if 0 + if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +#endif +} + +/* + * This routine sends a break character out the serial port. + */ +static void send_break( struct smc7004abrv2_serial * info, int duration) +{ +#if 0 + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + cli(); + sti(); +#else + printk("send_break\n"); +#endif +} + +static int get_serial_info(struct smc7004abrv2_serial * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = info->port; + tmp.irq = info->irq; + tmp.flags = info->flags; + tmp.baud_base = info->baud_base; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = info->custom_divisor; + copy_to_user(retinfo,&tmp,sizeof(*retinfo)); + return 0; +} + + +static int set_serial_info(struct smc7004abrv2_serial * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct smc7004abrv2_serial old_info; + int retval = 0; + + if (!new_info) + return -EFAULT; + copy_from_user(&new_serial,new_info,sizeof(new_serial)); + old_info = *info; + + if (!suser()) { + if ((new_serial.baud_base != info->baud_base) || + (new_serial.type != info->type) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~S_USR_MASK) != + (info->flags & ~S_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~S_USR_MASK) | + (new_serial.flags & S_USR_MASK)); + info->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if (info->count > 1) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->baud_base = new_serial.baud_base; + info->flags = ((info->flags & ~S_FLAGS) | + (new_serial.flags & S_FLAGS)); + info->type = new_serial.type; + info->close_delay = new_serial.close_delay; + info->closing_wait = new_serial.closing_wait; + +check_and_exit: + retval = startup(info); + return retval; +} + + +static int get_lsr_info(struct smc7004abrv2_serial * info, unsigned int *value) +{ + unsigned char status; + + status = 0; + put_user(status,value); + return 0; +} + + +static void rs_throttle(struct tty_struct * tty) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + info->x_char = STOP_CHAR(tty); + + /* Turn off RTS line (do this atomic) */ +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) + return; +#if 0 + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + info->x_char = START_CHAR(tty); + } +#endif + /* Assert RTS line (do this atomic) */ +} + +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct smc7004abrv2_serial * info = (struct smc7004abrv2_serial *)tty->driver_data; + int retval; + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && + (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + send_break(info, HZ/4); /* 1/4 second */ + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + send_break(info, arg ? arg*(HZ/10) : HZ/4); + return 0; + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + case TIOCSSOFTCAR: + get_user(arg, (unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCGSERIAL: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct)); + if (error) + return error; + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERGETLSR: /* Get line status register */ + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + else + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct smc7004abrv2_serial)); + if (error) + return error; + copy_to_user((struct smc7004abrv2_serial *) arg, + info, sizeof(struct smc7004abrv2_serial)); + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + + +static void rs_stop(struct tty_struct *tty) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; +#if 0 + save_flags(flags); cli(); + tx_stop(info->uart); + restore_flags(flags); +#endif + +} + +static void rs_start(struct tty_struct *tty) +{ + unsigned long flags; + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info,tty->device, "rs_start")) + return; + + save_flags(flags); + cli(); + tx_start(info->uart, info->use_ints); + start_rx(); + restore_flags(flags); +} + +static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + + change_speed(info); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } + +} + + +static void rs_set_ldisc(struct tty_struct *tty) +{ + struct smc7004abrv2_serial *info = (struct smc7004abrv2_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_set_ldisc")) + return; + + info->is_cons = (tty->termios->c_line == N_TTY); + + printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off"); +} + + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static void rs_sched_event(struct smc7004abrv2_serial *info, int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static inline void receive_chars(struct smc7004abrv2_serial *info, unsigned long status) +{ + unsigned char ch; + + struct uart_regs *uart = info->uart; + +#if 0 + // hack to receive chars by polling from anywhere + struct tty_struct *tty = info->tty; + if (!(info->flags & S_INITIALIZED)) + return; +#else + struct tty_struct *tty = info->tty; + if (!(info->flags & S_INITIALIZED)) { + return; + } +#endif + + ch = (unsigned char)uart->fifo; + + if(info->is_cons) + { + + if (ch == 0x10) + { + show_state(); + show_free_areas(); + show_buffers(); + //show_net_buffers(); + return; + } + else if (ch == 0x12) + { + HARD_RESET_NOW(); + return; + } +#ifdef CONFIG_CONSOLE + // It is a 'keyboard interrupt' ;-) + wake_up(&keypress_wait); +#endif + } + + /* Look for kgdb 'stop' character, consult the gdb documentation + * for remote target debugging and arch/sparc/kernel/sparc-stub.c + * to see how all this works. + */ + + + if(!tty) + { + printk("no tty\n"); + goto clear_and_exit; + } +#if 0 + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + tty->flip.count++; + if(status & US_PARE) + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + else if(status & US_OVRE) + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + else if(status & US_FRAME) + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + else + *tty->flip.flag_buf_ptr++ = 0; +#endif + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = ch; + + + queue_task(&tty->flip.tqueue, &tq_timer); + + +clear_and_exit: + + return; +} + + +static inline void transmit_chars(struct smc7004abrv2_serial *info) +{ + struct uart_regs *uart = info->uart; + + if (info->x_char) { + /* Send next char */ + //xmit_char(info->x_char); + uart->fifo=info->x_char; + info->x_char = 0; + goto clear_and_return; + } + + if((info->xmit_cnt <= 0) || info->tty->stopped) { + /* That's peculiar... */ + goto clear_and_return; + } + + /* Send char */ + //xmit_char(info->xmit_buf[info->xmit_tail++]); + info->uart->fifo=info->xmit_buf[info->xmit_tail++]; + + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + + if (info->xmit_cnt < 256) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + + if(info->xmit_cnt <= 0) { + goto clear_and_return; + } + +clear_and_return: + /* Clear interrupt (should be auto)*/ + return; +} + + + +void rs_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + char status; + + struct smc7004abrv2_serial * info = &uart_info; + struct uart_regs *uart = uart_info.uart; + + status = (uart->iir & 0x0f); /* only concerned w/ lower nibble */ + + if (status == ISR_Tx_Rdy_Source) { + transmit_chars(info); + } + if ((status == ISR_Rx_Rdy_Source) || + (status == ISR_Rx_Rdy_TO_Src )){ + receive_chars(info,status); + } + #if 0 + if(!info->use_ints){ + serialpoll.data = (void *)&sp_uart_info; + queue_task_irq_off(&serialpoll, &tq_timer); + } + #endif + + + return; +} + + +static int __init +rs_smc7004abrv2_init(void) +{ + int flags; + struct smc7004abrv2_serial *info; + + /* Setup base handler, and timer table. */ + init_bh(SERIAL_BH, do_serial_bh); + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.name = "ttyS"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = 1; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B57600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_open; + serial_driver.close = rs_close; + serial_driver.write = rs_write; + serial_driver.put_char = rs_put_char; + serial_driver.flush_chars = rs_flush_chars; + serial_driver.write_room = rs_write_room; + serial_driver.chars_in_buffer = rs_chars_in_buffer; + serial_driver.flush_buffer = rs_flush_buffer; + serial_driver.ioctl = rs_ioctl; + serial_driver.throttle = rs_throttle; + serial_driver.unthrottle = rs_unthrottle; + serial_driver.set_termios = rs_set_termios; + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; + serial_driver.set_ldisc = rs_set_ldisc; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout driver\n"); + + save_flags(flags); cli(); + + + info = &uart_info; + info->magic = SERIAL_MAGIC; + info->uart = (struct uart_regs*)UART0_Base_Addr; + info->port = UART0_Base_Addr; + info->tty = 0; + info->irq = GPIOINT_UART1; + info->custom_divisor = 16; + info->close_delay = 50; + info->closing_wait = 3000; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->tqueue_hangup.routine = do_serial_hangup; + info->tqueue_hangup.data = info; + info->callout_termios =callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + info->line = 0; + info->is_cons = 1; /* Means shortcuts work */ + + printk("%s%d at 0x%08x (irq = %d)", serial_driver.name, info->line, + info->port, info->irq); + printk(" is a 16c550 UART\n"); + + s5n8947_gpio_irq_mode(GPIOINT_UART1, SMC7004ABRV2_GPIO_INT_UART); + request_irq(info->irq, rs_interrupt, SA_INTERRUPT, "smc7004abrv2serial", info); + s5n8947_unmask_irq(GPIOINT_UART1); + + + restore_flags(flags); + + return 0; +} + +module_init(rs_smc7004abrv2_init); diff -urN uClinux-2.4.22-uc1-virgin/drivers/char/serial_smc7004abrv2.h uClinux-2.4.22-uc1-smc/drivers/char/serial_smc7004abrv2.h --- uClinux-2.4.22-uc1-virgin/drivers/char/serial_smc7004abrv2.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/char/serial_smc7004abrv2.h 2004-05-02 17:42:08.000000000 +0200 @@ -0,0 +1,282 @@ +/**************************************************************************** +* +* Name: cnxtserial.h +* Based on: cnxtserial.h +* +* Description: Provides serial port character I/O (16550) definitions +* +* Copyright: (c) 2003 Petr Novak +* Copyright: (c) 2002 Conexant Systems Inc. +* +***************************************************************************** + + 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 _SMC7004ABRV2_H +#define _SMC7004ABRV2_H + +#include +#include + + +struct uart_regs +{ + volatile unsigned short fifo; + volatile unsigned short irqe; + volatile unsigned short iir; + volatile unsigned short line_ctrl; + volatile unsigned short modem_ctrl; + volatile unsigned short line_status_reg; + volatile unsigned short msr; + volatile unsigned short scratch; +}; + +/*******************************************************************************/ +/* Interrupt Status Register - ISR */ +/*******************************************************************************/ + +/* ISR b0 indicates that an interrupt is pending when clear. b3 - b1 signal which interrupt as per:- */ + +#define ISR_LSR_Source 0x06 /* 0 1 1 - Receiver Line Status Register Priority 1 */ +#define ISR_Rx_Rdy_Source 0x04 /* 0 1 0 - Received Data Ready Priority 2 */ +#define ISR_Rx_Rdy_TO_Src 0x0c /* 1 1 0 - Received Data Ready Time Out Priority 2 */ +#define ISR_Tx_Rdy_Source 0x02 /* 0 0 1 - Transmitter Holding Reg Empty Priority 3 */ +#define ISR_MODEM_Source 0x00 /* 0 0 0 - Modem Status Register Priority 4 */ + +/* ISR b7 - b4 are not used - in st16c552 b7 - b6 are Set b5 - b4 are Clear */ + +/*******************************************************************************/ +/* Interrupt Enable Register - IER */ +/*******************************************************************************/ +#define IER_Rx_Holding_Reg 0x01 /* b0 - Receive Holding Register Interrupt - Enabled When Set */ +#define IER_Tx_Holding_Reg 0x02 /* b1 - Transmit Holding Register Interrupt - Enabled When Set */ +#define IER_Rx_Line_Stat 0x04 /* b2 - Receiver Line Status Interrupt - Enabled When Set */ +#define IER_Modem_Status 0x08 /* b3 - Modem Status Register Interrupt - Enabled When Set */ + /* b7 - b4 are not used and are set to zero */ + +/*******************************************************************************/ +/* Line Status Register - LSR */ +/*******************************************************************************/ + +#define LSR_Rx_Data_Ready 0x01 /* b0 - Data Received and Saved in Holding Reg - Set when Valid */ +#define LSR_Overrun_Error 0x02 /* b1 - Overrun Error Occured - Set When Valid */ +#define LSR_Parity_Error 0x04 /* b2 - Received Data has Incorrect Parity - Set When Valid */ +#define LSR_Framing_Error 0x08 /* b3 - Framing Error (No Stop Bit) - Set When Valid */ +#define LSR_Break_Interrupt 0x10 /* b4 - Break Signal Received - Set When Valid */ +#define LSR_Tx_Hold_Empty 0x20 /* b5 - Tx Holding Register is empty and ready - Set When Valid */ +#define LSR_Tx_Fifo_Empty 0x40 /* b6 - Tx Shift Registers and FIFO are Empty - Set When Valid */ +#define LSR_Fifo_Error 0x80 /* b7 - At Least one of b4 - b2 has occurred - Set When Valid */ + +/*******************************************************************************/ +/* Latch Control Register - LCR */ +/*******************************************************************************/ + +/* LCR b2 defines the stop bits setup b1 - b0 define the Tx - Rx Word Length */ +/* The following defines cover all of the available options */ + +#define LCR_5_Bit_Word_1 0x00 /* 0 0 0 - 5 Bit Word - 1 Stop Bit */ +#define LCR_6_Bit_Word_1 0x01 /* 0 0 1 - 6 Bit Word - 1 Stop Bit */ +#define LCR_7_Bit_Word_1 0x02 /* 0 1 0 - 7 Bit Word - 1 Stop Bit */ +#define LCR_8_Bit_Word_1 0x03 /* 0 1 1 - 8 Bit Word - 1 Stop Bit */ +#define LCR_5_Bit_Word_1p5 0x04 /* 1 0 0 - 5 Bit Word - 1.5 Stop Bit */ +#define LCR_6_Bit_Word_2 0x05 /* 1 0 1 - 6 Bit Word - 2 Stop Bit */ +#define LCR_7_Bit_Word_2 0x06 /* 1 1 0 - 6 Bit Word - 1 Stop Bit */ +#define LCR_8_Bit_Word_2 0x07 /* 1 1 1 - 6 Bit Word - 1 Stop Bit */ + +#define LCR_Parity_Enable 0x08 /* b3 - Enable Parity Bit Generation and Check - Enabled When Set */ +#define LCR_Parity_Even 0x10 /* b4 - Odd/Even Parity Generation and Check - Even When Set */ +#define LCR_Parity_Set 0x20 /* b5 - Toggle Generated Parity Bit 0/1 - 0 When Set */ +#define LCR_Break_Set 0x40 /* b6 - Force Break Control ( Tx o/p low) - Forced When Set */ +#define LCR_Divisor_Latch 0x80 /* b7 - Enable Internal Baud Rate Latch - Enabled When Set */ + + +#define UART0_Base_Addr (EXTIO0+(0x3f8<<1)) + +/* We use a 1.8432Mhz clk */ +#define UART_CLK 1843200 +#define UART_2400 ((UART_CLK)/(16*2400)) +#define UART_4800 ((UART_CLK)/(16*4800)) +#define UART_9600 ((UART_CLK)/(16*9600)) +#define UART_19200 ((UART_CLK)/(16*19200)) +#define UART_38400 ((UART_CLK)/(16*38400)) +#define UART_57600 ((UART_CLK)/(16*57600)) +#define UART_115200 ((UART_CLK)/(16*115200)) + +#define UART_CTRL (UART0_Base_Addr+ ) +#define UART_BRD (UART0_Base_Addr+ ) +#define UART_DBUF (UART0_Base_Addr+ ) +#define UART_ISR (UART0_Base_Addr+ ) + +/*******************************************************************************/ +/* FIFO Control Register - FCR */ +/*******************************************************************************/ +#define FCR_Fifo_Enable 0x01 /* b0 - Tx and Rx FIFO Enable - Enabled When Set */ +#define FCR_Rx_Fifo_Reset 0x02 /* b1 - Clear Rx FIFO and reset its counter - Clears When Set */ +#define FCR_Tx_Fifo_Reset 0x04 /* b2 - Clear Tx FIFO and reset its counter - Clears When Set */ +#define FCR_DMA_Mode_Select 0x08 /* b3 - Change DMA Mode State from m0 to m1 - Mode 1 When Set */ + +/* FCR b7 - b6 FIFO Trigger Level */ + +#define FCR_Rx_Trig_Lvl_01 0x00 /* 0 0 - FIFO Rx Trigger Level 01 */ +#define FCR_Rx_Trig_Lvl_04 0x40 /* 0 1 - FIFO Rx Trigger Level 04 */ +#define FCR_Rx_Trig_Lvl_08 0x80 /* 1 0 - FIFO Rx Trigger Level 08 */ +#define FCR_Rx_Trig_Lvl_16 0xc0 /* 1 1 - FIFO Rx Trigger Level 16 */ + + + +struct serial_struct { + int type; + int line; + int port; + int irq; + int flags; + int xmit_fifo_size; + int custom_divisor; + int baud_base; + unsigned short close_delay; + char reserved_char[2]; + int hub6; /* FIXME: We don't have AT&T Hub6 boards! */ + unsigned short closing_wait; /* time to wait before closing */ + unsigned short closing_wait2; /* no longer used... */ + int reserved[4]; +}; + +/* + * For the close wait times, 0 means wait forever for serial port to + * flush its output. 65535 means don't wait at all. + */ +#define S_CLOSING_WAIT_INF 0 +#define S_CLOSING_WAIT_NONE 65535 + +/* + * Definitions for S_struct (and serial_struct) flags field + */ +#define S_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes + on the callout port */ +#define S_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ +#define S_SAK 0x0004 /* Secure Attention Key (Orange book) */ +#define S_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ + +#define S_SPD_MASK 0x0030 +#define S_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ + +#define S_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ +#define S_SPD_CUST 0x0030 /* Use user-specified divisor */ + +#define S_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ +#define S_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ +#define S_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ +#define S_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ +#define S_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ + +#define S_FLAGS 0x0FFF /* Possible legal S flags */ +#define S_USR_MASK 0x0430 /* Legal flags that non-privileged + * users can set or reset */ + +/* Internal flags used only by kernel/chr_drv/serial.c */ +#define S_INITIALIZED 0x80000000 /* Serial port was initialized */ +#define S_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ +#define S_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ +#define S_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ +#define S_CLOSING 0x08000000 /* Serial port is closing */ +#define S_CTS_FLOW 0x04000000 /* Do CTS flow control */ +#define S_CHECK_CD 0x02000000 /* i.e., CLOCAL */ + +/* Software state per channel */ + +#ifdef __KERNEL__ +/* + * This is our internal structure for each serial port's state. + * + * Many fields are paralleled by the structure used by the serial_struct + * structure. + * + * For definitions of the flags field, see tty.h + */ + +struct smc7004abrv2_serial { + char soft_carrier; /* Use soft carrier on this channel */ + char break_abort; /* Is serial console in, so process brk/abrt */ + char is_cons; /* Is this our console. */ + + /* We need to know the current clock divisor + * to read the bps rate the chip has currently + * loaded. + */ + unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */ + int baud; + int magic; + int baud_base; + int port; + int irq; + int irqmask; + int flags; /* defined in tty.h */ + int type; /* UART type */ + int use_ints; + struct uart_regs *uart; + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int timeout; + int xmit_fifo_size; + int custom_divisor; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + unsigned short closing_wait2; + unsigned long event; + unsigned long last_active; + int line; + int count; /* # of fd on device */ + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + unsigned char *xmit_buf; + unsigned char *rx_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + struct termios normal_termios; + struct termios callout_termios; + //struct wait_queue *open_wait; + //struct wait_queue *close_wait; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; +}; + + +#define SERIAL_MAGIC 0x5301 + +/* + * The size of the serial xmit buffer is 1 page, or 4096 bytes + */ +#define SERIAL_XMIT_SIZE PAGE_SIZE +#define SERIAL_RX_SIZE PAGE_SIZE + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define RS_EVENT_WRITE_WAKEUP 0 + +#endif /* __KERNEL__ */ + + + +#endif diff -urN uClinux-2.4.22-uc1-virgin/drivers/char/tty_io.c uClinux-2.4.22-uc1-smc/drivers/char/tty_io.c --- uClinux-2.4.22-uc1-virgin/drivers/char/tty_io.c 2003-12-11 08:13:19.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/char/tty_io.c 2004-05-02 17:36:57.000000000 +0200 @@ -174,6 +174,7 @@ extern void c5471_console_init(void); extern void bf535_console_init(void); extern void bf533_console_init(void); +extern void smc7004abrv2_console_init(void); #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -2333,6 +2334,9 @@ #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE samsung_console_init(); #endif +#ifdef CONFIG_SERIAL_SMC7004ABRV2_CONSOLE + smc7004abrv2_console_init(); +#endif #ifdef CONFIG_SERIAL_S3C3410_CONSOLE s3c3410_console_init(); #endif diff -urN uClinux-2.4.22-uc1-virgin/drivers/net/Config.in uClinux-2.4.22-uc1-smc/drivers/net/Config.in --- uClinux-2.4.22-uc1-virgin/drivers/net/Config.in 2003-11-20 20:19:42.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/net/Config.in 2004-05-02 17:36:57.000000000 +0200 @@ -31,6 +31,9 @@ string 'S3C2500 MAC Address' S3C2500_HWADDR 00:00:00:00:00:00 fi fi + if [ "$CONFIG_CPU_S5N8947" = "y" ]; then + tristate ' Samsung S5N8947 ethernet support' CONFIG_ETH_S5N8947 + fi if [ "$CONFIG_CPU_S3C4510" = "y" ]; then tristate ' Samsung 4510B ethernet support' CONFIG_ETH_S3C4510 fi diff -urN uClinux-2.4.22-uc1-virgin/drivers/net/Makefile uClinux-2.4.22-uc1-smc/drivers/net/Makefile --- uClinux-2.4.22-uc1-virgin/drivers/net/Makefile 2003-09-12 20:41:59.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/drivers/net/Makefile 2004-05-02 17:36:57.000000000 +0200 @@ -162,6 +162,7 @@ obj-$(CONFIG_VIA_RHINE_FET) += linuxfet.o obj-$(CONFIG_ETH_S3C2500) += s3c2500.o obj-$(CONFIG_ETH_S3C4510) += s3c4510.o +obj-$(CONFIG_ETH_S5N8947) += s5n8947.o obj-$(CONFIG_ETH_S3C4530) += s3c4530.o obj-$(CONFIG_SMC91111) += smc91111.o obj-$(CONFIG_FEC) += fec.o diff -urN uClinux-2.4.22-uc1-virgin/drivers/net/s3c4530.c uClinux-2.4.22-uc1-smc/drivers/net/s3c4530.c --- uClinux-2.4.22-uc1-virgin/drivers/net/s3c4530.c 2003-12-15 12:36:13.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/net/s3c4530.c 2004-05-02 17:36:57.000000000 +0200 @@ -1207,7 +1207,7 @@ static _INLINE_ void SetSpeed10( void) { - WriteCSR( MacCtl, (( ReadCSR( MacCtl)) & ~MCtlLoop10 ); + WriteCSR( MacCtl, (( ReadCSR( MacCtl)) & ~MCtlLoop10 )); } #endif // ENABLE_IOCTL /************************************************************************ @@ -1238,16 +1238,16 @@ Tag &= 0x3fff000; tagline = 0xff & (bufaddr >> L1_CACHE_SHIFT); - if ( (Tag >> (16 - L1_CACHE_SHIFT) == (0x3fff & TagRamPtr[ tagline ])) + if ( (Tag >> (16 - L1_CACHE_SHIFT) == (0x3fff & TagRamPtr[ tagline ])) ) { - TagRomPtr[ tagline } &= 0xbfffffff; + TagRomPtr[ tagline ] &= 0xbfffffff; } else { // checking against Set 1 tag / / / / NOTE: the following not fixed for L1_CACHE_SHIFT / / ...MaTed--- if ( (Tag << 3) == (0x1fff8000 & TagRamPtr[ tagline ])) { - TagRomPtr[ tagline } &= 0x7fffffff; // top bit + TagRomPtr[ tagline ] &= 0x7fffffff; // top bit } } #endif diff -urN uClinux-2.4.22-uc1-virgin/drivers/net/s5n8947.c uClinux-2.4.22-uc1-smc/drivers/net/s5n8947.c --- uClinux-2.4.22-uc1-virgin/drivers/net/s5n8947.c 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/net/s5n8947.c 2004-05-02 20:52:41.000000000 +0200 @@ -0,0 +1,620 @@ +/* + * linux/deriver/net/s5n8947.c + * Ethernet driver for Samsung S5N8947 + * Based on s3c4510.c with very minor changes + * Copyright (C) 2004 Petr Novak + * Copyright (C) 2002 Mac Wang + */ + +#include +#include + +#include +#include +#include // printk() +#include // kmalloc() +#include // error codes +#include // size_t +#include // mark_bh + +#include +#include // net_device +#include +#include +#include +#include + +#include +#include "s5n8947.h" + +#undef DEBUG +#ifdef DEBUG +#define TRACE(str, args...) printk("S5N8947 eth: " str, ## args) +#else +#define TRACE(str, args...) +#endif + + +static char mac0[18] = "00:04:11:22:33:44"; // can be changed with kernel parameter +static int timeout = 100; // tx watchdog ticks 100 = 1s +static char *version = "Samsung S5N8947 Ethernet driver version 0.1\n"; + +/* + * This structure is private to each device. It is used to pass + * packets in and out, so there is place for a packet + */ +struct s5n8947_priv { + struct net_device_stats stats; + + unsigned long tx_ptr; + unsigned long gtx_ptr; + unsigned long rx_ptr; + + // Frame Descriptor linked list for S5N8947 MAC, initialized by FD_init() + volatile struct FrameDesc rx_fd[RX_FRAME_SIZE]; + volatile struct FrameDesc tx_fd[TX_FRAME_SIZE]; + + // Frame Buffer for S5N8947 MAC + volatile struct ethframe rx_buf[RX_FRAME_SIZE]; + volatile struct ethframe tx_buf[TX_FRAME_SIZE]; + + spinlock_t lock; + + struct sk_buff *skb; +}; + +/* --------------------- * + * MII support functions * + * --------------------- */ +void MIIWrite(unsigned long PhyInAddr, unsigned long PhyAddr, unsigned long PhyData) +{ + CSR_WRITE(STADATA, PhyData); + CSR_WRITE(STACON, PhyInAddr | PhyAddr | MiiBusy | PHYREGWRITE); + while(CSR_READ(STACON) & MiiBusy); +} + +unsigned long MIIRead(unsigned long PhyInAddr, unsigned long PhyAddr) +{ + CSR_WRITE(STACON, PhyInAddr | PhyAddr | MiiBusy); + while(CSR_READ(STACON) & MiiBusy); + return CSR_READ(STADATA); +} + +/* + * + */ +void FD_Init(struct net_device *dev) +{ + struct s5n8947_priv *priv = (struct s5n8947_priv *) dev->priv; + + struct FrameDesc *FD_ptr; + struct FrameDesc *FD_start_ptr; + struct FrameDesc *FD_last_ptr = NULL; + unsigned long FB_base; + unsigned long i; + + // TxFDInitialize() in diag code + // Get Frame descriptor's base address + // +0x4000000 is for setting this area to non-cacheable area. + CSR_WRITE(BDMATXPTR, (unsigned long) priv->tx_fd + 0x4000000); + priv->gtx_ptr = priv->tx_ptr = CSR_READ(BDMATXPTR); + + // Get Transmit buffer base address. + FB_base = (unsigned long) priv->tx_buf + 0x4000000; + + // Generate linked list. + FD_start_ptr = FD_ptr = (struct FrameDesc *)priv->tx_ptr; + FD_last_ptr = NULL; + + for(i = 0; i < TX_FRAME_SIZE; i++) { + if(FD_last_ptr == NULL) + FD_last_ptr = FD_ptr; + else + FD_last_ptr -> NextFrameDescriptor = (unsigned long)FD_ptr; + + FD_ptr->FrameDataPtr = (unsigned long)(FB_base & CPU_owner); + FD_ptr->Reserved = 0; + FD_ptr->StatusAndFrameLength = (unsigned long)0x0; + FD_last_ptr = FD_ptr; + FD_ptr++; + FB_base += sizeof(struct ethframe); + } + + //Make Frame descriptor to ring buffer type. + FD_last_ptr->NextFrameDescriptor = (unsigned long)FD_start_ptr; + + // RxFDInitialize() in diag code + // Get Frame descriptor's base address + CSR_WRITE(BDMARXPTR, (unsigned long) priv->rx_fd + 0x4000000); + priv->rx_ptr = CSR_READ(BDMARXPTR); + + // Get Transmit buffer base address + FB_base = (unsigned long)priv->rx_buf + 0x4000000; + + // Generate linked list + FD_start_ptr = FD_ptr = (struct FrameDesc *)priv->rx_ptr; + FD_last_ptr = NULL; + + for(i = 0; i < RX_FRAME_SIZE; i++) { + if(FD_last_ptr == NULL ) + FD_last_ptr = FD_ptr; + else + FD_last_ptr -> NextFrameDescriptor = (unsigned long)FD_ptr; + + FD_ptr->FrameDataPtr = (unsigned long)(FB_base | BDMA_owner); + FD_ptr->Reserved = 0; + FD_ptr->StatusAndFrameLength = (unsigned long)0x0; + FD_ptr->NextFrameDescriptor = 0x0; + + FD_last_ptr = FD_ptr; + FD_ptr++; + FB_base += sizeof(struct ethframe); + } + + // Make Frame descriptor to ring buffer type. + FD_last_ptr->NextFrameDescriptor = (unsigned long)FD_start_ptr; +} + + +/* + * rx + */ +void s5n8947_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + int i; + int len; + unsigned char *data; + struct sk_buff *skb; + struct FrameDesc *FD_ptr; + unsigned long CFD_ptr; + unsigned long RxStat; + unsigned long BDMAStat; + struct net_device *dev = (struct net_device *) dev_id; + struct s5n8947_priv *priv = (struct s5n8947_priv *) dev->priv; + TRACE("rx\n"); + + spin_lock(&priv->lock); + + // 1. Get current frame descriptor and status + CFD_ptr = CSR_READ(BDMARXPTR); + BDMAStat = CSR_READ(BDMASTAT); + + // 2. Clear BDMA status register bit by write 1 + CSR_WRITE(BDMASTAT, BDMAStat | S_BRxRDF); + + do { + // 3. Check Null List Interrupt + /* + if(CSR_READ(BDMASTAT) & BRxNL) { + CSR_WRITE(BDMASTAT, ); + } + */ + + // 4. Get Rx Frame Descriptor + FD_ptr = (struct FrameDesc *) priv->rx_ptr; + RxStat = (FD_ptr->StatusAndFrameLength >> 16) & 0xffff; + + // 5. If Rx frame is good, then process received frame + if(RxStat & Good){ +#if 0 + len = (FD_ptr->StatusAndFrameLength & 0xffff) - 4 - 4; +#else + len = (FD_ptr->StatusAndFrameLength & 0xffff) - 4; +#endif + data = (unsigned char *) FD_ptr->FrameDataPtr + 2; + + // 6. Get received frame to memory buffer + skb = dev_alloc_skb(len+2); + if(!skb) { + printk("S5N8947 eth: low on mem - packet dropped\n"); + priv->stats.rx_dropped++; + return; + } + + skb->dev = dev; + skb_reserve(skb, 2); +#if 0 + memcpy(skb->data, data, 12); + memcpy(skb->data+12, data+12+4, len-12); + skb_put(skb, len); +#else + skb_put(skb, len); + eth_copy_and_sum(skb, data, len, 0); +#endif + skb->protocol = eth_type_trans(skb, dev); + priv->stats.rx_packets++; + priv->stats.rx_bytes += len; + netif_rx(skb); + } + else { + // 7. If Rx frame has error, then process err frame + priv->stats.rx_errors++; + if(RxStat & LongErr) + priv->stats.rx_length_errors++; + if(RxStat & OvMax) + priv->stats.rx_over_errors++; + if(RxStat & CRCErr) + priv->stats.rx_crc_errors++; + if(RxStat & AlignErr) + priv->stats.rx_frame_errors++; + if(RxStat & Overflow) + priv->stats.rx_fifo_errors++; + + } + // 8. Change ownership to BDMA for next use + FD_ptr -> FrameDataPtr |= BDMA_owner; + + // Save Current Status and Frame Length field, and clear + FD_ptr -> StatusAndFrameLength = 0x0; + + // 9. Get Next Frame Descriptor pointer to process + priv->rx_ptr = (unsigned long)(FD_ptr -> NextFrameDescriptor); + }while(CFD_ptr != priv->rx_ptr); + + // 10. Check Notowner status + if(CSR_READ(BDMASTAT) & S_BRxNO) { + CSR_WRITE(BDMASTAT, S_BRxNO); + } + + spin_unlock(&priv->lock); +} + +/* + * tx + */ +void s5n8947_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct FrameDesc *FD_ptr; + unsigned long CFD_ptr; + unsigned long *FB_ptr; + unsigned long status; + struct net_device *dev = (struct net_device *) dev_id; + struct s5n8947_priv *priv = (struct s5n8947_priv *) dev->priv; + TRACE("tx\n"); + + spin_lock(&priv->lock); + + CFD_ptr = CSR_READ(BDMATXPTR); + while(priv->gtx_ptr != CFD_ptr) + { + FD_ptr = (struct FrameDesc *) priv->gtx_ptr; + FB_ptr = (unsigned long *) &FD_ptr->FrameDataPtr; + + if(!(*FB_ptr & BDMA_owner)) + { + status = (FD_ptr->StatusAndFrameLength >> 16) & 0xffff; + if(status & Comp) { + priv->stats.tx_packets++; + } + else { + priv->stats.tx_errors++; + if(status & NCarr) + priv->stats.tx_carrier_errors++; + if(status & Under) + priv->stats.tx_fifo_errors++; + if(status & LateColl) + priv->stats.tx_window_errors++; + if(status & ExColl) + priv->stats.collisions++; + } + } + + // Clear Framedata pointer already used + FD_ptr->StatusAndFrameLength = 0x0; + priv->gtx_ptr = (unsigned long) FD_ptr->NextFrameDescriptor; + } + + spin_unlock(&priv->lock); + return; +} + +/* + * Open and Close + */ +int s5n8947_open(struct net_device *dev) +{ + int i; + unsigned long status; + MOD_INC_USE_COUNT; + + TRACE("open\n"); + + // Disable irqs + disable_irq(INT_ETH0TX); + disable_irq(INT_ETH0RX); + + // register rx isr + if(request_irq(INT_ETH0RX, &s5n8947_rx, SA_INTERRUPT, "eth0 rx isr", dev)) { + printk(KERN_ERR "s5n8947: Can't get irq %d\n", INT_ETH0RX); + return -EAGAIN; + } + + // register tx isr + if(request_irq(INT_ETH0TX, &s5n8947_tx, SA_INTERRUPT, "eth0 tx isr", dev)) { + printk(KERN_ERR "s5n8947: Can't get irq %d\n", INT_ETH0TX); + return -EAGAIN; + } +#if 0 + printk("Reseting PHY\n"); + MIIWrite(0x0, 0x0, 1<<15); + while((MIIRead(0x0, 0x0) & (1<<15))); + + + printk("Perform AN again\n"); + MIIWrite(0x0, 0x0, MIIRead(0x0, 0x0) & ~(1<<12)); +// while(!(MIIRead(0x11, 0x0) & (1<<4))); + + MIIWrite(0x0, 0x0, 1<<13|1<<8); + + status = MIIRead(0x11, 0x0); + if(status & 1<<15) + printk("10M/b "); + else + printk("100M/b "); + if(status & 1<<14) + printk("Full duplex\n"); + else printk("Half duplex\n"); +#endif + // reset BDMA and MAC + CSR_WRITE(BDMARXCON, BRxRS); + CSR_WRITE(BDMATXCON, BTxRS); + CSR_WRITE(MACON, Reset); + CSR_WRITE(BDMARXLSZ, sizeof(struct ethframe)); + CSR_WRITE(MACON, gMACCON); + + FD_Init(dev); + + for(i = 0; i < (int)dev->addr_len-2; i++) + CAM_Reg(0) = (CAM_Reg(0) << 8) | dev->dev_addr[i]; + for(i = (int)dev->addr_len-2; i < (int)dev->addr_len; i++) + CAM_Reg(1) = (CAM_Reg(1) << 8) | dev->dev_addr[i]; + CAM_Reg(1) = (CAM_Reg(1) << 16); + + CSR_WRITE(CAMEN, 0x0001); + CSR_WRITE(CAMCON, gCAMCON); + + enable_irq(INT_ETH0RX); + enable_irq(INT_ETH0TX); + + // ReadyMACTx(); + CSR_WRITE(BDMATXCON, gBDMATXCON); + CSR_WRITE(MACTXCON, gMACTXCON); + // ReadyMACRx(); + CSR_WRITE(BDMARXCON, gBDMARXCON); + CSR_WRITE(MACRXCON, gMACRXCON); + + // Start the transmit queue + netif_start_queue(dev); + + return 0; +} + +int s5n8947_stop(struct net_device *dev) +{ + TRACE("stop\n"); + CSR_WRITE(BDMATXCON, 0); + CSR_WRITE(BDMARXCON, 0); + CSR_WRITE(MACTXCON, 0); + CSR_WRITE(MACRXCON, 0); + + free_irq(INT_ETH0RX, dev); + free_irq(INT_ETH0TX, dev); + + netif_stop_queue(dev); + MOD_DEC_USE_COUNT; + + return 0; +} + +int s5n8947_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +// int i; + int len; + char *data; + struct s5n8947_priv *priv = (struct s5n8947_priv *) dev->priv; + + struct FrameDesc *FD_ptr; // frame descriptor pointer + volatile unsigned long *FB_ptr; // frame data pointer + unsigned char *FB_data; // frame data + + TRACE("start_xmit\n"); +/* + printk("sk_buff->len: %d\n", skb->len); + printk("sk_buff->data_len: %d\n", skb->data_len); + for(i = 0; i < skb->len; i++) { + printk("%4x", skb->data[i]); + if((i+1)%16 ==0) + printk("\n"); + } + printk("\n"); +*/ + len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; + data = skb->data; + dev->trans_start = jiffies; + + // 1. Get Tx frame descriptor & data pointer + FD_ptr = (struct FrameDesc *) priv->tx_ptr; + FB_ptr = (unsigned long *) &FD_ptr->FrameDataPtr; + FB_data = (unsigned char *) FD_ptr->FrameDataPtr; + + // 2. Check BDMA ownership + if(*FB_ptr & BDMA_owner) { + printk("no tx buffer\n"); +// check it later +// netif_stop_queue(dev); + return 1; + } + + // 3. Prepare Tx Frame data to Frame buffer + memcpy(FB_data, data, len); + + // 4. Set Tx Frame flag & Length Field + FD_ptr->Reserved = (Padding | CRCMode | FrameDataPtrInc | LittleEndian | WA00 | MACTxIntEn); + FD_ptr->StatusAndFrameLength = (len & 0xFFFF); + + // 5. Change ownership to BDMA + FD_ptr->FrameDataPtr |= BDMA_owner; + + // 6. Enable MAC and BDMA Tx control register + CSR_WRITE(BDMATXCON, gBDMATXCON); + CSR_WRITE(MACTXCON, gMACTXCON); + + // 7. Change the Tx frame descriptor for next use + priv->tx_ptr = (unsigned long)(FD_ptr->NextFrameDescriptor); + + dev_kfree_skb(skb); + + return 0; +} + +struct net_device_stats *s5n8947_get_stats(struct net_device *dev) +{ + struct s5n8947_priv *priv = (struct s5n8947_priv *) dev->priv; + TRACE("get_stats\n"); + return &priv->stats; +} + + +static int +parse_mac_addr(struct net_device *dev, char* macstr) +{ + int i, j; + unsigned char result, value; + + for (i=0; i<6; i++) { + result = 0; + if (i != 5 && *(macstr+2) != ':') { + printk(KERN_ERR "s5n8947eth: invalid mac address format: %d %c\n", + i, *(macstr+2)); + return -EINVAL; + } + + for (j=0; j<2; j++) { + register char c = *macstr; + if (c >= '0' && c <= '9') { + result = (result << 4) + c - '0'; + macstr++; + } else if ((c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { + result = (result << 4) + (c & (~('A'-'a'))) - 'A' + 10; + macstr++; + } else { + printk(KERN_ERR "s5n8947eth: invalid mac address " + "character: %c\n", *macstr); + return -EINVAL; + } + } + + macstr++; // step over ':' + dev->dev_addr[i] = result; + } + + return 0; +} + +/* + * The init function, invoked by register_netdev() + */ +int s5n8947_init(struct net_device *dev) +{ + int i; + TRACE("init\n"); + ether_setup(dev); // Assign some of the fields + + // set net_device methods + dev->open = s5n8947_open; + dev->stop = s5n8947_stop; +// dev->ioctl = s5n8947_ioctl; + dev->get_stats = s5n8947_get_stats; +// dev->tx_timeout = s5n8947_tx_timeout; + dev->hard_start_xmit = s5n8947_start_xmit; + + // set net_device data members + dev->watchdog_timeo = timeout; + dev->irq = INT_ETH0RX; + dev->dma = 0; + +#if 0 + // set MAC address manually + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0x40; + dev->dev_addr[2] = 0x95; + dev->dev_addr[3] = 0x36; + dev->dev_addr[4] = 0x35; + dev->dev_addr[5] = 0x34; +#else + parse_mac_addr(dev, mac0); +#endif + + printk(KERN_INFO "%s: ", dev->name); + for(i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], (i==5) ? ' ' : ':'); + printk("\n"); + + SET_MODULE_OWNER(dev); + + dev->priv = kmalloc(sizeof(struct s5n8947_priv), GFP_KERNEL); + if(dev->priv == NULL) + return -ENOMEM; + memset(dev->priv, 0, sizeof(struct s5n8947_priv)); + spin_lock_init(&((struct s5n8947_priv *) dev->priv)->lock); + return 0; +} + +struct net_device s5n8947_netdevs = { + init: s5n8947_init, +}; + +#ifndef MODULE + +static int __init s5n8947eth_setup(char *options) +{ + char *this_opt; + + if (!options || !*options) + return 0; + + for (this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL, ",")) { + if(!strncmp(this_opt, "mac0:", 5)) { + memcpy(mac0, this_opt+5, 17); + mac0[17] = '\0'; + } + } + + return 1; +} + +__setup("s5n8947eth=", s5n8947eth_setup); + +#endif /* ! MODULE */ + +/* + * Finally, the module stuff + */ +int __init s5n8947_init_module(void) +{ + int result; + TRACE("init_module\n"); + + //Print version information + printk(KERN_INFO "%s", version); + + //register_netdev will call s5n8947_init() + if((result = register_netdev(&s5n8947_netdevs))) + printk("S5N8947 eth: Error %i registering device \"%s\"\n", result, s5n8947_netdevs.name); + return result ? 0 : -ENODEV; +} + +void __exit s5n8947_cleanup(void) +{ + TRACE("cleanup\n"); + kfree(s5n8947_netdevs.priv); + unregister_netdev(&s5n8947_netdevs); + return; +} + +module_init(s5n8947_init_module); +module_exit(s5n8947_cleanup); + +MODULE_DESCRIPTION("Samsung S5N8947 ethernet driver"); +MODULE_AUTHOR("Petr Novak "); +MODULE_LICENSE("GPL"); diff -urN uClinux-2.4.22-uc1-virgin/drivers/net/s5n8947.h uClinux-2.4.22-uc1-smc/drivers/net/s5n8947.h --- uClinux-2.4.22-uc1-virgin/drivers/net/s5n8947.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/drivers/net/s5n8947.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,212 @@ +#ifndef __S5N8947_H +#define __S5N8947_H + +#define RX_FRAME_SIZE 10 +#define TX_FRAME_SIZE 5 + +struct FrameDesc { + unsigned long FrameDataPtr; // [31] Ownership 0=CPU, 1=BDMA + unsigned long Reserved; + unsigned long StatusAndFrameLength; // [31:16] Rx Status, [15:0] Frame Length + unsigned long NextFrameDescriptor; +}; + +struct ethframe { + unsigned char dst[ETH_ALEN]; + unsigned char src[ETH_ALEN]; + unsigned short proto; + unsigned char data[ETH_DATA_LEN+22]; +}; + +struct s5n8947_mac_init_block { + unsigned short mode; + unsigned char phys_addr[6]; +// unsigned filter[2]; + + unsigned long tx_ptr; + unsigned long gtx_ptr; + unsigned long rx_ptr; + + struct FrameDesc rx_fd[RX_FRAME_SIZE]; + struct FrameDesc tx_fd[TX_FRAME_SIZE]; + + struct ethframe rx_buf[RX_FRAME_SIZE]; + struct ethframe tx_buf[TX_FRAME_SIZE]; +}; + +/* -------------------------------------------- * + * Definitions of Samsung S5N8947 MAC registers * + * -------------------------------------------- */ + +#define MaxRxFrameSize (1520) + +/* -------------------------------------- * + * Buffered DMA Transmit Control Register * + * -------------------------------------- */ +#define BTxBRST (0x0000F) /* [4:0] */ +#define BTxSTSKO (0x00020) +#define BTxCCPIE (0x00080) +#define BTxNLIE (0x00100) +#define BTxNOIE (0x00200) +#define BTxEmpty (0x00400) +//#define BTxMSL /* [13:11] */ +#define BTxMSL000 (0x00000) +#define BTxMSL001 (0x00800) +#define BTxMSL010 (0x01000) +#define BTxMSL011 (0x01800) +#define BTxMSL100 (0x02000) +#define BTxMSL101 (0x02800) +#define BTxMSL110 (0x03000) +#define BTxMSL111 (0x03800) +#define BTxEn (0x04000) +#define BTxRS (0x08000) + +/* ------------------------------------- * + * Buffered DMA Receive Control Register * + * ------------------------------------- */ +#define BRxBRST (0x0000F) /* [4:0] */ +#define BRxSTSKO (0x00020) +#define BRxMAINC (0x00040) +#define BRxDIE (0x00080) +#define BRxNLIE (0x00100) +#define BRxNOIE (0x00200) +#define BRxMSOIE (0x00400) +#define BRxLittle (0x00800) +//#define BRxWA /* [13:12] */ +#define BRxWA01 (0x01000) +#define BRxWA10 (0x02000) +#define BRxWA11 (0x03000) +#define BRxEn (0x04000) +#define BRxRS (0x08000) +#define BRxEmpty (0x10000) +#define BRxEarly (0x20000) + +/* -------------------- * + * BDMA Status Register * + * -------------------- */ +#define S_BRxRDF (0x00001) +#define S_BRxNL (0x00002) +#define S_BRxNO (0x00004) +#define S_BRxMSO (0x00008) +#define S_BRxEmpty (0x00010) +#define S_BRxSEarly (0x00020) +#define S_BRxFRF (0x00080) +#define S_BRxNFR (0x00080) +#define S_BTxCCP (0x10000) +#define S_BTxNL (0x20000) +#define S_BTxNO (0x40000) +#define S_BTxEmpty (0x100000) + +/* -------------------- * + * MAC Control Register * + * -------------------- */ +#define HaltReq (0x00001) +#define HaltImm (0x00002) +#define Reset (0x00004) +#define FullDup (0x00008) +#define MACLoop (0x00010) +#define MIIOFF (0x00040) +#define Loop10 (0x00080) +#define MissRoll (0x00400) +#define MDCOFF (0x01000) +#define EnMissRoll (0x02000) +#define Link10 (0x08000) + +/* ----------------------------- * + * MAC Transmit Control Register * + * ----------------------------- */ +#define TxEn (0x0001) +#define TxHalt (0x0002) +#define NoPad (0x0004) +#define NoCRC (0x0008) +#define FBack (0x0010) +#define NoDef (0x0020) +#define SdPause (0x0040) +#define SQEn (0x0080) +#define EnComp (0x4000) /* FIXME!!! */ + +/* ---------------------------- * + * MAC Receive Control Register * + * ---------------------------- */ +#define RxEn (0x0001) +#define RxHalt (0x0002) +#define LongEn (0x0004) +#define ShortEn (0x0008) +#define StripCRC (0x0010) +#define PassCtl (0x0020) +#define IgnoreCRC (0x0040) + +/* -------------------- * + * CAM Control Register * + * -------------------- */ +#define StationAcc (0x0001) +#define GroupAcc (0x0002) +#define BroadAcc (0x0004) +#define NegCAM (0x0008) +#define CompEn (0x0010) + +/* Tx & Rx Frame Descriptor Ownership bit[31](O) */ +#define BDMA_owner 0x80000000 /* BDMA */ +#define CPU_owner 0x7fffffff /* CPU */ + +/* ------------------------------- * + * Tx Frame Descriptor Description * + * ------------------------------- */ +#define Padding (0x00) +#define NoPadding (0x01) +#define CRCMode (0x00) +#define NoCRCMode (0x02) +#define MACTxIntEn (0x04) +#define MACTxIntDis (0x00) +#define LittleEndian (0x08) +#define BigEndian (0x00) +#define FrameDataPtrInc (0x10) +#define FrameDataPtrDec (0x00) +#define WA00 (0x00) +#define WA01 (0x20) +#define WA10 (0x40) +#define WA11 (0x60) + +/* --------- * + * Tx Status * + * --------- */ +#define TxCollCntMask (0x000F) +#define ExColl (0x0010) +#define TxDefer (0x0020) +#define Paused (0x0040) +#define Under (0x0100) +#define Defer (0x0200) +#define NCarr (0x0400) +#define SQErr (0x0800) +#define LateColl (0x1000) +#define Comp (0x4000) +#define TxHalted (0x8000) + +/* --------- * + * Rx Status * + * --------- */ +#define OvMax (0x0004) +#define CtlRcv (0x0020) +#define Rx10stat (0x0080) +#define AlignErr (0x0100) +#define CRCErr (0x0200) +#define Overflow (0x0400) +#define LongErr (0x0800) +#define Good (0x4000) +#define RxHalted (0x8000) + +/* ------------- * + * MII Registers * + * ------------- */ +#define MiiBusy (1<<11) +#define PHYREGWRITE (1<<10) + +enum { + gMACCON = FullDup, + gMACTXCON = EnComp | TxEn, + gMACRXCON = RxEn, + gBDMATXCON = BTxBRST | BTxMSL110 | BTxSTSKO | BTxEn, + gBDMARXCON = BRxDIE | BRxEn | BRxLittle | BRxMAINC | BRxBRST | BRxNLIE | BRxNOIE | BRxSTSKO | BRxWA10, + gCAMCON = CompEn | BroadAcc +}; +#endif /* __S5N8947_H */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-arm/mach-types.h uClinux-2.4.22-uc1-smc/include/asm-arm/mach-types.h --- uClinux-2.4.22-uc1-virgin/include/asm-arm/mach-types.h 2003-08-28 13:43:24.000000000 +0200 +++ uClinux-2.4.22-uc1-smc/include/asm-arm/mach-types.h 2004-05-02 17:36:57.000000000 +0200 @@ -377,6 +377,7 @@ #define MACH_TYPE_S3C2440 362 #define MACH_TYPE_KS8695P 363 #define MACH_TYPE_SE4000 364 +#define MACH_TYPE_SMC7004ABRV2 365 #ifdef CONFIG_ARCH_EBSA110 # ifdef machine_arch_type @@ -4734,6 +4735,18 @@ # define machine_is_se4000() (0) #endif +#ifdef CONFIG_ARCH_SMC7004ABRV2 +# ifdef machine_arch_type +# undef machine_arch_type +# define machine_arch_type __machine_arch_type +# else +# define machine_arch_type MACH_TYPE_SMC7004ABRV2 +# endif +# define machine_is_smc7004abrv2() (machine_arch_type == MACH_TYPE_SMC7004ABRV2) +#else +# define machine_is_smc7004abrv2() (0) +#endif + /* * These have not yet been registered */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/dma.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/dma.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/dma.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/dma.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,14 @@ +/* + * asm/arch-armnommu/arch-p52/dma.h: + * Mindspeed 2001 + */ + +#ifndef __ASM_ARCH_DMA_H +#define __ASM_ARCH_DMA_H + + +#define MAX_DMA_ADDRESS 0x01000000 +#define MAX_DMA_CHANNELS 13 +#define arch_dma_init(dma_chan) + +#endif /* _ASM_ARCH_DMA_H */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/hardware.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/hardware.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/hardware.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/hardware.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,301 @@ +/********************************************************/ +/* */ +/* Samsung S5N8947 */ +/* based on code by Mac Wang */ +/* Petr Novak */ +/* */ +/********************************************************/ +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +/* + * define S5N8947 CPU master clock + */ +#define MHz 1000000 +#define fMCLK_MHz (72 * MHz) +#define fMCLK (fMCLK_MHz / MHz) +#define MCLK2 (fMCLK_MHz / 2) + +/* + * ASIC Address Definition + */ + +#define Base_Addr 0x3FF0000 + +#define VPint *(volatile unsigned int *) +#define VPshort *(volatile unsigned short *) +#define VPchar *(volatile unsigned char *) + +#ifndef CSR_WRITE +# define CSR_WRITE(addr,data) (VPint(addr) = (data)) +#endif + +#ifndef CSR_READ +# define CSR_READ(addr) (VPint(addr)) +#endif + +#ifndef CAM_Reg +# define CAM_Reg(x) (VPint(CAMBASE+(x*0x4))) +#endif + +/* ************************ */ +/* System Manager Registers */ +/* ************************ */ +#define SYSCFG (Base_Addr+0x0000) +#define PCMCON (Base_Addr+0x3000) +#define EXTACON0 (Base_Addr+0x3008) +#define EXTACON1 (Base_Addr+0x300C) +#define EXTDBWTH (Base_Addr+0x3010) +#define ROMCON0 (Base_Addr+0x3014) +#define ROMCON1 (Base_Addr+0x3018) +#define ROMCON2 (Base_Addr+0x301C) +#define PCMOFFSET (Base_Addr+0x3020) +#define DRAMCON0 (Base_Addr+0x3024) +#define DRAMCON1 (Base_Addr+0x3028) +#define DRAMCON2 (Base_Addr+0x302C) +#define DRAMCON3 (Base_Addr+0x3030) +#define REFEXTCON (Base_Addr+0x3034) + +/* ************************ */ +/* Ethernet BDMA0 Registers */ +/* ************************ */ +#define BDMATXCON (Base_Addr+0x9000) +#define BDMARXCON (Base_Addr+0x9004) +#define BDMATXPTR (Base_Addr+0x9008) +#define BDMARXPTR (Base_Addr+0x900C) +#define BDMARXLSZ (Base_Addr+0x9010) +#define BDMASTAT (Base_Addr+0x9014) +#define CAMBASE (Base_Addr+0x9100) +/* + * CAM 0x9100 ~ 0x917C + * BDMATXBUF 0x9200 ~ 0x92FC + * BDMARXBUF 0x9800 ~ 0x99FC + */ + +/* ********************** */ +/* Ethernet MAC Registers */ +/* ********************** */ +#define MACON (Base_Addr+0xA000) +#define CAMCON (Base_Addr+0xA004) +#define MACTXCON (Base_Addr+0xA008) +#define MACTXSTAT (Base_Addr+0xA00C) +#define MACRXCON (Base_Addr+0xA010) +#define MACRXSTAT (Base_Addr+0xA014) +#define STADATA (Base_Addr+0xA018) +#define STACON (Base_Addr+0xA01C) +#define CAMEN (Base_Addr+0xA028) +#define EMISSCNT (Base_Addr+0xA03C) +#define EPZCNT (Base_Addr+0xA040) +#define ERMPZCNT (Base_Addr+0xA044) +#define EXTSTAT (Base_Addr+0x9040) + +/* ******************* */ +/* I/O Ports Registers */ +/* ******************* */ +#define IOPMOD (Base_Addr+0x5000) +#define IOPCON0 (Base_Addr+0x5004) +#define IOPCON1 (Base_Addr+0x5008) +#define IOPDATA (Base_Addr+0x500C) + +#define IopMod (VPint(IOPMOD)) +#define IopCon0 (VPint(IOPCON0)) +#define IopCon1 (VPint(IOPCON1)) +#define IopData (VPint(IOPDATA)) + +/* ****************************** */ +/* Interrupt Controller Registers */ +/* ****************************** */ +#define INTMOD (Base_Addr+0x4000) +#define INTPND (Base_Addr+0x4004) +#define INTMSK (Base_Addr+0x4008) +#define INTPRI0 (Base_Addr+0x400C) +#define INTPRI1 (Base_Addr+0x4010) +#define INTPRI2 (Base_Addr+0x4014) +#define INTPRI3 (Base_Addr+0x4018) +#define INTPRI4 (Base_Addr+0x401C) +#define INTPRI5 (Base_Addr+0x4020) +#define INTOFFSET (Base_Addr+0x4024) +#define INTPNDPRI (Base_Addr+0x4028) +#define INTPNDTST (Base_Addr+0x402C) +#define INTOSET_FIQ (Base_Addr+0x4030) +#define INTOSET_IRQ (Base_Addr+0x4034) + +#define IntMode (VPint(INTMOD)) +#define IntPend (VPint(INTPND)) +#define IntMask (VPint(INTMSK)) +#define IntOffset (VPint(INTOFFSET)) +#define IntPndTst (VPint(INTPNDTST)) +#define IntOffset_FIQ (VPint(INTOSET_FIQ)) +#define IntOffset_IRQ (VPint(INTOSET_IRQ)) + +#define INT_MODE_IRQ 0x000000 +#define INT_MODE_FIQ 0x1FFFFF +#define INT_MASK_DIS 0x1FFFFF +#define INT_MASK_ENA 0x000000 + +#define INT_ENABLE(n) IntMask &= ~(1<<(n)) +#define INT_DISABLE(n) IntMask |= (1<<(n)) +#define CLEAR_PEND_INT(n) IntPend = (1<<(n)) +#define SET_PEND_INT(n) IntPndTst |= (1<<(n)) + +/* ***************** */ +/* I2C Bus Registers */ +/* ***************** */ + +/* ************** */ +/* GDMA Registers */ +/* ************** */ + +/* ************** */ +/* UART Registers */ +/* ************** */ + +#define DEBUG_CONSOLE (0) + +#define ULCON0 (Base_Addr+0xD000) +#define UCON0 (Base_Addr+0xD004) +#define USTAT0 (Base_Addr+0xD008) +#define UTXBUF0 (Base_Addr+0xD00C) +#define URXBUF0 (Base_Addr+0xD010) +#define UBRDIV0 (Base_Addr+0xD014) + +#define UART_BASE0 ULCON0 + +#if DEBUG_CONSOLE == 0 + #define DEBUG_TX_BUFF_BASE UTXBUF0 + #define DEBUG_RX_BUFF_BASE URXBUF0 + #define DEBUG_UARTLCON_BASE ULCON0 + #define DEBUG_UARTCONT_BASE UCON0 + #define DEBUG_UARTBRD_BASE UBRDIV0 + #define DEBUG_CHK_STAT_BASE USTAT0 +#endif + +#define DEBUG_ULCON_REG_VAL (0x3) +#define DEBUG_UCON_REG_VAL (0x9) +#define DEBUG_UBRDIV_REG_VAL (0x500) +#define DEBUG_RX_CHECK_BIT (0X20) +#define DEBUG_TX_CAN_CHECK_BIT (0X40) +#define DEBUG_TX_DONE_CHECK_BIT (0X80) + + +/* **************** */ +/* Timers Registers */ +/* **************** */ +#define TMOD (Base_Addr+0x6000) +#define TDATA0 (Base_Addr+0x6004) +#define TDATA1 (Base_Addr+0x6008) +#define TDATA2 (Base_Addr+0x600C) +#define TCNT0 (Base_Addr+0x6010) +#define TCNT1 (Base_Addr+0x6014) +#define TCNT2 (Base_Addr+0x6018) +#define WDCON (Base_Addr+0x601C) +#define WDCNT (Base_Addr+0x6020) + + +/*******************/ +/* SYSCFG Register */ +/*******************/ +#define SYS_INIT_BASE EXTDBWTH +#define rSYSCFG (0xA3FF0000) /* disable Cache/Write buffer */ + +/**********************************/ +/* System Memory Control Register */ +/**********************************/ +#define DSR0 (2<<0) /* ROM Bank0 */ +#define DSR1 (0<<2) /* 0: Disable, 1: Byte, 2: Half-Word, 3: Word */ +#define DSR2 (0<<4) +#define DSP (0<<6) +#define DSD0 (3<<12) /* RAM Bank0 */ +#define DSD1 (0<<14) +#define DSD2 (0<<16) +#define DSD3 (0<<18) +#define DSX0 (2<<20) /* EXTIO0 */ +#define DSX1 (0<<22) +#define DSX2 (0<<24) +#define DSX3 (0<<26) + +#define rEXTDBWTH (DSR0|DSR1|DSR2|DSP | DSD0|DSD1|DSD2|DSD3 | DSX0|DSX1|DSX2|DSX3) + +/****************************************/ +/* ROMCON0: ROM Bank 0 Control Register */ +/****************************************/ +#define PMC0 (0x0<<0) /*00: Normal ROM 01: 4 word page*/ + /*10: 8 word page 11:16 word page*/ +#define tPA0 (0x0<<2) /*00: 5 cycles 01: 2 cycles*/ + /*10: 3 cycles 11: 4 cycles*/ +#define tACC0 (0x6<<4) /*000: Disable bank 001: 2 cycles*/ + /*010: 3 cycles 011: 4 cycles*/ + /*110: 7 cycles 111: Reserved*/ +#define ROM_BASE0_R ((0x00000000>>16)<<10) +#define ROM_NEXT0_R ((0x00100000>>16)<<20) +#define ROM_BASE0_B ((0x01000000>>16)<<10) +#define ROM_NEXT0_B ((0x01100000>>16)<<20) +#define rROMCON0_R (ROM_NEXT0_R|ROM_BASE0_R|tACC0|tPA0|PMC0) +#define rROMCON0_B (ROM_NEXT0_B|ROM_BASE0_B|tACC0|tPA0|PMC0) + +#define rROMCON1 0x0 +#define rROMCON2 0x0 +#define rROMCON3 0x0 +#define rROMCON4 0x0 +#define rROMCON5 0x0 + + +/********************************************/ +/* SDRAMCON0: SDRAM Bank 0 Control Register */ +/********************************************/ +#define StRC0 (0x1<<6) +#define StRP0 (0x3<<8) +#define SDRAM_BASE0_R ((0x01000000>>16)<<10) +#define SDRAM_NEXT0_R ((0x01800000>>16)<<20) +#define SDRAM_BASE0_B ((0x00000000>>16)<<10) +#define SDRAM_NEXT0_B ((0x00800000>>16)<<20) +#define SCAN0 (0x0<<30) +#define rSDRAMCON0_R (SCAN0|SDRAM_NEXT0_R|SDRAM_BASE0_R|StRP0|StRC0) +#define rSDRAMCON0_B (SCAN0|SDRAM_NEXT0_B|SDRAM_BASE0_B|StRP0|StRC0) + +#define rSDRAMCON1 0x0 +#define rSDRAMCON2 0x0 +#define rSDRAMCON3 0x0 + +/************************************************/ +/* DRAM Refresh & External I/O Control Register */ +/************************************************/ +#define ExtIOBase (0x360<<0) +#define tRPR (0x0<<13) +#define VSF (0x1<<15) +#define REN (0x1<<16) +#define tCHR (0x0<<17) +#define tCSR (0x0<<20) +#define RefCountValue ((2048+1-(16*fMCLK))<<21) +#define rREFEXTCON (RefCountValue|tCSR|tCHR|REN|VSF|tRPR|ExtIOBase) + +/********/ +/* Misc */ +/********/ + +#define HARD_RESET_NOW() +#define TMOD_TIMER0_VAL 0x3 /* Timer0 TOGGLE, and Run */ +#define TAG_BASE 0x01000000 + +#define EXTIO0 (0x04000000+(ExtIOBase<<16)+0) +#define EXTIO1 (0x04000000+(ExtIOBase<<16)+0x4000) +#define EXTIO2 (0x04000000+(ExtIOBase<<16)+0x8000) +#define EXTIO3 (0x04000000+(ExtIOBase<<16)+0xc000) + +#define GPIOINT_UART1 6 /* IRQ5 on GPIO pin 10 */ + +#define S5N8947_GPIO_INT_ON (1<<4) +#define S5N8947_GPIO_INT_OFF (0<<4) +#define S5N8947_GPIO_INT_ACT_LOW (0<<3) +#define S5N8947_GPIO_INT_ACT_HIGH (1<<3) +#define S5N8947_GPIO_INT_FILTER_ON (1<<2) +#define S5N8947_GPIO_INT_FILTER_OFF (0<<2) +#define S5N8947_GPIO_INT_LEVEL (0) +#define S5N8947_GPIO_INT_RISING (1) +#define S5N8947_GPIO_INT_FALLING (2) +#define S5N8947_GPIO_INT_BOTH (3) + +#define SMC7004ABRV2_GPIO_INT_UART 0x1d + + +#endif /* __ASM_ARCH_HARDWARE_H */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/io.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/io.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/io.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/io.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,39 @@ +/* + * linux/include/asm-armnommu/arch-dsc21/io.h + * + * Copyright (C) 1997-1999 Russell King + * + * Modifications: + * 06-12-1997 RMK Created. + * 07-04-1999 RMK Major cleanup + * 02-19-2001 gjm Leveraged for armnommu/dsc21 + */ +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +/* + * kernel/resource.c uses this to initialize the global ioport_resource struct + * which is used in all calls to request_resource(), allocate_resource(), etc. + * --gmcnutt + */ +#define IO_SPACE_LIMIT 0xffffffff + +/* + * If we define __io then asm/io.h will take care of most of the inb & friends + * macros. It still leaves us some 16bit macros to deal with ourselves, though. + * We don't have PCI or ISA on the dsc21 so I dropped __mem_pci & __mem_isa. + * --gmcnutt + */ +#define PCIO_BASE 0 +#define __io(a) (PCIO_BASE + (a)) +#define __arch_getw(a) (*(volatile unsigned short *)(a)) +#define __arch_putw(v,a) (*(volatile unsigned short *)(a) = (v)) + +/* + * Defining these two gives us ioremap for free. See asm/io.h. + * --gmcnutt + */ +#define iomem_valid_addr(iomem,sz) (1) +#define iomem_to_phys(iomem) (iomem) + +#endif diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/irq.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/irq.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/irq.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/irq.h 2004-05-02 17:42:08.000000000 +0200 @@ -0,0 +1,44 @@ +/* + * asm/arch-samsung/irq.h: + * 2001 Mac Wang + * 2003 Petr Novak + */ + +#ifndef __ASM_ARCH_IRQ_H__ +#define __ASM_ARCH_IRQ_H__ + +#include +#include +#include +#include + +extern void s5n8947_mask_irq(unsigned int irq); +extern void s5n8947_unmask_irq(unsigned int irq); +extern void s5n8947_mask_ack_irq(unsigned int irq); +extern void s5n8947_int_init(void); +extern void s5n8947_gpio_irq_mode(unsigned int irq, unsigned int mode); + +static __inline__ unsigned int fixup_irq(unsigned int irq) +{ + CLEAR_PEND_INT(irq); + return irq; +} + +static __inline__ void irq_init_irq(void) +{ + unsigned long flags; + int irq; + + save_flags_cli(flags); + s5n8947_int_init(); + restore_flags(flags); + + for (irq = 0; irq < NR_IRQS; irq++) { + irq_desc[irq].valid = 1; + irq_desc[irq].probe_ok = 1; + irq_desc[irq].mask_ack = s5n8947_mask_ack_irq; + irq_desc[irq].mask = s5n8947_mask_irq; + irq_desc[irq].unmask = s5n8947_unmask_irq; + } +} +#endif /* __ASM_ARCH_IRQ_H__ */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/irqs.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/irqs.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/irqs.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/irqs.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,38 @@ +/* + * asm/arch-samsung/irqs.h: + * Mac Wang + * Petr Novak + */ +#ifndef __ASM_ARCH_IRQS_H__ +#define __ASM_ARCH_IRQS_H__ +#define NR_IRQS 23 +#define VALID_IRQ(i) (i + * 2001 Mindspeed + */ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +#include + +#define TASK_SIZE (0x01a00000UL) +#define TASK_SIZE_26 TASK_SIZE + +#define PHYS_OFFSET (DRAM_BASE) +#define PAGE_OFFSET PHYS_OFFSET +#define END_MEM (DRAM_BASE + DRAM_SIZE) + +#define __virt_to_phys(vpage) ((unsigned long) (vpage)) +#define __phys_to_virt(ppage) ((void *) (ppage)) +#define __virt_to_bus(vpage) ((unsigned long) (vpage)) +#define __bus_to_virt(ppage) ((void *) (ppage)) + +#endif diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/param.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/param.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/param.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/param.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,6 @@ +#ifndef __ARCH_ASM_PARAM_H__ +#define __ARCH_ASM_PARAM_H__ + +/* nothing for now */ + +#endif /* __ARCH_ASM_PARAM_H__ */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/processor.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/processor.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/processor.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/processor.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,15 @@ +/* + * uclinux/include/asm-armnommu/arch-atmel/mmu.h + * + */ +#ifndef __ASM_ARCH_PROCESSOR_H +#define __ASM_ARCH_PROCESSOR_H + +#define EISA_bus 0 +#define EISA_bus__is_a_macro +#define MCA_bus 0 +#define MCA_bus__is_a_macro + +#define TASK_SIZE (0x01a00000UL) + +#endif diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/serial.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/serial.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/serial.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/serial.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,122 @@ +/* + * linux/include/asm/arch-samsung/serial.h + * 2001 Mac Wang + */ +#ifndef __ASM_ARCH_SERIAL_H +#define __ASM_ARCH_SERIAL_H + +#include +#include + +#define RS_TABLE_SIZE 2 +#define BASE_BAUD 9600 +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) +#define STD_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, UART_BASE0, INT_UARTRX, STD_COM_FLAGS }, /* ttyS0 */ \ +#define EXTRA_SERIAL_PORT_DEFNS + +#define UART_LCR 0x00 /* Line Control Register */ +#define UART_IER 0x04 /* Global Control Register */ +#define UART_GCR 0x04 /* Global Control Register */ +#define UART_LSR 0x08 /* Line Status Reigster */ +#define UART_TX 0x0C /* TX buffer Register */ +#define UART_RX 0x10 /* RX buffer Register */ +#define UART_BDR 0x14 /* Baudrate Divisor Reigster */ + +#define UART_LSR_OE 0x01 // Overrun error +#define UART_LSR_PE 0x02 // Parity error +#define UART_LSR_FE 0x04 // Frame error +#define UART_LSR_BI 0x08 // Break detect +#define UART_LSR_DTR 0x10 // Data terminal ready +#define UART_LSR_DR 0x20 // Receive data ready +#define UART_LSR_THRE 0x40 // Transmit buffer register empty +#define UART_LSR_TEMT 0x80 // Transmit complete + +#define UART_LCR_WLEN5 0x00 +#define UART_LCR_WLEN6 0x01 +#define UART_LCR_WLEN7 0x02 +#define UART_LCR_WLEN8 0x03 +#define UART_LCR_PARITY 0x20 +#define UART_LCR_NPAR 0x00 +#define UART_LCR_OPAR 0x20 +#define UART_LCR_EPAR 0x28 +#define UART_LCR_SPAR 0x10 +#define UART_LCR_SBC 0x40 + +#define UART_GCR_RX_INT 0x01 +#define UART_GCR_TX_INT 0x08 +#define UART_GCR_RX_STAT_INT 0x04 + +#define UART_IER_MSI +#define UART_IER_RLSI 0x04 +#define UART_IER_THRI 0x08 +#define UART_IER_RDI 0x01 + +#define UART_MSR 0 +#define UART_MSR_DCD 0 +#define UART_MSR_RI 0 +#define UART_MSR_DSR 0 +#define UART_MSR_CTS 0 +#define UART_MSR_DDCD 0 +#define UART_MSR_TERI 0 +#define UART_MSR_DDSR 0 +#define UART_MSR_DCTS 0 +#define UART_MSR_ANY_DELTA 0 + +#define PORT_S3C4510B 14 + +struct serial_baudtable +{ + unsigned int baudrate; + unsigned int div; +}; + +struct serial_baudtable uart_baudrate[] = +{ + { 1200, 0x5150}, + { 2400, 0x28A0}, + { 4800, 0x1440}, + { 9600, 0x0A20}, + { 19200, 0x0500}, + { 38400, 0x0280}, + { 57600, 0x01A0}, + {115200, 0x00D0}, + {230400, 0x0060}, + {460860, 0x0020} +}; + +unsigned int baudrate_div(unsigned int baudrate) +{ + int i; + int len = sizeof(uart_baudrate)/sizeof(struct serial_baudtable); + for(i = 0; i < len; i++) + if(uart_baudrate[i].baudrate == baudrate) + return uart_baudrate[i].div; + return 0; +} + +#define disable_uart_tx_interrupt(line) \ +{ \ + INT_DISABLE(INT_UARTTX); \ + CLEAR_PEND_INT(INT_UARTTX); \ +} + +#define disable_uart_rx_interrupt(line) \ +{ \ + INT_DISABLE(INT_UARTRX); \ + CLEAR_PEND_INT(INT_UARTRX); \ +} + +#define enable_uart_tx_interrupt(line) \ +{ \ + INT_ENABLE(INT_UARTTX); \ + SET_PEND_INT(INT_UARTTX); \ +} + +#define enable_uart_rx_interrupt(line) \ +{ \ + INT_ENABLE(INT_UARTRX); \ +} + +#endif /* __ASM_ARCH_SERIAL_H */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/shmparam.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/shmparam.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/shmparam.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/shmparam.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,5 @@ +/* + * linux/include/asm-arm/arch-a5k/shmparam.h + * + * Copyright (c) 1996 Russell King. + */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/system.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/system.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/system.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/system.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,18 @@ +/* + * linux/include/asm-armnommu/arch-dsc21/system.h + * + * Copyright (c) 1999 Nicolas Pitre + * Copyright (c) 2001 RidgeRun, Inc (http://www.ridgerun.org) + * + */ + +static inline void arch_idle(void) +{ + while (!current->need_resched && !hlt_counter); +} + +extern inline void arch_reset(char mode) +{ + /* REVISIT --gmcnutt */ +} + diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/time.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/time.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/time.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/time.h 2004-05-02 17:49:09.000000000 +0200 @@ -0,0 +1,35 @@ +/* + * linux/include/asm/arch-samsung/time.h + * 2001 Mac Wang + */ + +#ifndef __ASM_ARCH_TIME_H__ +#define __ASM_ARCH_TIME_H__ + +#include +#include +#include +#include + +extern struct irqaction timer_irq; + +extern unsigned long samsung_gettimeoffset(void); +extern void samsung_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs); + + +void __inline__ setup_timer (void) +{ + CSR_WRITE(TCNT0, 0); + CSR_WRITE(TDATA0, CLOCK_TICK_RATE); + CSR_WRITE(TMOD, TMOD_TIMER0_VAL); + + CLEAR_PEND_INT(IRQ_TIMER); + INT_ENABLE(IRQ_TIMER); + + gettimeoffset = samsung_gettimeoffset; + + timer_irq.handler = samsung_timer_interrupt; + setup_arm_irq(IRQ_TIMER, &timer_irq); +} + +#endif /* __ASM_ARCH_TIME_H__ */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/timex.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/timex.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/timex.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/timex.h 2004-05-02 17:40:33.000000000 +0200 @@ -0,0 +1,13 @@ +/* + * timex.h: + * 2001 Mac Wang + */ +#ifndef __ASM_ARCH_TIMEX_H__ +#define __ASM_ARCH_TIMEX_H__ + +#include + +// #define CLOCK_TICK_RATE (fMCLK_MHz/HZ-1) +#define CLOCK_TICK_RATE (fMCLK_MHz/HZ-1) + +#endif /* __ASM_ARCH_TIMEX_H__ */ diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/uncompress.c uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/uncompress.c --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/uncompress.c 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/uncompress.c 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,28 @@ +/* + * linux/include/asm/arch-samsung/uncompress.c + * 2001 Mac Wang + */ + +#include + +static int s5n8947_decomp_setup() +{ + CSR_WRITE(DEBUG_UARTLCON_BASE, DEBUG_ULCON_REG_VAL); + CSR_WRITE(DEBUG_UARTCONT_BASE, DEBUG_UCON_REG_VAL); + CSR_WRITE(DEBUG_UARTBRD_BASE, DEBUG_UBRDIV_REG_VAL); +} + +static int s5n8947_putc(char c) +{ + CSR_WRITE(DEBUG_TX_BUFF_BASE, c); + while(!(CSR_READ(DEBUG_CHK_STAT_BASE) & DEBUG_TX_DONE_CHECK_BIT)); + + if(c == '\n') + s5n8947_putc('\r'); +} + +static void s5n8947_puts(const char *s) +{ + while(*s != '\0') + s5n8947_putc(*s++); +} diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/uncompress.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/uncompress.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/uncompress.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/uncompress.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,50 @@ +/* + * asm/arch/uncompress.c: + * Optional routines to aid in debugging the decompression phase + * of kernel boot. + * copyright: + * (C) 2001 RidgeRun, Inc. (http://www.ridgerun.com) + * author: Gordon McNutt + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +/* + * This is used by arch/armnommu/boot/compressed/misc.c to write progress info + * out the serial port so that the user can see debug messages up to the point + * where the kernel is decompressed. The STANDALONE_DEBUG macro chooses between + * this and the standard printf. Punt. + * --gmcnutt + */ +#define puts(s) s5n8947_puts(s) + +/* + * Not sure what this is for. Probably an optional watchdog to check if the + * decompress got hung so we can warn the user. Punt. + */ +#define arch_decomp_wdog() + +/* + * If we need to do some setup prior to decompression (like initializing the + * UART if we want to use puts() above) then we define it here. Punt. + */ +#define arch_decomp_setup() s5n8947_decomp_setup() diff -urN uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/vmalloc.h uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/vmalloc.h --- uClinux-2.4.22-uc1-virgin/include/asm-armnommu/arch-SMC7004ABRV2/vmalloc.h 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/include/asm-armnommu/arch-SMC7004ABRV2/vmalloc.h 2004-05-02 17:36:57.000000000 +0200 @@ -0,0 +1,33 @@ +/* + * linux/include/asm-arm/arch-dsc21/vmalloc.h + * + * Copyright (C) 2000 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Just any arbitrary offset to the start of the vmalloc VM area: the + * current 8MB value just means that there will be a 8MB "hole" after the + * physical memory until the kernel virtual memory starts. That means that + * any out-of-bounds memory accesses will hopefully be caught. + * The vmalloc() routines leaves a hole of 4kB between each vmalloced + * area for the same reason. ;) + */ +#define VMALLOC_OFFSET (8*1024*1024) +#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) +#define VMALLOC_VMADDR(x) ((unsigned long)(x)) +#define VMALLOC_END (PAGE_OFFSET + 0x10000000) + diff -urN uClinux-2.4.22-uc1-virgin/Makefile uClinux-2.4.22-uc1-smc/Makefile --- uClinux-2.4.22-uc1-virgin/Makefile 2004-01-07 05:09:59.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/Makefile 2004-05-02 17:36:57.000000000 +0200 @@ -5,7 +5,7 @@ KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) -# ARCH := armnommu +ARCH := armnommu # ARCH := m68knommu # ARCH := h8300 # ARCH := niosnommu @@ -28,7 +28,7 @@ HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer # CROSS_COMPILE = m68k-elf- -# CROSS_COMPILE = arm-elf- +CROSS_COMPILE = arm-elf- # CROSS_COMPILE = h8300-elf- # CROSS_COMPILE = nios-elf- # CROSS_COMPILE = e1-coff- diff -urN uClinux-2.4.22-uc1-virgin/SMC7004ABRV2-config uClinux-2.4.22-uc1-smc/SMC7004ABRV2-config --- uClinux-2.4.22-uc1-virgin/SMC7004ABRV2-config 1970-01-01 01:00:00.000000000 +0100 +++ uClinux-2.4.22-uc1-smc/SMC7004ABRV2-config 2004-05-02 21:43:53.000000000 +0200 @@ -0,0 +1,484 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +# CONFIG_SBUS is not set +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_UCLINUX=y +MAGIC_ROM_PTR=y + +# +# Code maturity level options +# +# CONFIG_EXPERIMENTAL is not set +# CONFIG_OBSOLETE is not set + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# System Type +# +# CONFIG_ARCH_DSC21 is not set +# CONFIG_ARCH_C5471 is not set +# CONFIG_ARCH_CNXT is not set +# CONFIG_ARCH_NETARM is not set +# CONFIG_ARCH_TA7S is not set +# CONFIG_ARCH_TA7V is not set +# CONFIG_ARCH_SWARM is not set +CONFIG_ARCH_SAMSUNG=y +# CONFIG_ARCH_ATMEL is not set +# CONFIG_CPU_BIG_ENDIAN is not set +# CONFIG_SET_MEM_PARAM is not set +CONFIG_RAMKERNEL=y +# CONFIG_ROMKERNEL is not set +# CONFIG_BOARD_SMDK40100 is not set +# CONFIG_BOARD_MBA44 is not set +# CONFIG_BOARD_EVS3C4530HEI is not set +# CONFIG_BOARD_S3C2500REFRGP is not set +# CONFIG_BOARD_SMDK2500 is not set +CONFIG_BOARD_SMC7004ABRV2=y +# CONFIG_BOARD_SNDS100 is not set +CONFIG_CPU_NAME="S5N8947" +CONFIG_NO_PGT_CACHE=y +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set +CONFIG_CPU_S5N8947=y +CONFIG_CPU_ARM710=y +CONFIG_CPU_WITH_CACHE=y +# CONFIG_CPU_WITH_MCR_INSTRUCTION is not set +DRAM_BASE=0x01000000 +DRAM_SIZE=0x00800000 +FLASH_MEM_BASE=0x00000000 +FLASH_SIZE=0x00100000 + +# +# General setup +# +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set +CONFIG_NET=y +# CONFIG_SYSVIPC is not set +CONFIG_REDUCED_MEMORY=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_SYSCTL is not set +# CONFIG_NWFPE is not set +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +CONFIG_BINFMT_FLAT=y +CONFIG_BINFMT_ZFLAT=y +CONFIG_KERNEL_ELF=y +# CONFIG_ARTHUR is not set +# CONFIG_CMDLINE_BOOL is not set +# CONFIG_ALIGNMENT_TRAP is not set +# CONFIG_RAM_ATTACHED_ROMFS is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +# CONFIG_UNIX is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_ARP_LIMIT is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_VLAN_8021Q=y + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_ETH_S5N8947=y +# CONFIG_ARM_AM79C961A is not set +# CONFIG_ARM_CIRRUS is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FEC is not set +# CONFIG_CS89x0 is not set +# CONFIG_UCCS8900 is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set +# CONFIG_FDDI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=2048 +CONFIG_BLK_DEV_INITRD=y +CONFIG_BLK_DEV_RAMDISK_DATA=y +# CONFIG_BLK_DEV_BLKMEM is not set +# CONFIG_BLK_STATS is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_QFMT_V2 is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BEFS_DEBUG is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +CONFIG_ROMFS_FS=y +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_NFSD_TCP is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Character devices +# +CONFIG_SERIAL_SMC7004ABRV2=y +CONFIG_SERIAL_SMC7004ABRV2_CONSOLE=y +# CONFIG_LEDMAN is not set +# CONFIG_DS1302 is not set +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_COMPUTONE is not set +# CONFIG_ROCKETPORT is not set +# CONFIG_CYCLADES is not set +# CONFIG_DIGIEPCA is not set +# CONFIG_DIGI is not set +# CONFIG_ESPSERIAL is not set +# CONFIG_MOXA_INTELLIO is not set +# CONFIG_MOXA_SMARTIO is not set +# CONFIG_SYNCLINK is not set +# CONFIG_SYNCLINKMP is not set +# CONFIG_N_HDLC is not set +# CONFIG_RISCOM8 is not set +# CONFIG_SPECIALIX is not set +# CONFIG_SX is not set +# CONFIG_RIO is not set +# CONFIG_STALDRV is not set +# CONFIG_UNIX98_PTYS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_INPUT_NS558 is not set +# CONFIG_INPUT_LIGHTNING is not set +# CONFIG_INPUT_PCIGAME is not set +# CONFIG_INPUT_CS461X is not set +# CONFIG_INPUT_EMU10K1 is not set +# CONFIG_INPUT_SERIO is not set +# CONFIG_INPUT_SERPORT is not set + +# +# Joysticks +# +# CONFIG_INPUT_ANALOG is not set +# CONFIG_INPUT_A3D is not set +# CONFIG_INPUT_ADI is not set +# CONFIG_INPUT_COBRA is not set +# CONFIG_INPUT_GF2K is not set +# CONFIG_INPUT_GRIP is not set +# CONFIG_INPUT_INTERACT is not set +# CONFIG_INPUT_TMDC is not set +# CONFIG_INPUT_SIDEWINDER is not set +# CONFIG_INPUT_IFORCE_USB is not set +# CONFIG_INPUT_IFORCE_232 is not set +# CONFIG_INPUT_WARRIOR is not set +# CONFIG_INPUT_MAGELLAN is not set +# CONFIG_INPUT_SPACEORB is not set +# CONFIG_INPUT_SPACEBALL is not set +# CONFIG_INPUT_STINGER is not set +# CONFIG_INPUT_DB9 is not set +# CONFIG_INPUT_GAMECON is not set +# CONFIG_INPUT_TURBOGRAFX is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPMI_PANIC_EVENT is not set +# CONFIG_IPMI_DEVICE_INTERFACE is not set +# CONFIG_IPMI_KCS is not set +# CONFIG_IPMI_WATCHDOG is not set + +# +# Controller Area Network Cards/Chips +# +# CONFIG_CAN4LINUX is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_SCx200_GPIO is not set +# CONFIG_AMD_PM768 is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +# CONFIG_REVISIT is not set +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_INFO=y +# CONFIG_MAGIC_SYSRQ is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC32 is not set +CONFIG_ZLIB_INFLATE=y +# CONFIG_ZLIB_DEFLATE is not set