This patch adds support to the popular NEC Vr41XX CPU family. In particular, it enables CPU Vr4181 and NEC Osprey board. The patch is also tested on Vr4120 and Vr4122, but the corresponding board patches for those CPUs will be posted later. It contains a hack in ne.c which works around the x86 legacy limit. See the comment. It also contains a couple of other fixes: . It contains a compromising fix for sysmips(MIPS_ATOMIC_SET, ...). We basically force SIGSYS if the return value is a small negative int, which is the case causing us all the trouble at the first place. For glibc and other "well-behaved" programs, this limit is acceptable. The code is based on Maciej's new syscall patch. . It fixes a typo in include/asm/bootinfo.h. . in net/ipv4/ipconfig.h: It reverts the default value of ic_enable to 1, which somehow was changed to 0 in v2.4.5. Need to ask the maintainer why. Jun 010626 diff -Nru linux/arch/mips/kernel/setup.c.orig linux/arch/mips/kernel/setup.c --- linux/arch/mips/kernel/setup.c.orig Wed Jun 13 15:04:17 2001 +++ linux/arch/mips/kernel/setup.c Tue Jun 26 12:19:18 2001 @@ -191,6 +191,12 @@ MIPS_CPU_WATCH | MIPS_CPU_VCE; mips_cpu.tlbsize = 48; break; + case PRID_IMP_VR41XX: + mips_cpu.cputype = CPU_VR41XX; + mips_cpu.isa_level = MIPS_CPU_ISA_III; + mips_cpu.options = R4K_OPTS; + mips_cpu.tlbsize = 32; + break; case PRID_IMP_R4600: mips_cpu.cputype = CPU_R4600; mips_cpu.isa_level = MIPS_CPU_ISA_III; @@ -518,6 +524,7 @@ void malta_setup(void); void momenco_ocelot_setup(void); void nino_setup(void); + void nec_osprey_setup(void); unsigned long bootmap_size; unsigned long start_pfn, max_pfn, first_usable_pfn; @@ -596,6 +603,11 @@ ddb_setup(); break; #endif +#ifdef CONFIG_NEC_OSPREY + case MACH_GROUP_NEC_VR41XX: + nec_osprey_setup(); + break; +#endif #ifdef CONFIG_MIPS_EV96100 case MACH_GROUP_GALILEO: ev96100_setup(); @@ -710,11 +722,15 @@ continue; size = last_pfn - curr_pfn; +printk("[jsun] before free_bootmem\n"); free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); +printk("[jsun] after free_bootmem\n"); } /* Reserve the bootmap memory. */ +printk("[jsun] before reserve_bootmem\n"); reserve_bootmem(PFN_PHYS(first_usable_pfn), bootmap_size); +printk("[jsun] after reserve_bootmem\n"); #ifdef CONFIG_BLK_DEV_INITRD /* Board specific code should have set up initrd_start and initrd_end */ diff -Nru linux/arch/mips/kernel/traps.c.orig linux/arch/mips/kernel/traps.c --- linux/arch/mips/kernel/traps.c.orig Tue Jun 19 10:38:56 2001 +++ linux/arch/mips/kernel/traps.c Tue Jun 26 10:27:41 2001 @@ -830,7 +830,7 @@ void __init trap_init(void) { - extern char except_vec0_nevada, except_vec0_r4000; + extern char except_vec0_nevada, except_vec0_r4000, except_vec0_vr41xx; extern char except_vec0_r4600, except_vec0_r2300; extern char except_vec1_generic, except_vec2_generic; extern char except_vec3_generic, except_vec3_r4000; @@ -906,6 +906,8 @@ memcpy((void *)KSEG0, &except_vec0_nevada, 0x80); } else if (mips_cpu.cputype == CPU_R4600) memcpy((void *)KSEG0, &except_vec0_r4600, 0x80); + else if (mips_cpu.cputype == CPU_VR41XX) + memcpy((void *)KSEG0, &except_vec0_vr41xx, 0x80); else memcpy((void *)KSEG0, &except_vec0_r4000, 0x80); diff -Nru linux/arch/mips/kernel/head.S.orig linux/arch/mips/kernel/head.S --- linux/arch/mips/kernel/head.S.orig Fri May 18 04:44:21 2001 +++ linux/arch/mips/kernel/head.S Tue Jun 26 10:27:41 2001 @@ -92,6 +92,35 @@ eret # return from trap END(except_vec0_r4000) + /* TLB refill, EXL == 0, NEC Vr41xx version */ + .set noreorder + .set noat + LEAF(except_vec0_vr41xx) + .set mips3 + mfc0 k0, CP0_BADVADDR + srl k0, k0, 22 + lw k1, current_pgd # get pgd pointer + sll k0, k0, 2 + addu k1, k1, k0 + mfc0 k0, CP0_CONTEXT + lw k1, (k1) + srl k0, k0, 3 + and k0, k0, 0xff8 + addu k1, k1, k0 + lw k0, 0(k1) + lw k1, 4(k1) + srl k0, k0, 6 + mtc0 k0, CP0_ENTRYLO0 + srl k1, k1, 6 + mtc0 k1, CP0_ENTRYLO1 + nop + nop + tlbwr + nop + nop + eret + END(except_vec0_vr41xx) + /* TLB refill, EXL == 0, R4600 version */ LEAF(except_vec0_r4600) .set mips3 diff -Nru linux/arch/mips/kernel/sysmips.c.orig linux/arch/mips/kernel/sysmips.c --- linux/arch/mips/kernel/sysmips.c.orig Mon Apr 23 11:32:54 2001 +++ linux/arch/mips/kernel/sysmips.c Tue Jun 26 11:50:48 2001 @@ -16,7 +16,7 @@ #include #include #include - +#include #include #include #include @@ -75,53 +75,83 @@ } case MIPS_ATOMIC_SET: { -#ifdef CONFIG_CPU_HAS_LLSC - unsigned int tmp; + int *ptr, val, ret, err, tmp; + struct siginfo info; - p = (int *) arg1; - errno = verify_area(VERIFY_WRITE, p, sizeof(*p)); - if (errno) - return errno; - errno = 0; - - __asm__(".set\tpush\t\t\t# sysmips(MIPS_ATOMIC, ...)\n\t" - ".set\tmips2\n\t" - ".set\tnoat\n\t" - "1:\tll\t%0, %4\n\t" - "move\t$1, %3\n\t" - "2:\tsc\t$1, %1\n\t" - "beqz\t$1, 1b\n\t" - ".set\tpop\n\t" - ".section\t.fixup,\"ax\"\n" - "3:\tli\t%2, 1\t\t\t# error\n\t" - ".previous\n\t" - ".section\t__ex_table,\"a\"\n\t" - ".word\t1b, 3b\n\t" - ".word\t2b, 3b\n\t" - ".previous\n\t" - : "=&r" (tmp), "=o" (* (u32 *) p), "=r" (errno) - : "r" (arg2), "o" (* (u32 *) p), "2" (errno) - : "$1"); + ptr = (int *)arg1; + val = (int)arg2; - if (errno) - return -EFAULT; + /* Don't emulate unaligned accesses. */ + if ((int)ptr & 3) { + info.si_signo = SIGBUS; + info.si_code = BUS_ADRALN; + goto fault; + } + + /* A zero here saves us three instructions. */ + err = verify_area(VERIFY_WRITE, ptr, 0); + if (err) { + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; + goto fault; + } - /* We're skipping error handling etc. */ - if (current->ptrace & PT_TRACESYS) - syscall_trace(); - - ((struct pt_regs *)&cmd)->regs[2] = tmp; - ((struct pt_regs *)&cmd)->regs[7] = 0; - - __asm__ __volatile__( - "move\t$29, %0\n\t" - "j\to32_ret_from_sys_call" - : /* No outputs */ - : "r" (&cmd)); - /* Unreached */ +#ifdef CONFIG_CPU_HAS_LLSC + __asm__(".set mips2\n\t" + "1:\n\t" + "ll %0,%5\n\t" + ".set push\n\t" + ".set noreorder\n\t" + "beq %0,%4,3f\n\t" + " move %3,%4\n" + ".set pop\n\t" + "2:\n\t" + "sc %3,%1\n\t" + "beqz %3,1b\n\t" + "3:\n\t" + ".set mips0\n\t" + ".section .fixup,\"ax\"\n" + "4:\n\t" + "li %2,%7\n\t" + "j 3b\n\t" + ".previous\n\t" + ".section __ex_table,\"a\"\n\t" + ".word 1b,4b\n\t" + ".word 2b,4b\n\t" + ".previous" + : "=&r" (ret), "=R" (*ptr), "=r" (err), "=&r" (tmp) + : "r" (val), "1" (*ptr), "2" (0), "i" (-EFAULT)); #else - printk("sys_sysmips(MIPS_ATOMIC_SET, ...) not ready for !CONFIG_CPU_HAS_LLSC\n"); + save_and_cli(tmp); + err = __get_user(ret, ptr); + if (ret != val) + err |= __put_user(val, ptr); /* No fault + unless unwriteable. */ + restore_flags(tmp); #endif + + if (err) { + info.si_signo = SIGSEGV; + info.si_code = SEGV_MAPERR; + goto fault; + } + + if ( (ret < 0) && (ret >= -EMAXERRNO) ) { + info.si_signo = SIGSYS; + info.si_code = 0; + goto fault; + } + + return ret; + +fault: + /* Go back to SYSCALL. */ + ((struct pt_regs *)&cmd)->cp0_epc -= 4; + + info.si_addr = (void *)((struct pt_regs *)&cmd)->cp0_epc; + force_sig_info(info.si_signo, &info, current); + + return 0; } case MIPS_FIXADE: diff -Nru linux/arch/mips/kernel/proc.c.orig linux/arch/mips/kernel/proc.c --- linux/arch/mips/kernel/proc.c.orig Fri May 18 04:44:21 2001 +++ linux/arch/mips/kernel/proc.c Tue Jun 26 11:17:19 2001 @@ -48,13 +48,15 @@ const char *mach_sibyte_names[] = GROUP_SIBYTE_NAMES; const char *mach_toshiba_names[] = GROUP_TOSHIBA_NAMES; const char *mach_alchemy_names[] = GROUP_ALCHEMY_NAMES; + const char *mach_nec_vr41xx_names[] = GROUP_NEC_VR41XX_NAMES; const char **mach_group_to_name[] = { mach_unknown_names, mach_jazz_names, mach_dec_names, mach_arc_names, mach_sni_rm_names, mach_acn_names, mach_sgi_names, mach_cobalt_names, mach_nec_ddb_names, mach_baget_names, mach_cosine_names, mach_galileo_names, mach_momenco_names, mach_ite_names, mach_philips_names, mach_globespan_names, - mach_sibyte_names, mach_toshiba_names, mach_alchemy_names}; + mach_sibyte_names, mach_toshiba_names, mach_alchemy_names, + mach_nec_vr41xx_names}; unsigned int version = read_32bit_cp0_register(CP0_PRID); int len; diff -Nru linux/arch/mips/mm/r4xx0.c.orig linux/arch/mips/mm/r4xx0.c --- linux/arch/mips/mm/r4xx0.c.orig Sun Mar 11 13:52:24 2001 +++ linux/arch/mips/mm/r4xx0.c Tue Jun 26 10:27:45 2001 @@ -2121,10 +2121,6 @@ #undef DEBUG_TLB #undef DEBUG_TLBUPDATE -#define NTLB_ENTRIES 48 /* Fixed on all R4XX0 variants... */ - -#define NTLB_ENTRIES_HALF 24 /* Fixed on all R4XX0 variants... */ - void flush_tlb_all(void) { unsigned long flags; @@ -2146,7 +2142,7 @@ entry = get_wired(); /* Blast 'em all away. */ - while(entry < NTLB_ENTRIES) { + while(entry < mips_cpu.tlbsize) { set_index(entry); BARRIER; tlb_write_indexed(); @@ -2188,7 +2184,7 @@ __save_and_cli(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; size = (size + 1) >> 1; - if(size <= NTLB_ENTRIES_HALF) { + if(size <= mips_cpu.tlbsize/2) { int oldpid = (get_entryhi() & 0xff); int newpid = (mm->context & 0xff); @@ -2416,7 +2412,14 @@ /* Detect and size the various r4k caches. */ static void __init probe_icache(unsigned long config) { - icache_size = 1 << (12 + ((config >> 9) & 7)); + switch (mips_cpu.cputype) { + case CPU_VR41XX: + icache_size = 1 << (10 + ((config >> 9) & 7)); + break; + default: + icache_size = 1 << (12 + ((config >> 9) & 7)); + break; + } ic_lsize = 16 << ((config >> 5) & 1); printk("Primary instruction cache %dkb, linesize %d bytes.\n", @@ -2425,7 +2428,14 @@ static void __init probe_dcache(unsigned long config) { - dcache_size = 1 << (12 + ((config >> 6) & 7)); + switch (mips_cpu.cputype) { + case CPU_VR41XX: + dcache_size = 1 << (10 + ((config >> 6) & 7)); + break; + default: + dcache_size = 1 << (12 + ((config >> 6) & 7)); + break; + } dc_lsize = 16 << ((config >> 4) & 1); printk("Primary data cache %dkb, linesize %d bytes.\n", diff -Nru linux/arch/mips/vr4181/common/Makefile.orig linux/arch/mips/vr4181/common/Makefile --- linux/arch/mips/vr4181/common/Makefile.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/common/Makefile Tue Jun 26 10:27:45 2001 @@ -0,0 +1,18 @@ +# +# Makefile for common code of NEC vr4181 based boards +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +O_TARGET:= vr4181.o + +obj-y := irq.o kbd_no.o serial.o time.o + +include $(TOPDIR)/Rules.make diff -Nru linux/arch/mips/vr4181/common/irq.c.orig linux/arch/mips/vr4181/common/irq.c --- linux/arch/mips/vr4181/common/irq.c.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/common/irq.c Tue Jun 26 11:45:41 2001 @@ -0,0 +1,573 @@ +/* + * linux/arch/mips/vr41xx/irq.c + * + * Code to handle VR4181 IRQs plus some generic interrupt stuff. + * + * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1994, 1995, 1996, 1997 Ralf Baechle + * Copyright (C) 1999 Bradley D. LaRonde + * Copyright (C) 1999, 2000 Michael Klar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* [jsun] HACK */ +#define CONFIG_CPU_VR4181 y +#define DEVICE_IRQ_MASKL 0xffff + +extern asmlinkage void vr41xx_handle_irq(void); +extern void breakpoint(void); + +// This is used elsewhere +unsigned long spurious_count = 0; + +static unsigned short irq_mask_probe[(VR4181_IRQ_MAX + 9)/16]; + +#define BIT_MASK(bit) ( 1 << (bit) ) +#define MAKE_CP0_STATUS_IRQ_MASK(irq) ( BIT_MASK( (irq) + 8)) + +static inline void mask_irq(unsigned int irq) +{ + if (irq < 8) { + // it's a cpu interrupt + unsigned short newstatus = read_32bit_cp0_register(CP0_STATUS); + newstatus &= ~((unsigned short)1 << (irq + 8)); + change_cp0_status(ST0_IM, newstatus); + } else { + if (irq < 40) { + // it's an ICU interrupt + if (irq < 24) { + *VR4181_MSYSINT1REG &= ~((unsigned short)1 << (irq - 8)); + } else { + *VR4181_MSYSINT2REG &= ~((unsigned short)1 << (irq - 24)); + } + } else { + // it's a GPIO interrupt +#ifdef CONFIG_CPU_VR4181 + *VR4181_GPINTMSK |= (unsigned short)1 << (irq - 40); +#else + if (irq < 56) { + *VR4181_MGIUINTLREG &= ~((unsigned short)1 << (irq - 40)); + } else { + *VR4181_MGIUINTHREG &= ~((unsigned short)1 << (irq - 56)); + } +#endif + } + } +} + +static inline void unmask_irq(unsigned int irq) +{ + if (irq < 8) { + // it's a cpu interrupt + unsigned short newstatus = read_32bit_cp0_register(CP0_STATUS); + newstatus |= ((unsigned short)1 << (irq + 8)); + change_cp0_status(ST0_IM, newstatus); + } else { + if (irq < 40) { + // it's an ICU interrupt + if (irq < 24) { + *VR4181_MSYSINT1REG |= (unsigned short)1 << (irq - 8); + } else { + *VR4181_MSYSINT2REG |= (unsigned short)1 << (irq - 24); + } + } else { + // it's a GPIO interrupt (also ack edge-triggered or hold ints) +#ifdef CONFIG_CPU_VR4181 + if (!((irq < 48 ? *VR4181_GPINTTYPL : *VR4181_GPINTTYPH) & + ((unsigned short)2 << ((irq & 0x7) * 2)))) + *VR4181_GPINTSTAT = (unsigned short)1 << (irq - 40); + *VR4181_GPINTMSK &= ~((unsigned short)1 << (irq - 40)); +#else + if (irq < 56) { + if (*VR4181_GIUINTHTSELL & ((unsigned short)1 << (irq - 40))) + *VR4181_GIUINTSTATL = (unsigned short)1 << (irq - 40); + *VR4181_MGIUINTLREG |= (unsigned short)1 << (irq - 40); + } else { + if (*VR4181_GIUINTHTSELH & ((unsigned short)1 << (irq - 56))) + *VR4181_GIUINTSTATH = (unsigned short)1 << (irq - 56); + *VR4181_MGIUINTHREG |= (unsigned short)1 << (irq - 56); + } +#endif + } + } +} + +/* + * Per-IRQ information, by not initializing, this gets filled with NULLs: + */ +typedef struct { + struct irqaction* irq_action; // info on low-level handler + int depth; // < 0: enabled, >= 0: disabled +} irq_info_t; + +static irq_info_t irq_info[VR4181_IRQ_MAX + 1]; + +void inline disable_irq_nosync(unsigned int irq) +{ + disable_irq(irq); +} + +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + + if (++irq_info[irq_nr].depth >= 0) { + save_and_cli(flags); + mask_irq(irq_nr); + restore_flags(flags); + } +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + +#ifdef CONFIG_CPU_VR4122 + if (irq_nr == VR4181_IRQ_GPIO14) + return; +#endif + + if (--irq_info[irq_nr].depth < 0) { + save_and_cli(flags); + unmask_irq(irq_nr); + restore_flags(flags); + } +} + +int get_irq_list(char *buf) +{ + // make a human-readable list of irq actions + // used by /proc/interrupts + + int i, len = 0; + struct irqaction *action; + + for (i = 0; i <= VR4181_IRQ_MAX; i++) { + action = irq_info[i].irq_action; + if (!action) + continue; + len += sprintf(buf + len, "%2d: %8d %c %s", + i, kstat.irqs[0][i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + for (action = action->next; action; action = action->next) { + len += sprintf(buf + len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf + len, "\n"); + } + return len; +} + +atomic_t __mips_bh_counter; + +asmlinkage void do_IRQ(int irq, struct pt_regs *regs) +{ + // This handles all IRQ's that have been installed. + // It is called from int-handler.S. + // Actions without SA_INTERRUPT run with interrupts enabled + // and use the full signal-handling return. + // Actions with SA_INTERRUPT run with interrupts disabled. + + struct irqaction *action; + int cpu = smp_processor_id(); + + irq_enter(cpu, irq); + + kstat.irqs[cpu][irq]++; + + // don't interrupt on this same irq again until we're finished + // also, it gets left masked if there is no action (see below) + mask_irq(irq); + + action = irq_info[irq].irq_action; + + if (action != 0) { + unsigned long flags = 0; + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do { + // handle it + action->handler(irq, action->dev_id, regs); + + flags |= action->flags; + action = action->next; + } while (action); + + if (flags & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + + __cli(); + + unmask_irq(irq); + } + + irq_exit(cpu, irq); + + // Unmasking and softirq handling is done for us + // currently by ret_from_irq in entry.S. +} + +int add_irq_action(int irq, struct irqaction *new) +{ + // put the specified action (new) at the end of the list of actions for this irq + + int shared = 0; + struct irqaction *old, **p; + unsigned long flags; + + p = &irq_info[irq].irq_action; + + if ((old = *p) != NULL) { + if (!(old->flags & new->flags & SA_SHIRQ)) + return -EBUSY; + + // Shared interrupts must be all same type + if ((old->flags ^ new->flags) & SA_INTERRUPT) + return -EBUSY; + + do { + p = &old->next; + old = *p; + } while (old); + + shared = 1; + } + + if (new->flags & SA_SAMPLE_RANDOM) + rand_initialize_irq(irq); + + save_and_cli(flags); + + *p = new; + + if (!shared) { + irq_info[irq].depth = -1; + unmask_irq(irq); + } + + restore_flags(flags); + + return 0; +} + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + int retval; + struct irqaction *action; + + // some assertiveness + if (irq > VR4181_IRQ_MAX) + return -EINVAL; + + if (!handler) + return -EINVAL; + + // allocate a new action struct + action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + // initialize it + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + // add it to the list of actions for this irq + retval = add_irq_action(irq, action); + if (retval) + kfree(action); + + return retval; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction *action, **p; + unsigned long flags; + + if (irq > VR4181_IRQ_MAX) { + printk("Error - trying to free invalid IRQ %d\n", irq); + return; + } + + for (p = &irq_info[irq].irq_action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + // Found it - now free it + save_and_cli(flags); + *p = action->next; + if (!irq_info[irq].irq_action) { + irq_info[irq].depth = 0; + mask_irq(irq); + } + restore_flags(flags); + kfree(action); + return; + } +} + +unsigned long probe_irq_on(void) +{ + int i; + unsigned long delay; + + // Note the lower 8 bits of irq_mask_probe[0] are not used. Also, + // we don't probe for IRQ 0, since no way to report (0 = no IRQ found) + irq_mask_probe[0] = read_32bit_cp0_register(CP0_STATUS); + irq_mask_probe[1] = *VR4181_MSYSINT1REG; + irq_mask_probe[2] = *VR4181_MSYSINT2REG; +#ifdef CONFIG_CPU_VR4181 + irq_mask_probe[3] = ~*VR4181_GPINTMSK; +#else + irq_mask_probe[3] = *VR4181_MGIUINTLREG; + irq_mask_probe[4] = *VR4181_MGIUINTHREG; +#endif + + for (i = VR4181_IRQ_MAX; i > 0; i--) + if (!irq_info[i].irq_action && i != VR4181_IRQ_TIMER) + enable_irq(i); + + // Wait for spurious interrupts to mask themselves out again... + for (delay = jiffies + HZ/10; time_before(jiffies, delay); ) + barrier(); // about 100ms of delay + + irq_mask_probe[0] |= ~read_32bit_cp0_register(CP0_STATUS); + irq_mask_probe[1] |= ~*VR4181_MSYSINT1REG; + irq_mask_probe[2] |= ~*VR4181_MSYSINT2REG; +#ifdef CONFIG_CPU_VR4181 + irq_mask_probe[3] |= *VR4181_GPINTMSK; +#else + irq_mask_probe[3] |= ~*VR4181_MGIUINTLREG; + irq_mask_probe[4] |= ~*VR4181_MGIUINTHREG; +#endif + + return 0x12345678; +} + +int probe_irq_off(unsigned long unused) +{ + int i, irq_found, nr_irqs; + unsigned short tmp; + unsigned long flags; + + if (unused != 0x12345678) + printk("Bad IRQ probe detected\n"); + + // The following mess unmasks the interrupts we enabled to autoprobe + // and finishes bit-processing irq_mask_probe at the same time + save_and_cli(flags); + tmp = read_32bit_cp0_register(CP0_STATUS); + change_cp0_status(ST0_IM, tmp & irq_mask_probe[0]); + irq_mask_probe[0] |= tmp; + tmp = *VR4181_MSYSINT1REG; + *VR4181_MSYSINT1REG = tmp & irq_mask_probe[1]; + irq_mask_probe[1] |= tmp; + tmp = *VR4181_MSYSINT2REG; + *VR4181_MSYSINT2REG = tmp & irq_mask_probe[2]; + irq_mask_probe[2] |= tmp; +#ifdef CONFIG_CPU_VR4181 + tmp = ~*VR4181_GPINTMSK; + *VR4181_GPINTMSK = ~(tmp & irq_mask_probe[3]); + irq_mask_probe[3] |= tmp; +#else + tmp = *VR4181_MGIUINTLREG; + *VR4181_MGIUINTLREG = tmp & irq_mask_probe[3]; + irq_mask_probe[3] |= tmp; + tmp = *VR4181_MGIUINTHREG; + *VR4181_MGIUINTHREG = tmp & irq_mask_probe[4]; + irq_mask_probe[4] |= tmp; +#endif + restore_flags(flags); + + nr_irqs = 0; + irq_found = 0; + + for (i = VR4181_IRQ_MAX; i > 0; i--) + if (!(irq_mask_probe[(i + 8)/16] & ((unsigned short)1 << ((i + 8) & 15)))) { + irq_found = i; + nr_irqs++; + } + + if (nr_irqs > 1) + irq_found = -irq_found; + return irq_found; +} + +#ifdef CONFIG_PM +// +// Unlike the real pm_request callbacks, this one doesn't get registered +// with PM, and only gets called from do_hibernate and do_wakeup, because +// it has to happen in a certain order. It also assumes ints disabled. +// +void do_pm_irq_request(pm_request_t rqst) +{ + static unsigned short irq_mask[(VR4181_IRQ_MAX + 9)/16]; + unsigned int status; + + switch (rqst) { + case PM_RESUME: + status = read_32bit_cp0_register(CP0_STATUS) & 0xffff00ff; + write_32bit_cp0_register(CP0_STATUS, status | irq_mask[0]); + *VR4181_MSYSINT1REG = irq_mask[1]; + *VR4181_MSYSINT2REG = irq_mask[2]; +#ifdef CONFIG_CPU_VR4181 + *VR4181_GPINTMSK = irq_mask[3]; +#else + *VR4181_MGIUINTLREG = irq_mask[3]; + *VR4181_MGIUINTHREG = irq_mask[4]; +#endif + break; + case PM_SUSPEND: + irq_mask[0] = read_32bit_cp0_register(CP0_STATUS) & 0xff00; + irq_mask[1] = *VR4181_MSYSINT1REG; + irq_mask[2] = *VR4181_MSYSINT2REG; +#ifdef CONFIG_CPU_VR4181 + irq_mask[3] = *VR4181_GPINTMSK; +#else + irq_mask[3] = *VR4181_MGIUINTLREG; + irq_mask[4] = *VR4181_MGIUINTHREG; +#endif + break; + } +} +#endif // CONFIG_PM + +static struct irqaction cascade = { NULL, SA_INTERRUPT, 0, "cascade", NULL, NULL }; +static struct irqaction reserved = { NULL, SA_INTERRUPT, 0, "reserved", NULL, NULL }; + +void __init init_IRQ(void) +{ + set_except_vector(0, vr41xx_handle_irq); + + // Default all ICU IRQs to off ... + *VR4181_MSYSINT1REG = 0; + *VR4181_MSYSINT2REG = 0; + + // We initialize the level 2 ICU registers to all bits disabled. + // After this, these registers are the resposibility of whatever + // driver requests the IRQ of the corresponding level 1 ICU bit. + // (except GIU, where each level 2 bit has its own IRQ) +#ifdef CONFIG_CPU_VR4122 + *VR4181_MPCIINTREG = 0; + *VR4181_MSCUINTREG = 0; + *VR4181_MCSIINTREG = 0; +#else + *VR4181_MPIUINTREG = 0; + *VR4181_MAIUINTREG = 0; + *VR4181_MKIUINTREG = 0; +#endif +#ifdef CONFIG_CPU_VR4181 + *VR4181_GPINTMSK = 0xffff; +#else + *VR4181_MGIUINTLREG = 0; + *VR4181_MDSIUINTREG = 0; + *VR4181_MGIUINTHREG = 0; + *VR4181_MFIRINTREG = 0; +#endif + barrier(); + +// +// NOTE: This may break autodetection on some devices. If so, an IRQ mask needs +// to be defined in vr41xx-platdep.h to disable some GPIO lines from interrupting, +// or the broken autodetect IRQ needs to be defined explicitly. This define is +// for documentation purposes, only turn it off for test/debug: +// +#define LET_ALL_GPIO_INPUTS_CAUSE_INTERRUPTS +#ifdef LET_ALL_GPIO_INPUTS_CAUSE_INTERRUPTS + // Initialize secondary IRQ mask to enable any GPIO line configured as an input. + // Note that the int won't be active until enabled in the primary mask, too. +#ifdef CONFIG_CPU_VR4181 + { + unsigned int bits; + + bits = (unsigned int)(*VR4181_GPMD1REG | 0xaaaa) << 16; + bits |= *VR4181_GPMD0REG | 0xaaaa; + bits &= bits >> 1; + bits |= 0xcccccccc; + bits &= bits >> 2; + bits |= 0xf0f0f0f0; + bits &= bits >> 4; + bits |= 0xff00; + bits &= bits >> 8; + *VR4181_SECIRQMASKL = ~bits & DEVICE_IRQ_MASKL; + } +#else + *VR4181_SECIRQMASKL = ~*VR4181_GIUIOSELL & DEVICE_IRQ_MASKL; + *VR4181_SECIRQMASKH = ~*VR4181_GIUIOSELH & DEVICE_IRQ_MASKH; +#endif +#endif // LET_ALL_GPIO_INPUTS_CAUSE_INTERRUPTS + + // These don't really add handlers, these IRQs are never reported by the int + // vector handler. What these do is register the IRQ as non-sharable + add_irq_action(VR4181_IRQ_INT0, &cascade); + add_irq_action(VR4181_IRQ_GIU, &cascade); + add_irq_action(VR4181_IRQ_RTCL1, &reserved); + add_irq_action(VR4181_IRQ_RTCL2, &reserved); + +#ifdef CONFIG_REMOTE_DEBUG + printk("Setting debug traps - please connect the remote debugger.\n"); + + set_debug_traps(); + + // you may move this line to whereever you want + breakpoint(); +#endif +} + +#ifdef CONFIG_PCMCIA +/* + * dummy functions needed for PCMCIA support for non-ISA systems (vr4122) - mikemac + * + */ + +/* + * Return a mask of triggered interrupts (this + * can handle only legacy ISA interrupts). + */ +unsigned int +probe_irq_mask(unsigned long val) +{ + return val; +} + +#ifndef CONFIG_ISA + +#define CS_IN_USE 0x1e + +int try_irq(u_int Attributes, int irq, int specific) +{ + return CS_IN_USE; +} +#endif /* CONFIG_ISA */ + +#endif /* CONFIG_PCMCIA */ diff -Nru linux/arch/mips/vr4181/common/kbd_no.c.orig linux/arch/mips/vr4181/common/kbd_no.c --- linux/arch/mips/vr4181/common/kbd_no.c.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/common/kbd_no.c Tue Jun 26 10:27:45 2001 @@ -0,0 +1,51 @@ +/* + * linux/arch/mips/vr41xx/kbd_no.c + * + * Allows CONFIG_VT without keyboard (KIU) driver + * + * Copyright (C) 1999 Bradley D. LaRonde + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include + +void kbd_leds(unsigned char leds) +{ + return; +} + +int kbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + return (scancode == keycode) ? 0 : -EINVAL; +} + +int kbd_getkeycode(unsigned int scancode) +{ + return scancode; +} + +int kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + *keycode = scancode; + return 1; +} + +char kbd_unexpected_up(unsigned char keycode) +{ + return 0x80; +} + +void __init kbd_init_hw(void) +{ +#ifndef CONFIG_NEC_HARRIER + printk("No keyboard driver installed\n"); +#endif +} + diff -Nru linux/arch/mips/vr4181/common/serial.c.orig linux/arch/mips/vr4181/common/serial.c --- linux/arch/mips/vr4181/common/serial.c.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/common/serial.c Tue Jun 26 10:27:45 2001 @@ -0,0 +1,3838 @@ +/* + * linux/drivers/char/serial.c + * Serial (SIU) driver for NEC VR41xx CPUs, VR4102 and up only. + * + * Based almost entirely on linux/drivers/char/serial.c + * Modified for VR41xx by Michael Klar, mfklar@ponymail.com + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include + + +/* [jsun] HACK */ +#define MAX_VR_PORT 2 +#define SHARED_RS232_LINE 2 +#define SHARED_IRDA_LINE 1 + +#define VR4181_ENABLE_SERIAL(x) do { } while (0) +#define VR4181_DISABLE_SERIAL(x) do { } while (0) +#define VR4181_ENABLE_IRDA() do { } while (0) +#define VR4181_DISABLE_IRDA() do { } while (0) + +#define CONFIG_CPU_VR4181 y + +/* [jsun] END OF HACK */ + +#undef SERIAL_PARANOIA_CHECK +#define SERIAL_DO_RESTART + +/* 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 + +#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. + */ + +#ifdef MODVERSIONS +#include +#endif +#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 +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SERIAL_CONSOLE +#include +#endif +#ifdef CONFIG_MAGIC_SYSRQ +#include +#endif + +#include +#include +#include +#include +#include + +static unsigned int serial_powered_on; + +#define UART_CONFIG_NAME "16550A" +#define XMIT_FIFO_SIZE 16 +#define SERIAL_DEV_OFFSET 0 + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags, + unsigned int minor); +extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor); + +static char *serial_name = "VR41xx Serial driver"; +static char *serial_version = "0.4"; +static char *serial_revdate = "22-Oct-2000"; + +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]; +static int IRQ_timeout[NR_IRQS]; +#ifdef CONFIG_SERIAL_CONSOLE +static struct console sercons; +#endif +#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && !defined(MODULE) +static unsigned long break_pressed; /* break, really ... */ +#endif + +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 }, + { 0, 0} +}; + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + { type: PORT_16550A, \ + baud_base: 1152000, \ + port: (unsigned long)VR4181_SIURB, \ + irq: VR4181_IRQ_SIU, \ + flags: STD_COM_FLAGS, \ + iomem_base: (unsigned char *)VR4181_SIURB, \ + io_type: SERIAL_IO_MEM } +}; + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +#ifndef PREPARE_FUNC +#define PREPARE_FUNC(dev) (dev->prepare) +#define ACTIVATE_FUNC(dev) (dev->activate) +#define DEACTIVATE_FUNC(dev) (dev->deactivate) +#endif + + +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; +static DECLARE_MUTEX(tmp_buf_sem); +#ifdef CONFIG_PM +static unsigned char fcr_shadow[MAX_VR_PORT+1]; +#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; +} + +/* + * Note that overloading iomem_base to hold a kernel virtual address for + * SERIAL_VADDR io_type is pretty hackish, but there's no sense in adding + * another member to async_struct. It should probably be a union, but at + * this point, that would complicate kernel version imports too much + */ +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + switch (info->io_type) { +#ifdef CONFIG_HUB6 + case SERIAL_IO_HUB6: + outb(info->hub6 - 1 + offset, info->port); + return inb(info->port+1); +#endif + case SERIAL_IO_MEM: + return readb((unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); +#ifdef CONFIG_SERIAL_GSC + case SERIAL_IO_GSC: + return gsc_readb(info->iomem_base + offset); +#endif + default: + return inb(info->port + offset); + } +} + +static _INLINE_ void serial_out(struct async_struct *info, int offset, int value) +{ + switch (info->io_type) { +#ifdef CONFIG_HUB6 + case SERIAL_IO_HUB6: + outb(info->hub6 - 1 + offset, info->port); + outb(value, info->port+1); + break; +#endif + case SERIAL_IO_MEM: + writeb(value, (unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); + break; +#ifdef CONFIG_SERIAL_GSC + case SERIAL_IO_GSC: + gsc_writeb(value, info->iomem_base + offset); + break; +#endif + default: + outb(value, info->port+offset); + } +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(info, offset) serial_in(info, offset) +#define serial_outp(info, offset, value) serial_out(info, offset, value) + +#ifdef CONFIG_PM +static int pm_serial_request(struct pm_dev *dev, pm_request_t rqst, void *data) +{ +#ifndef CONFIG_CPU_VR4181 + static unsigned char save_state[6]; +#else + static unsigned char save_state[11]; +#endif + unsigned char dummy; + unsigned long flags; + + /* if not powered on, do nothing */ + if (!serial_powered_on) + return 0; + + switch (rqst) { + case PM_SUSPEND: + /* wait for anything in the xmit FIFO to send */ + save_and_cli(flags); + while (!(*VR4181_SIULS & UART_LSR_TEMT)) + barrier(); + save_state[0] = *VR4181_SIULC; + *VR4181_SIULC = UART_LCR_DLAB; /* DLAB = 1 to read baud rate */ + barrier(); + save_state[1] = *VR4181_SIUDLL; + save_state[2] = *VR4181_SIUDLM; + barrier(); + *VR4181_SIULC = 0; + barrier(); + save_state[3] = *VR4181_SIUIE; + *VR4181_SIUIE = save_state[3] & ~UART_IER_THRI; +// MFK: should also power down, cut clocks, and disable receive int, too, if +// wakeup not enabled for this device. +// MFK: should probably also enable DCD IRQ if wakeup is enabled + save_state[4] = *VR4181_SIUMC; +#ifndef CONFIG_CPU_VR4181 + save_state[5] = *VR4181_SIUIRSEL; +#else /* VR4181 has a 2nd UART */ + while (!(*VR4181_SIULS_2 & UART_LSR_TEMT)) + barrier(); + save_state[5] = *VR4181_SIULC_2; + *VR4181_SIULC_2 = UART_LCR_DLAB; /* DLAB = 1 to read baud rate */ + barrier(); + save_state[6] = *VR4181_SIUDLL_2; + save_state[7] = *VR4181_SIUDLM_2; + barrier(); + *VR4181_SIULC_2 = 0; + barrier(); + save_state[8] = *VR4181_SIUIE_2; + *VR4181_SIUIE_2 = save_state[8] & ~UART_IER_THRI; + save_state[9] = *VR4181_SIUMC_2; + save_state[10] = *VR4181_SIUIRSEL_2; +#endif + restore_flags(flags); + break; + case PM_RESUME: +// MFK: it's too late to disable ints here, if it was serial data that woke us +// up. Need to check in the interrupt handler if we're still in suspend, then +// throw out the data there if we are (because data in FIFO will be bogus). + /* clear out any pending data or interrupts */ + *VR4181_SIUFC = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; + dummy = *VR4181_SIURB; + dummy = *VR4181_SIUMS; + dummy = *VR4181_SIUIID; + barrier(); + while ( *VR4181_SIULS & UART_LSR_DR ) + dummy = *VR4181_SIURB; + + *VR4181_SIULC = UART_LCR_DLAB; + barrier(); + *VR4181_SIUDLL = save_state[1]; + *VR4181_SIUDLM = save_state[2]; + barrier(); + *VR4181_SIULC = 0; + barrier(); + *VR4181_SIUIE = save_state[3]; + *VR4181_SIUMC = save_state[4]; +#ifndef CONFIG_CPU_VR4181 + *VR4181_SIUIRSEL = save_state[5]; +#endif + barrier(); + *VR4181_SIULC = save_state[0]; +#ifndef CONFIG_CPU_VR4181 + *VR4181_SIUFC = fcr_shadow[(save_state[5] & 1) ? 1 : 0 ]; +#else + *VR4181_SIUFC = fcr_shadow[0]; + + /* same as above for 2nd UART on VR4181 */ + *VR4181_SIUFC_2 = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; + dummy = *VR4181_SIURB_2; + dummy = *VR4181_SIUMS_2; + dummy = *VR4181_SIUIID_2; + barrier(); + while ( *VR4181_SIULS_2 & UART_LSR_DR ) + dummy = *VR4181_SIURB_2; + + *VR4181_SIULC_2 = UART_LCR_DLAB; + barrier(); + *VR4181_SIUDLL_2 = save_state[6]; + *VR4181_SIUDLM_2 = save_state[7]; + barrier(); + *VR4181_SIULC_2 = 0; + barrier(); + *VR4181_SIUIE_2 = save_state[8]; + *VR4181_SIUMC_2 = save_state[9]; + *VR4181_SIUIRSEL_2 = save_state[10]; + barrier(); + *VR4181_SIULC_2 = save_state[5]; + *VR4181_SIUFC_2 = fcr_shadow[(save_state[10] & 1) ? 1 : 2 ]; +#endif + break; + } + return 0; +} +#endif + +/* + * 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); + } + 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); + } + 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; + int ignored = 0; + struct async_icount *icount; + + icount = &info->state->icount; + do { + ch = serial_inp(info, UART_RX); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + *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++; + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + */ + if (*status & info->ignore_status_mask) { + if (++ignored > 100) + break; + goto ignore_char; + } + *status &= info->read_status_mask; + + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif +#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && !defined(MODULE) + if (info->line == sercons.index) { + if (!break_pressed) { + break_pressed = jiffies; + goto ignore_char; + } + break_pressed = 0; + } +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } 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 (*status & UART_LSR_OE) { + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + } + } +#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && !defined(MODULE) + 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 + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + ignore_char: + *status = serial_inp(info, UART_LSR); + } while (*status & UART_LSR_DR); + tty_flip_buffer_push(tty); +} + +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) +{ + int count; + + if (info->x_char) { + serial_outp(info, UART_TX, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + return; + } + + count = info->xmit_fifo_size; + do { + serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (intr_done) + *intr_done = 0; + + if (info->xmit.head == info->xmit.tail) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } +} + +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + /* update input line counters */ + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; + } + 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 SERIAL_DEBUG_INTR + printk("rs_interrupt(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info) + return; + + do { + if (!info->tty || + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { + if (!end_mark) + end_mark = info; + goto next; + } + 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 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 SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info || !info->tty) + return; + + do { + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + check_modem_status(info); + 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; + } + } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + info->last_active = jiffies; +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#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); + } +} + +/* + * 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 = 0; + 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); + 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] - 2); + } +} + +/* + * --------------------------------------------------------------- + * 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 ? timeout : 1; +} + +/* + * Power on the hardware, also select between RS232 and IrDA for + * shared UARTs + * + * Note: for VR4121, other code (probably platdep_setup()) is + * responsible for setting the correct IR module settings in SIUIRSEL + * Note2: the VR4121 code sets the SIUIRSEL to not use the IR module + * for FIR. If we ever support FIR, will have to arbitrate that, too. + */ + +void serial_power_on(int line) +{ + unsigned long flags; + + save_and_cli(flags); + + /* turn on the UART clock, must be on before writes to any SIU regs */ + if (!serial_powered_on) { + *VR4181_CMUCLKMSK |= VR4181_CMUCLKMSK_MSKSIU; + barrier(); + } + serial_powered_on |= 1 << line; + + if (line == SHARED_IRDA_LINE) { + VR4181_ENABLE_IRDA(); +#ifdef CONFIG_CPU_VR4181 + *VR4181_SIUIRSEL_2 = 1; + // MFK: should probably set SIUCSEL_2, too, but to what value? +#else /* VR4121 and similar */ + *VR4181_SIUIRSEL = (*VR4181_SIUIRSEL & ~3) | 1; +#endif + } else { + VR4181_ENABLE_SERIAL(line); + if (line == SHARED_RS232_LINE) { +#ifdef CONFIG_CPU_VR4181 + *VR4181_SIUIRSEL_2 = 0; +#else + *VR4181_SIUIRSEL = (*VR4181_SIUIRSEL & ~1) | 0; +#endif + } + } + + restore_flags(flags); +} + +void serial_power_off(int line) +{ + unsigned long flags; + + save_and_cli(flags); + + serial_powered_on &= ~(1 << line); + + if (line == SHARED_IRDA_LINE) { + VR4181_DISABLE_IRDA(); + } else { + VR4181_DISABLE_SERIAL(line); + } + if (!serial_powered_on) + *VR4181_CMUCLKMSK &= ~VR4181_CMUCLKMSK_MSKSIU; /* turn off the UART clock */ + + restore_flags(flags); +} + +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; + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + serial_power_on(info->line); + + if (!state->port || !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 + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); +#ifdef CONFIG_PM + if (info->line <= MAX_VR_PORT) + fcr_shadow[info->line] = 0; +#endif + + /* + * 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 (serial_inp(info, UART_LSR) == 0xff) { + printk("LSR safety check engaged!\n"); + 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]); + handler = rs_interrupt; +#else + retval = -EBUSY; + goto errout; +#endif /* CONFIG_SERIAL_SHARE_IRQ */ + } else + handler = rs_interrupt_single; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + /* + * Insert serial port into IRQ chain. + */ + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + /* + * Clear the interrupt registers. + */ + /* (void) serial_inp(info, UART_LSR); */ /* (see above) */ + (void) serial_inp(info, UART_RX); + (void) serial_inp(info, UART_IIR); + (void) serial_inp(info, UART_MSR); + + /* + * 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; + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + + serial_outp(info, UART_MCR, info->MCR); + + /* + * Finally, enable interrupts + */ + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); /* enable interrupts */ + + /* + * And clear the interrupt registers again for luck. + */ + (void)serial_inp(info, UART_LSR); + (void)serial_inp(info, UART_RX); + (void)serial_inp(info, UART_IIR); + (void)serial_inp(info, UART_MSR); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + /* + * Set up serial timers... + */ + mod_timer(&serial_timer, jiffies + 2*HZ/100); + + /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + + /* + * and set the speed of the serial port + */ + change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct async_struct * info) +{ + unsigned long flags; + struct serial_state *state; + int retval; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * First unlink the serial port from the IRQ chain... + */ + if (info->next_port) + info->next_port->prev_port = info->prev_port; + if (info->prev_port) + info->prev_port->next_port = info->next_port; + else + IRQ_ports[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + /* + * Free the IRQ, if necessary + */ + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + + info->IER = 0; + serial_outp(info, UART_IER, 0x00); /* disable all intrs */ + info->MCR &= ~UART_MCR_OUT2; + + /* 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_PM + if (info->line <= MAX_VR_PORT) + fcr_shadow[info->line] = 0; +#endif + + (void)serial_in(info, UART_RX); /* read data port to reset things */ + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + serial_power_off(info->line); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(struct async_struct *info, + struct termios *old_termios) +{ + int quot = 0, baud_base, baud; + unsigned cflag, cval, fcr = 0; + int bits; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!(info->port)) + return; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: cval = 0x00; bits = 7; break; + } + if (cflag & CSTOPB) { + cval |= 0x04; + bits++; + } + if (cflag & PARENB) { + cval |= UART_LCR_PARITY; + bits++; + } + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ + baud_base = info->state->baud_base; + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } + /* If the quotient is zero refuse the change */ + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } + } + /* As a last resort, if the quotient is zero, default to 9600 bps */ + if (!quot) + quot = baud_base / 9600; + /* + * Work around a bug in the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && + (info->state->revision == 0x5201)) + quot++; + + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + /* Set up FIFO's */ + if ((info->state->baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); + + /* + * Set up parity check flag + */ +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_OE; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; + save_flags(flags); cli(); + 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 */ + serial_outp(info, UART_LCR, cval); /* reset DLAB */ + info->LCR = cval; /* Save LCR */ + serial_outp(info, UART_FCR, fcr); /* set fcr */ +#ifdef CONFIG_PM + if (info->line <= MAX_VR_PORT) + fcr_shadow[info->line] = fcr; +#endif + restore_flags(flags); +} + +static void rs_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_put_char")) + return; + + if (!tty || !info->xmit.buf) + return; + + save_flags(flags); cli(); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + restore_flags(flags); + return; + } + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + restore_flags(flags); +} + +static void rs_flush_chars(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) + return; + + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + save_flags(flags); cli(); + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + return ret; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) + return 0; + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void rs_flush_buffer(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_throttle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + rs_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; + + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + rs_send_xchar(tty, START_CHAR(tty)); + } + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int get_serial_info(struct async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + 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.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; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + state = info->state; + old_state = *state; + + change_irq = new_serial.irq != state->irq; + change_port = (new_serial.port != state->port); + + 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; + } + + if ((new_serial.irq >= NR_IRQS) || + (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 = 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_serial.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; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) + 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_serial.port; + } + if (state->type != PORT_UNKNOWN) + 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 ((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; + 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); + return put_user(result,value); +} + +static int set_modem_info(struct async_struct * info, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + + error = get_user(arg, value); + if (error) + return error; + 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; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +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); +} + +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 */ + unsigned long flags; + struct serial_icounter_struct *p_cuser; /* user space */ + int error; + + 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) { + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + + /* + * 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); + 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); + p_cuser = (struct serial_icounter_struct *) arg; + error = put_user(cnow.cts, &p_cuser->cts); + if (error) return error; + error = put_user(cnow.dsr, &p_cuser->dsr); + if (error) return error; + error = put_user(cnow.rng, &p_cuser->rng); + if (error) return error; + error = put_user(cnow.dcd, &p_cuser->dcd); + if (error) return error; + error = put_user(cnow.rx, &p_cuser->rx); + if (error) return error; + error = put_user(cnow.tx, &p_cuser->tx); + if (error) return error; + error = put_user(cnow.frame, &p_cuser->frame); + if (error) return error; + error = put_user(cnow.overrun, &p_cuser->overrun); + if (error) return error; + error = put_user(cnow.parity, &p_cuser->parity); + if (error) return error; + error = put_user(cnow.brk, &p_cuser->brk); + if (error) return error; + error = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); + if (error) return error; + return 0; + + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ( (cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->IER &= ~UART_IER_RLSI; + info->read_status_mask &= ~UART_LSR_DR; + if (info->flags & ASYNC_INITIALIZED) { + serial_out(info, UART_IER, info->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + rs_wait_until_sent(tty, info->timeout); + } + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + shutdown(info); + 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; + } + set_current_state(TASK_RUNNING); +#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); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } + memset(info, 0, sizeof(struct async_struct)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->io_type = sstate->io_type; + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; + info->xmit_fifo_size = sstate->xmit_fifo_size; + info->line = line; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->state = sstate; + if (sstate->info) { + kfree(info); + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +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; + } + + /* + * /dev/ttyS1 is the SIR IrDA port, UART is shared with /dev/ttyS0 + * (non-IrDA) on VR4121, /dev/ttyS2 on VR4181, so make the opening + * of the shared ports mutually exclusive: + * + * Need to do this before bumping up the state count on the line + * we're opening (in get_async_struct), otherwise, close will do + * the wrong thing if we abort on signal_pending + * + * More SMP-hostile code follows... + */ + while (1) { + int spline; + struct async_struct *spinfo; + + if (line == SHARED_RS232_LINE) + spline = SHARED_IRDA_LINE; + else if (line == SHARED_IRDA_LINE) + spline = SHARED_RS232_LINE; + else + break; + + retval = get_async_struct(spline, &spinfo); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + /* undo the count++ in get_async_struct */ + spinfo->state->count--; + + if (!spinfo->state->count) + break; + + /* + * if we get this far, that means we tried to open the + * shared RS232/IrDA port in a mode other than is already + * active, so we have to wait on the port closing + */ + if (filp->f_flags & O_NONBLOCK) { + MOD_DEC_USE_COUNT; + return -EAGAIN; + } + interruptible_sleep_on(&spinfo->close_wait); + if (signal_pending(current)) { + MOD_DEC_USE_COUNT; + return -ERESTARTSYS; + } + } + + 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 + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + 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_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info, 0); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ + +static inline int line_info(char *buf, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; + char stat_buf[30], control, status; + int ret; + unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, UART_CONFIG_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->quot = 0; + info->tty = 0; + } + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); + + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +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 + (begin-off); + 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 _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s%s (%s)\n", serial_name, + serial_version, LOCAL_VERSTRING, serial_revdate); +} + +/* + * 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 */ + + scr_info.magic = SERIAL_MAGIC; + scr_info.state = state; + scr_info.port = state->port; + scr_info.flags = state->flags; + 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); + return (irq > 0)? irq : 0; +} + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +static int size_fifo(struct async_struct *info) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(info, UART_FCR); + old_mcr = serial_inp(info, UART_MCR); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(info, UART_MCR, UART_MCR_LOOP); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(info, UART_DLL); + old_dlm = serial_inp(info, UART_DLM); + serial_outp(info, UART_DLL, 0x01); + serial_outp(info, UART_DLM, 0x00); + serial_outp(info, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(info, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(info, UART_RX); + serial_outp(info, UART_FCR, old_fcr); + serial_outp(info, UART_MCR, old_mcr); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, old_dll); + serial_outp(info, UART_DLM, old_dlm); + + return count; +} + +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void autoconfig_startech_uarts(struct async_struct *info, + struct serial_state *state, + unsigned long flags) +{ + unsigned char scratch, scratch2, scratch3; + + /* + * 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) { + /* 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. + * + */ + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, 0); + serial_outp(info, UART_DLM, 0); + state->revision = serial_inp(info, UART_DLL); + scratch = serial_inp(info, UART_DLM); + serial_outp(info, UART_LCR, 0); + if (scratch == 0x10 || scratch == 0x14) { + state->type = PORT_16850; + return; + } + + /* + * 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->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\n", + state->line); +#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); + } + serial_outp(info, UART_LCR, save_lcr); + if (state->type == PORT_16450) { + scratch = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0xa5); + status1 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0x5a); + status2 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, scratch); + + if ((status1 != 0xa5) || (status2 != 0x5a)) + state->type = PORT_8250; + } + state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; + + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + return; + } + + if (info->port) + request_region(info->port,8,"serial(auto)"); + + /* + * Reset the UART. + */ + serial_outp(info, UART_MCR, save_mcr); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + (void)serial_in(info, UART_RX); + serial_outp(info, UART_IER, 0); + + restore_flags(flags); +} + + +int register_serial(struct serial_struct *req); +void unregister_serial(int line); + +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); + +/* + * The serial driver boot-time initialization code! + */ +static int __init rs_init(void) +{ + int i; + struct serial_state * state; + + if (serial_timer.function) { + printk("RS_TIMER already set, another serial driver " + "already loaded?\n"); +#ifdef MODULE + printk("Can't load serial driver module over built-in " + "serial driver\n"); +#endif + return -EBUSY; + } + + 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; + } + +/* + * I don't know what the original motivation for this code was, but it doesn't + * appear to apply to VR41xx. Without the #if 0, echo "1" > /dev/ttyS1; from + * a serial console on ttyS0 will hang my machine. bdl + */ +#if 0 +#ifdef CONFIG_SERIAL_CONSOLE + /* + * The interrupt of the serial console port + * can't be shared. + */ + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif +#endif + + show_serial_version(); + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.driver_name = "serial"; + serial_driver.name = "tts/%d"; + 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.send_xchar = rs_send_xchar; + serial_driver.set_termios = rs_set_termios; + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; + serial_driver.break_ctl = rs_break; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua/%d"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; + + 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; +/* + if (state->port && check_region(state->port,8)) + continue; +*/ + if (i <= MAX_VR_PORT) { + state->xmit_fifo_size = 16; + state->type = PORT_16550A; + } + } + 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 at 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + state->port, state->irq, + UART_CONFIG_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 CONFIG_PM + pm_register(PM_ISA_DEV, PM_SYS_COM, pm_serial_request); +#endif + 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; + + save_flags(flags); cli(); + for (i = 0; i < NR_PORTS; i++) { + if ((rs_table[i].port == req->port) && + (rs_table[i].iomem_base == req->iomem_base)) + break; + } + 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=%d,irq=%d): " + "device already open\n", i, req->port, req->irq); + return -1; + } + state->irq = req->irq; + state->port = req->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 = req->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); + + 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_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) + release_region(rs_table[i].port, 8); + } + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } +} + +module_init(rs_init); +module_exit(rs_fini); + + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static struct async_struct async_sercons; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct async_struct *info) +{ + unsigned int tmout = 1000000; + + if (!(serial_powered_on & (1 << sercons.index))) + return; + while (--tmout && + ((serial_in(info, UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY)); +} + + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + static struct async_struct *info = &async_sercons; + int ier; + unsigned i; + + if (!(serial_powered_on & (1 << sercons.index))) + serial_power_on(sercons.index); + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(info, UART_IER); + serial_out(info, UART_IER, 0x00); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(info, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(info); + serial_out(info, UART_TX, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); + serial_out(info, UART_IER, ier); +} + +/* + * Receive character from the serial port + */ +static int serial_console_wait_key(struct console *co) +{ + static struct async_struct *info; + int ier, c; + + info = &async_sercons; + + if (!(serial_powered_on & (1 << sercons.index))) + serial_power_on(sercons.index); + /* + * First save the IER then disable the interrupts so + * that the real driver for the port does not get the + * character. + */ + ier = serial_in(info, UART_IER); + serial_out(info, UART_IER, 0x00); + + while ((serial_in(info, UART_LSR) & UART_LSR_DR) == 0); + c = serial_in(info, UART_RX); + + /* + * Restore the interrupts + */ + serial_out(info, UART_IER, ier); + + return c; +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init serial_console_setup(struct console *co, char *options) +{ + static struct async_struct *info; + struct serial_state *state; + unsigned cval; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; +#if defined(CONFIG_KDB) + extern int kdb_port; +#endif + + serial_power_on(co->index); + + 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'; + } + + /* + * 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; + 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; + info = &async_sercons; + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; + 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); + cval >>= 4; + 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; + +#if defined(CONFIG_KDB) + /* + * Remember I/O port for kdb + */ + if (kdb_port == 0 ) + kdb_port = ser->port; +#endif /* CONFIG_KDB */ + + return 0; +} + +static struct console sercons = { + "ttyS", + serial_console_write, + NULL, + serial_console_device, + serial_console_wait_key, + NULL, + serial_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +/* + * Register console. + */ +void __init serial_console_init(void) +{ + register_console(&sercons); +} +#endif + + +/* + * This is the interface to the remote debugger stub. + * I've put that here to be able to control the serial + * device more directly. + * + * We're a little paranoid with the barrier()s, just in + * case the compiler tries to be too cute. + */ + +static int initialized = 0; + +#if 0 +/* jsun, we like to use the debug board serial port for debugging + * and the main board serial port for serial console. + * + * Note that the debugger serial port is shared by NEC ROM debugger, + * and the baud rate is fixed at 38400. + * + * WARNING : No other code should follow this section as global macros + * such as VR4181_SIULC gets redefined here. + */ +#undef VR4181_SIURB +#undef VR4181_SIUTH +#undef VR4181_SIUDLL +#undef VR4181_SIUIE +#undef VR4181_SIUDLM +#undef VR4181_SIUIID +#undef VR4181_SIUFC +#undef VR4181_SIULC +#undef VR4181_SIUMC +#undef VR4181_SIULS +#undef VR4181_SIUMS +#undef VR4181_SIUSC + +#define VR4181_SIURB __preg8(KSEG1 + 0x17FFFFF0) +#define VR4181_SIUTH __preg8(KSEG1 + 0x17FFFFF0) +#define VR4181_SIUDLL __preg8(KSEG1 + 0x17FFFFF0) +#define VR4181_SIUIE __preg8(KSEG1 + 0x17FFFFF1) +#define VR4181_SIUDLM __preg8(KSEG1 + 0x17FFFFF1) +#define VR4181_SIUIID __preg8(KSEG1 + 0x17FFFFF2) +#define VR4181_SIUFC __preg8(KSEG1 + 0x17FFFFF2) +#define VR4181_SIULC __preg8(KSEG1 + 0x17FFFFF3) +#define VR4181_SIUMC __preg8(KSEG1 + 0x17FFFFF4) +#define VR4181_SIULS __preg8(KSEG1 + 0x17FFFFF5) +#define VR4181_SIUMS __preg8(KSEG1 + 0x17FFFFF6) +#define VR4181_SIUSC __preg8(KSEG1 + 0x17FFFFF7) +#endif + +void DbgInitSerial(void) +{ + unsigned char dummy; + + /* turn on the clocks to the serial port */ + serial_power_on(0); + + *VR4181_SIULC = UART_LCR_DLAB; /* prepare to set baud rate */ + barrier(); + + *VR4181_SIUDLL = 120; + *VR4181_SIUDLM = 0; /* hardcoded: set to 9600 */ + barrier(); + + *VR4181_SIULC = UART_LCR_WLEN8; /* clear DLAB, set up for 8N1 */ + barrier(); + + *VR4181_SIUIE = 0; /* disable interrupts */ + + *VR4181_SIUFC = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; +#ifdef CONFIG_PM + fcr_shadow[0] = 0; +#endif + *VR4181_SIUMC = UART_MCR_RTS | UART_MCR_DTR; /* set RTS and DTR */ + + dummy = *VR4181_SIURB; /* clear any pending ints */ + dummy = *VR4181_SIUMS; + dummy = *VR4181_SIUIID; + barrier(); + + /* clear the receive buffer (and finish clearing ints) */ + while ( *VR4181_SIULS & UART_LSR_DR ) + dummy = *VR4181_SIURB; +} + + +int myputDebugChar(unsigned char c) +{ + if (!initialized) { /* need to init device first */ + DbgInitSerial(); + initialized = 1; + } + + while ( !(*VR4181_SIULS & UART_LSR_THRE) ) ; + barrier(); + + *VR4181_SIUTH = c; + + return 1; +} + + +char mygetDebugChar(void) +{ + if (!initialized) { /* need to init device first */ + DbgInitSerial(); + initialized = 1; + } + + while ( !(*VR4181_SIULS & UART_LSR_DR) ) ; + barrier(); + + return(*VR4181_SIURB); +} + + +/* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + End: +*/ + diff -Nru linux/arch/mips/vr4181/common/time.c.orig linux/arch/mips/vr4181/common/time.c --- linux/arch/mips/vr4181/common/time.c.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/common/time.c Tue Jun 26 10:27:45 2001 @@ -0,0 +1,228 @@ +/* + * linux/arch/mips/vr41xx/time.c + * + * VR4181 timer interrupt using real-time clock. + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Copyright (C) 1999 Bradley D. LaRonde + * Copyright (C) 2000 Michael Klar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +extern int add_irq_action(int irq, struct irqaction *new); +extern volatile unsigned long wall_jiffies; +extern rwlock_t xtime_lock; + +#define USECS_PER_JIFFY (1000000/HZ) +#define COUNTS_PER_JIFFY ((32768 + HZ/2) / HZ) + +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; + +#ifdef CONFIG_RTC +unsigned long epoch_adj; +#else +#define epoch_adj 0 +#endif + +// per VR41xx docs, bad data can be read if between 2 counts +static inline unsigned short read_time_reg(volatile unsigned short *reg) +{ + unsigned short value; + do { + value = *reg; + barrier(); + } while (value != *reg); + return value; +} + +unsigned long get_rtc_time(volatile unsigned short *reg) +{ + unsigned short regh, regm, regl; + + // why this crazy order, you ask? to guarantee that neither m + // nor l wrap before all 3 read + do { + regm = read_time_reg(reg + 1); + barrier(); + regh = read_time_reg(reg + 2); + barrier(); + regl = read_time_reg(reg); + } while (regm != read_time_reg(reg + 1)); + return ((regh << 17) | (regm << 1) | (regl >> 15)) + epoch_adj; +} + +void set_rtc_time(unsigned long settime, volatile unsigned short *reg) +{ + unsigned short intreg; + unsigned long flags, timeval = settime - epoch_adj; + + spin_lock_irqsave(&rtc_lock, flags); + intreg = *VR4181_RTCINTREG & 0x05; + barrier(); + *reg = timeval << 15; + *(reg + 1) = timeval >> 1; + *(reg + 2) = timeval >> 17; + barrier(); + // assume that any ints that just triggered are invalid, since the + // time value is written non-atomically in 3 separate regs + *VR4181_RTCINTREG = 0x05 ^ intreg; + spin_unlock_irqrestore(&rtc_lock, flags); +} + +// must be called with ints disabled: +// +static unsigned long do_gettimeoffset(void) +{ + unsigned short count; + unsigned long offset; + + count = read_time_reg(VR4181_RTCL1CNTLREG); + if (count == 1) + offset = 0; + else + offset = (COUNTS_PER_JIFFY - count + 1) * 1000000 / 32768; + // detect if the counter wrapped, but int not serviced yet, being + // careful not to adjust if int happen after count was read just now + if (*VR4181_RTCINTREG & 0x0002 && (jiffies == wall_jiffies) && count != 2) + offset += USECS_PER_JIFFY; + return offset; +} + +// This version of gettimeofday has about 30us resolution +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags, lost; + + read_lock_irqsave(&xtime_lock, flags); + *tv = xtime; + tv->tv_usec += do_gettimeoffset(); + + // xtime is atomically updated in timer_bh, wall_jiffies is + // updated in timer_bh, jiffies is updated in timer int, so this + // will be nonzero if the timer bottom half hasn't executed yet: + lost = jiffies - wall_jiffies; + + read_unlock_irqrestore(&xtime_lock, flags); + + while (lost--) + tv->tv_usec += USECS_PER_JIFFY; + + while (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } +} + +void do_settimeofday(struct timeval *tv) +{ + write_lock_irq(&xtime_lock); + // undo whatever correction gettimeofday would have done + tv->tv_usec -= do_gettimeoffset(); + tv->tv_usec -= (jiffies - wall_jiffies) * USECS_PER_JIFFY; + + if (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; // stop active adjtime() + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irq(&xtime_lock); +} + +static unsigned long last_rtc_update; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + // Clear the interrupt. + *VR4181_RTCINTREG = 0x2; + + do_timer(regs); + + // If we have an externally synchronized Linux clock, then update + // CMOS clock accordingly every ~11 minutes. + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660) { + set_rtc_time(xtime.tv_sec, VR4181_ETIMELREG); + last_rtc_update = xtime.tv_sec; + } +} + +struct irqaction timer_action = { + timer_interrupt, SA_INTERRUPT, 0, + "timer", NULL, NULL +}; + +#ifdef CONFIG_PM +static int pm_time_request(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + unsigned long flags; + + switch (rqst) { + case PM_SUSPEND: + disable_irq(VR4181_IRQ_INT1); + break; + case PM_RESUME: + write_lock_irqsave(&xtime_lock, flags); + enable_irq(VR4181_IRQ_INT1); + xtime.tv_sec = get_rtc_time(VR4181_ETIMELREG); + xtime.tv_usec = 0; + // this shouldn't be necessary, but just to be safe... + *VR4181_RTCL1LREG = COUNTS_PER_JIFFY; + *VR4181_RTCL1HREG = 0; + write_unlock_irqrestore(&xtime_lock, flags); + break; + } + return 0; +} + +static int __init pm_time_init(void) +{ + pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, pm_time_request); + return 0; +} + +__initcall(pm_time_init); +#endif + +void __init time_init(void) +{ + // Set default time near beginning of Linux VR epoch. + xtime.tv_sec = get_rtc_time(VR4181_ETIMELREG); + xtime.tv_usec = 0; + + // Use RTCLong1 for the system timer. + // It has it's own cpu interrupt, is not T-Clock dependent, + // and has sufficient resolution. + + // Set the RTCLong1 counter (32.768kHz) to expire in 1 / HZ second + *VR4181_RTCL1LREG = COUNTS_PER_JIFFY; + *VR4181_RTCL1HREG = 0; + + // and ack any pending ints + *VR4181_RTCINTREG = 0x2; + + // Grab the IRQ for the cpu interrupt, not the ICU interrupt + // The RTCLong1 ICU interrupt is always left unmasked + // This can't use request_irq() because it's too early for kmalloc + add_irq_action(VR4181_IRQ_INT1, &timer_action); +} diff -Nru linux/arch/mips/vr4181/osprey/Makefile.orig linux/arch/mips/vr4181/osprey/Makefile --- linux/arch/mips/vr4181/osprey/Makefile.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/osprey/Makefile Tue Jun 26 10:27:45 2001 @@ -0,0 +1,18 @@ +# +# Makefile for common code of NEC Osprey board +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +O_TARGET:= osprey.o + +obj-y := setup.o prom.o reset.o int_handler.o dbg_io.o + +include $(TOPDIR)/Rules.make diff -Nru linux/arch/mips/vr4181/osprey/dbg_io.c.orig linux/arch/mips/vr4181/osprey/dbg_io.c --- linux/arch/mips/vr4181/osprey/dbg_io.c.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/osprey/dbg_io.c Tue Jun 26 10:27:45 2001 @@ -0,0 +1,126 @@ +#include + +#if defined(CONFIG_REMOTE_DEBUG) + +/* --- CONFIG --- */ + +typedef unsigned char uint8; +typedef unsigned int uint32; + +/* --- END OF CONFIG --- */ + +#define UART16550_BAUD_2400 2400 +#define UART16550_BAUD_4800 4800 +#define UART16550_BAUD_9600 9600 +#define UART16550_BAUD_19200 19200 +#define UART16550_BAUD_38400 38400 +#define UART16550_BAUD_57600 57600 +#define UART16550_BAUD_115200 115200 + +#define UART16550_PARITY_NONE 0 +#define UART16550_PARITY_ODD 0x08 +#define UART16550_PARITY_EVEN 0x18 +#define UART16550_PARITY_MARK 0x28 +#define UART16550_PARITY_SPACE 0x38 + +#define UART16550_DATA_5BIT 0x0 +#define UART16550_DATA_6BIT 0x1 +#define UART16550_DATA_7BIT 0x2 +#define UART16550_DATA_8BIT 0x3 + +#define UART16550_STOP_1BIT 0x0 +#define UART16550_STOP_2BIT 0x4 + +/* ----------------------------------------------------- */ + +/* === CONFIG === */ + +/* [jsun] we use the debug board serial port for kdb */ +#define BASE 0xb7fffff0 +#define MAX_BAUD 115200 + +#define REG_OFFSET 1 + +static int remoteDebugInitialized = 1; + +/* === END OF CONFIG === */ + + +/* register offset */ +#define OFS_RCV_BUFFER 0 +#define OFS_TRANS_HOLD 0 +#define OFS_SEND_BUFFER 0 +#define OFS_INTR_ENABLE (1*REG_OFFSET) +#define OFS_INTR_ID (2*REG_OFFSET) +#define OFS_DATA_FORMAT (3*REG_OFFSET) +#define OFS_LINE_CONTROL (3*REG_OFFSET) +#define OFS_MODEM_CONTROL (4*REG_OFFSET) +#define OFS_RS232_OUTPUT (4*REG_OFFSET) +#define OFS_LINE_STATUS (5*REG_OFFSET) +#define OFS_MODEM_STATUS (6*REG_OFFSET) +#define OFS_RS232_INPUT (6*REG_OFFSET) +#define OFS_SCRATCH_PAD (7*REG_OFFSET) + +#define OFS_DIVISOR_LSB (0*REG_OFFSET) +#define OFS_DIVISOR_MSB (1*REG_OFFSET) + + +/* memory-mapped read/write of the port */ +#define UART16550_READ(y) (*((volatile uint8*)(BASE + y))) +#define UART16550_WRITE(y, z) ((*((volatile uint8*)(BASE + y))) = z) + +void debugInit(uint32 baud, uint8 data, uint8 parity, uint8 stop) +{ + /* disable interrupts */ + UART16550_WRITE(OFS_INTR_ENABLE, 0); + + /* set up buad rate */ + { + uint32 divisor; + + /* set DIAB bit */ + UART16550_WRITE(OFS_LINE_CONTROL, 0x80); + + /* set divisor */ + divisor = MAX_BAUD / baud; + UART16550_WRITE(OFS_DIVISOR_LSB, divisor & 0xff); + UART16550_WRITE(OFS_DIVISOR_MSB, (divisor & 0xff00) >> 8); + + /* clear DIAB bit */ + UART16550_WRITE(OFS_LINE_CONTROL, 0x0); + } + + /* set data format */ + UART16550_WRITE(OFS_DATA_FORMAT, data | parity | stop); +} + + +uint8 getDebugChar(void) +{ + if (!remoteDebugInitialized) { + remoteDebugInitialized = 1; + debugInit(UART16550_BAUD_38400, + UART16550_DATA_8BIT, + UART16550_PARITY_NONE, UART16550_STOP_1BIT); + } + + while ((UART16550_READ(OFS_LINE_STATUS) & 0x1) == 0); + return UART16550_READ(OFS_RCV_BUFFER); +} + + +int putDebugChar(uint8 byte) +{ + if (!remoteDebugInitialized) { + remoteDebugInitialized = 1; + debugInit(UART16550_BAUD_38400, + UART16550_DATA_8BIT, + UART16550_PARITY_NONE, UART16550_STOP_1BIT); + } + + while ((UART16550_READ(OFS_LINE_STATUS) & 0x20) == 0); + UART16550_WRITE(OFS_SEND_BUFFER, byte); + return 1; +} + +#endif diff -Nru linux/arch/mips/vr4181/osprey/int_handler.S.orig linux/arch/mips/vr4181/osprey/int_handler.S --- linux/arch/mips/vr4181/osprey/int_handler.S.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/osprey/int_handler.S Tue Jun 26 10:27:45 2001 @@ -0,0 +1,186 @@ +/* + * linux/arch/mips/vr41xx/int-handler.S + * + * Copyright (C) 1995, 1996, 1997 Paul M. Antoine and Harald Koerfgen + * + * Written by Ralf Baechle and Andreas Busse, modified for DECStation + * support by Paul Antoine and Harald Koerfgen. + * + * completly rewritten: + * Copyright (C) 1998 Harald Koerfgen + * + * Adapted to the VR4181 and almost entirely rewritten: + * Copyright (C) 1999 Bradley D. LaRonde and Michael Klar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include +#include +#include + +/* jsun HACK */ +#define CONFIG_CPU_VR4181 y + + + .text + .set noreorder + +/* + * Interrupt handler for VR41xx based devices + * + * In a zealous attempt to save CPU cycles, we don't worry about making the code + * readable and instead optimize for the most frequently occurring ints (the + * timer int IRQ 3 and the ICU ints that cascade off IRQ 2), while keeping a + * priority scheme that actually makes some sense: + * + * Highest ---- CPU IP3 - Int1: RTClong1 IRQ 3 + * CPU IP4 - Int2: RTClong2 IRQ 4 + * CPU IP5 - Int3: HSP IRQ 5 + * CPU IP6 - Int4: unused IRQ 6 + * CPU IP7 - counter IRQ 7 + * ICU SYSINT1REG bit 0 IRQ 8 + * ... + * ICU SYSINT1REG bit 7 IRQ 15 + * GPIO 0 IRQ 40 + * ... + * GPIO 31 IRQ 71 + * ICU SYSINT1REG bit 9 IRQ 17 + * ... + * ICU SYSINT1REG bit 15 IRQ 23 + * ICU SYSINT2REG bit 0 IRQ 24 + * ... + * ICU SYSINT2REG bit 15 IRQ 39 + * CPU IP0 - software 1 IRQ 0 + * Lowest --- CPU IP1 - software 2 IRQ 1 + * + * IRQ 2 is the cascade to the ICU (CPU IP2, Int0) + * + * then we just return, if multiple IRQs are pending then we will just take + * another exception immediately. + * + */ + .align 5 + NESTED(vr41xx_handle_irq, PT_SIZE, ra) + .set noat + SAVE_ALL + CLI + .set at + .set noreorder + + // Get the pending interrupts + mfc0 t0,CP0_CAUSE + + // Isolate the allowed ones by anding with irq mask + mfc0 t2,CP0_STATUS + + andi t0,0xff00 + and t0,t2 + andi t1,t0,0x0800 // check for IRQ 3 + bnez t1,handle_it + li a0,3 + andi t1,t0,0xf000 // check for IRQ 4-7 + bnez t1,1f + andi t1,t0,0x0400 // check for IRQ 2 + bnez t1,icu_int + lui t3,%hi(VR4181_SYSINT1REG) + beqz t0,go_spurious // check for IRQ 0-1 + li a0,-1 + sll t0,4 +1: andi t1,t0,0x1000 + srl t0,1 + beqz t1,1b + addiu a0,1 + b handle_it + nop + +/* + * In the interest of speed, we implement a 3 tier check for which bit + * in the ICU level 1 interrupt registers is active. First, we determine + * which 16-bit register (1 or 2). Second, we narrow it down to which + * group of 4 bits, then we get the exact bit. a0 holds the IRQ number. + * Since we always add both 4 and 1 to a0, we initalize it to 5 less than + * the lowest IRQ number for each ICU level 1 register + */ +icu_int: // see delay slot instruction from branch that got us here + lhu t0,%lo(VR4181_SYSINT1REG)(t3) + lhu t2,%lo(VR4181_MSYSINT1REG)(t3) +/* + * OK, this next instruction deserves some explanation. The timer interrupt + * (a few others are similar, but the timer interrupt happens... well... all + * the time) comes in on both CPU int1 (IRQ 3) and ICU SYSINT1 bit 2 (IRQ 10), + * and since we check IRQ 3 before even loading the value of SYSINT1, it is + * possible for a timer interrupt to occur between the time we load CP0_STATUS to + * check IRQ 3 and the time we load SYSINT1REG to check IRQ 10. This will only + * happen when we're in the middle of servicing another interrupt, so we just + * ignore IRQ 10 (and a few others that behave similarly) and keep looking for + * the interrupt that actually caused the exception. When we re-enable + * interrupts, the timer int will immediately re-trigger and be detected as IRQ 3. + */ + and t0,0xfffb + and t0,t2 + beqz t0,2f + andi t1,t0,0x01ff // check for GIU int + xori t1,0x0100 // (and no higher priority SYSINT1 int) + bnez t1,1f + li a0,3 + + // if it is, break out further +#ifdef CONFIG_CPU_VR4181 + lui t3,%hi(VR4181_GPINTMSK) + lhu t0,%lo(VR4181_GPINTMSK)(t3) + lhu t2,%lo(VR4181_GPINTSTAT)(t3) + xori t0, 0xffff + and t0,t2 + bnez t0,1f + li a0,35 +#else + lhu t0,%lo(VR4181_GIUINTLREG)(t3) + lhu t2,%lo(VR4181_MGIUINTLREG)(t3) + li a0,35 + and t0,t2 + bnez t0,1f + lhu t2,%lo(VR4181_MGIUINTHREG)(t3) + lhu t0,%lo(VR4181_GIUINTHREG)(t3) + // load delay stall + and t0,t2 + bnez t0,1f + li a0,51 +#endif + +go_spurious: j spurious_interrupt + nop + +2: lhu t0,%lo(VR4181_SYSINT2REG)(t3) + lhu t2,%lo(VR4181_MSYSINT2REG)(t3) +#ifdef CONFIG_CPU_VR4181 + and t0,0xfffe // see above comment +#else + and t0,0xfffa // see above comment +#endif + and t0,t2 + beqz t0,go_spurious + li a0,19 + +1: andi t1,t0,0x000f + addi a0,4 + beqzl t1,1b // the beqzl will cancel the last + srl t0,4 // shift so we don't lose the bits + +1: andi t1,t0,0x0001 + addi a0,1 + beqz t1,1b + srl t0,1 + +handle_it: jal do_IRQ + move a1,sp + + j ret_from_irq + nop + + END(vr41xx_handle_irq) diff -Nru linux/arch/mips/vr4181/osprey/prom.c.orig linux/arch/mips/vr4181/osprey/prom.c --- linux/arch/mips/vr4181/osprey/prom.c.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/osprey/prom.c Tue Jun 26 10:27:45 2001 @@ -0,0 +1,52 @@ +/*********************************************************************** + * + * Copyright 2001 MontaVista Software Inc. + * Author: jsun@mvista.com or jsun@junsun.net + * + * arch/mips/vr4181/osprey/prom.c + * prom code for osprey. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + *********************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +char arcs_cmdline[COMMAND_LINE_SIZE]; + +/* + * [jsun] right now we assume it is the nec debug monitor, which does + * not pass any arguments. + */ +void __init prom_init() +{ + strcpy(arcs_cmdline, "ether=0,0x03fe0300,eth0 " + // strcpy(arcs_cmdline, "ether=0,0x0300,eth0 " + "video=vr4181fb:xres:240,yres:320,bpp:8"); + + mips_machgroup = MACH_GROUP_NEC_VR41XX; + mips_machtype = MACH_NEC_OSPREY; + + /* 16MB fixed */ + add_memory_region(0, 16 << 20, BOOT_MEM_RAM); +} + +void __init prom_free_prom_memory(void) +{ +} + +void __init prom_fixup_mem_map(unsigned long start, unsigned long end) +{ +} + diff -Nru linux/arch/mips/vr4181/osprey/reset.c.orig linux/arch/mips/vr4181/osprey/reset.c --- linux/arch/mips/vr4181/osprey/reset.c.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/osprey/reset.c Tue Jun 26 11:46:40 2001 @@ -0,0 +1,40 @@ +/* + * 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. + * + * Copyright (C) 1997, 2001 Ralf Baechle + * Copyright 2001 MontaVista Software Inc. + * Author: jsun@mvista.com or jsun@junsun.net + */ +#include +#include +#include +#include +#include +#include +#include + +void nec_osprey_restart(char *command) +{ + set_cp0_status(ST0_BEV | ST0_ERL); + change_cp0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); + flush_cache_all(); + write_32bit_cp0_register(CP0_WIRED, 0); + __asm__ __volatile__("jr\t%0"::"r"(0xbfc00000)); +} + +void nec_osprey_halt(void) +{ + printk(KERN_NOTICE "\n** You can safely turn off the power\n"); + while (1) + __asm__(".set\tmips3\n\t" + "wait\n\t" + ".set\tmips0"); +} + +void nec_osprey_power_off(void) +{ + nec_osprey_halt(); +} diff -Nru linux/arch/mips/vr4181/osprey/setup.c.orig linux/arch/mips/vr4181/osprey/setup.c --- linux/arch/mips/vr4181/osprey/setup.c.orig Tue Jun 26 10:27:45 2001 +++ linux/arch/mips/vr4181/osprey/setup.c Tue Jun 26 10:27:45 2001 @@ -0,0 +1,188 @@ +/* + * linux/arch/mips/vr4181/setup.c + * + * VR41xx setup routines + * + * Copyright (C) 1999 Bradley D. LaRonde + * Copyright (C) 1999, 2000 Michael Klar + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +extern struct kbd_ope no_kbd_ops; + +struct semaphore vr4181_dma_sem; + +// This enables the CF hacks below, to be replaced with real CF driver eventually +#if defined(CONFIG_EVEREX_FREESTYLE) +#define CF_HACK +#else +// If CF doesn't work on your device, try changing this to #define: +#undef CF_HACK +#endif + +#ifdef CONFIG_TCIC +#define TCIC_BASE 0x240 + +#define TCIC_MODE 0x08 +#define TCIC_AUX 0x0E + +#define TCIC_MODE_PGMMASK 0x1f +#define TCIC_AUX_ILOCK (6<<5) +#define TCIC_ILOCK_CRESET 0x04 +#endif + +extern void nec_osprey_restart(char* c); +extern void nec_osprey_halt(void); +extern void nec_osprey_power_off(void); + +#if defined(CF_HACK) || defined(CONFIG_I82365) +void __init put_cf_reg(unsigned char reg, unsigned char val) +{ +#ifdef CONFIG_CPU_VR4181 + *VR4181_PCCARDINDEX = reg; + barrier(); + *VR4181_PCCARDDATA = val; +#else + outb(reg, 0x3e0); + outb(val, 0x3e1); +#endif +} +#endif + +#if defined(CONFIG_NEC_OSPREY) || defined(CONFIG_IBM_WORKPAD) || \ + defined(CONFIG_NEC_MOBILEGEAR2_R300) || \ + defined(CONFIG_NEC_MOBILEGEAR2_R310) || \ + defined(CONFIG_NEC_MOBILEGEAR2_R430) || \ + defined(CONFIG_NEC_MOBILEGEAR2_R530) || \ + defined(CONFIG_NEC_MOBILEGEAR2_R700) || \ + defined(CONFIG_NEC_MOBILEGEAR2_R730) || \ + defined(CONFIG_DOCOMO_SIGMARION) || \ + defined(CONFIG_AGENDA_VR3) +// extern void platdep_setup(void); +void platdep_setup(void) {} +#else +void platdep_setup(void) {} +#endif + +void __init nec_osprey_setup(void) +{ + mips_io_port_base = VR4181_PORT_BASE; + isa_slot_offset = VR4181_ISAMEM_BASE; + +#ifdef CONFIG_BLK_DEV_IDE + ide_ops = &vr4181_ide_ops; +#endif + +#ifdef CONFIG_FB + conswitchp = &dummy_con; +#endif + + // reset the PC card, and power off +#ifdef CONFIG_I82365 + put_cf_reg(0x03, 0x20); // socket 0 + put_cf_reg(0x43, 0x20); // socket 1 + put_cf_reg(0x02, 0x00); // socket 0 + put_cf_reg(0x42, 0x00); // socket 1 +#endif +#ifdef CONFIG_TCIC +{ + u_char mode; + mode = (inb(TCIC_BASE+TCIC_MODE) & TCIC_MODE_PGMMASK) | TCIC_AUX_ILOCK; + outb(mode, TCIC_BASE+TCIC_MODE); + outb(TCIC_ILOCK_CRESET, TCIC_BASE+TCIC_AUX); +} +#endif + +#ifdef CF_HACK + // this is a nasty hack to initialize the CF registers, eventually this + // is to be replaced with a real CF controller driver + { + unsigned char i; + unsigned char cfinitdata[] = { 0x90, 0x63, 0x00, 0x35, 0x00, 0x55, + 0x76, 0x03, 0x77, 0x03, 0x70, 0x01, 0x78, 0x01, 0x10, 0x00, + 0x1f, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0x20, 0x00, 0xe4, 0x00, + 0xe0, 0x3f, 0x00, 0x02, 0xe5, 0x00, 0xed, 0x00, 0x1b, 0x3f, + 0x00, 0x00, 0xee, 0x00, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00 }; + +#ifdef CONFIG_CPU_VR4181 + // Enable CF, not key scan. + *VR4181_KEYEN = 0; + + // Unmask all CF IRQs for now except for IRQ 3 (which makes it fail) + // I think the bit sense in reversed from what User Manual states + *VR4181_INTMSKREG = 0xdeb0; + + // Set CF wait states. + *VR4181_CFG_REG_1 = 1; +#else +#warning "Generic CF hack in use, assuming non-i82365 registers set up prior to boot" +#endif + + put_cf_reg(0x02, 0x10); + udelay(1); + + for (i = 0x02; i < 0x36; i++) + put_cf_reg(i, cfinitdata[i-2]); + + // enable the IO and mem windows, now that start and stop values set up + put_cf_reg(0x06, 0xdf); + + // reset the card + put_cf_reg(0x03, 0x23); + udelay(1); + put_cf_reg(0x03, 0x63); + mdelay(30); + + // now set up CF config register (this assumes it is a CF card...) + writeb(0x43, ioremap(0x10200, 1)); + } +#endif + + _machine_restart = nec_osprey_restart; + _machine_halt = nec_osprey_halt; + _machine_power_off = nec_osprey_power_off; + + /* setup resource limit */ + ioport_resource.end = 0xffffffff; + iomem_resource.end = 0xffffffff; + + // Insure that vr4181_dma_sem is initialized as unlocked, even + // in the case of a failed hibernate/wakeup: + init_MUTEX(&vr4181_dma_sem); + + // Do platform-dependent setup. + // This is mostly stuff that doesn't fit well anywhere else. + platdep_setup(); + + /* [jsun] hack */ + /* + printk("[jsun] hack to change external ISA control register, %x -> %x\n", + (*VR4181_XISACTL), + (*VR4181_XISACTL) | 0x2); + *VR4181_XISACTL |= 0x2; + */ + + // *VR4181_GPHIBSTH = 0x2000; + // *VR4181_GPMD0REG = 0x00c0; + // *VR4181_GPINTEN = 1<<6; + + /* [jsun] I believe this will get the interrupt type right + * for the ether port. + */ + *VR4181_GPINTTYPL = 0x3000; +} diff -Nru linux/arch/mips/config.in.orig linux/arch/mips/config.in --- linux/arch/mips/config.in.orig Tue Jun 26 10:20:55 2001 +++ linux/arch/mips/config.in Tue Jun 26 12:27:00 2001 @@ -20,6 +20,7 @@ bool 'Support for BAGET MIPS series (EXPERIMENTAL)' CONFIG_BAGET_MIPS bool 'Support for DECstations (EXPERIMENTAL)' CONFIG_DECSTATION bool 'Support for NEC DDB Vrc-5074 (EXPERIMENTAL)' CONFIG_DDB5074 + bool 'Support for NEC Osprey board (EXPERIMENTAL)' CONFIG_NEC_OSPREY bool 'Support for Galileo EV96100 Evaluation board' CONFIG_MIPS_EV96100 bool 'Support for Galileo EV64120 Evaluation board' CONFIG_MIPS_EV64120 if [ "$CONFIG_MIPS_EV64120" = "y" ]; then @@ -183,6 +184,14 @@ define_bool CONFIG_NEW_TIME_C y define_bool CONFIG_NEW_IRQ y fi +if [ "$CONFIG_NEC_OSPREY" = "y" ]; then + define_bool CONFIG_CPU_VR41XX y + define_bool CONFIG_CPU_R4X00 y + define_bool CONFIG_VR4181 y + define_bool CONFIG_ISA y + define_bool CONFIG_SERIAL y + define_bool CONFIG_SCSI n +fi if [ "$CONFIG_MIPS_ITE8172" = "y" ]; then define_bool CONFIG_PCI y define_bool CONFIG_IT8712 y @@ -255,8 +264,13 @@ define_bool CONFIG_CPU_HAS_WB n fi else + if [ "$CONFIG_CPU_VR41XX" = "y" ]; then + define_bool CONFIG_CPU_HAS_LLSC n + define_bool CONFIG_CPU_HAS_WB n + else define_bool CONFIG_CPU_HAS_LLSC y define_bool CONFIG_CPU_HAS_WB n + fi fi fi endmenu @@ -266,7 +280,8 @@ if [ "$CONFIG_DECSTATION" = "y" -o \ "$CONFIG_DDB5074" = "y" -o \ "$CONFIG_DDB5476" = "y" -o \ - "$CONFIG_NINO" = "y" ]; then + "$CONFIG_NINO" = "y" -o \ + "$CONFIG_NEC_OSPREY" = "y" ]; then define_bool CONFIG_CPU_LITTLE_ENDIAN y else bool 'Generate little endian code' CONFIG_CPU_LITTLE_ENDIAN diff -Nru linux/arch/mips/Makefile.orig linux/arch/mips/Makefile --- linux/arch/mips/Makefile.orig Tue Jun 12 14:15:23 2001 +++ linux/arch/mips/Makefile Tue Jun 26 12:58:27 2001 @@ -188,6 +188,16 @@ endif # +# NEC Osprey (vr4181) board +# +ifdef CONFIG_NEC_OSPREY +SUBDIRS += arch/mips/vr4181/common arch/mips/vr4181/osprey +LIBS += arch/mips/vr4181/common/vr4181.o \ + arch/mips/vr4181/osprey/osprey.o +LOADADDR += 0x80014000 +endif + +# # Galileo EV64120 Board # ifdef CONFIG_MIPS_EV64120 diff -Nru linux/drivers/char/Makefile.orig linux/drivers/char/Makefile --- linux/drivers/char/Makefile.orig Tue Jun 19 10:39:10 2001 +++ linux/drivers/char/Makefile Tue Jun 26 10:27:45 2001 @@ -115,6 +115,11 @@ SERIAL = endif +ifeq ($(CONFIG_VR4181),y) + KEYBD = + SERIAL = +endif + ifneq ($(CONFIG_SUN_SERIAL),) SERIAL = endif diff -Nru linux/drivers/net/ne.c.orig linux/drivers/net/ne.c --- linux/drivers/net/ne.c.orig Wed Jun 13 15:06:05 2001 +++ linux/drivers/net/ne.c Tue Jun 26 10:27:45 2001 @@ -166,6 +166,22 @@ SET_MODULE_OWNER(dev); +#if defined(CONFIG_NEC_OSPREY) + /* [jsun] Okay, this is a total hack. NE2k on NEC Osprey is + * at 0x03fe0300 (+ VR4181_PORT_BASE). However, the stupid + * include/linux/if.h:ifmap structure cannot handle u32 base value. + * We get the address chopped off. + * + * The real fix is to change ifmap structure. However, + * There are some concerns (from David Miller) as to binary + * compatibility. So I just hack this file for mvista tree only. + */ + if (dev->base_addr == 0x0300) { + base_addr = dev->base_addr = 0x03fe0300; + printk(KERN_WARNING "nec.c : Modify NE2K base addr from 0x300 to 0x03fe0300 for NEC Osprey board!\n"); + } +#endif + /* First check any supplied i/o locations. User knows best. */ if (base_addr > 0x1ff) /* Check a single specified location. */ return ne_probe1(dev, base_addr); diff -Nru linux/include/asm-mips/cpu.h.orig linux/include/asm-mips/cpu.h --- linux/include/asm-mips/cpu.h.orig Tue Jun 19 15:13:26 2001 +++ linux/include/asm-mips/cpu.h Tue Jun 26 11:45:44 2001 @@ -44,6 +44,7 @@ #define PRID_IMP_R6000A 0x0600 #define PRID_IMP_R10000 0x0900 #define PRID_IMP_R4300 0x0b00 +#define PRID_IMP_VR41XX 0x0c00 #define PRID_IMP_R12000 0x0e00 #define PRID_IMP_R8000 0x1000 #define PRID_IMP_R4600 0x2000 @@ -52,11 +53,11 @@ #define PRID_IMP_R4640 0x2200 #define PRID_IMP_R4650 0x2200 /* Same as R4640 */ #define PRID_IMP_R5000 0x2300 -#define PRID_IMP_R5432 0x5400 #define PRID_IMP_SONIC 0x2400 #define PRID_IMP_MAGIC 0x2500 #define PRID_IMP_RM7000 0x2700 #define PRID_IMP_NEVADA 0x2800 /* RM5260 ??? */ +#define PRID_IMP_R5432 0x5400 #define PRID_IMP_4KC 0x8000 #define PRID_IMP_5KC 0x8100 #define PRID_IMP_4KEC 0x8400 diff -Nru linux/include/asm-mips/bootinfo.h.orig linux/include/asm-mips/bootinfo.h --- linux/include/asm-mips/bootinfo.h.orig Tue Jun 12 14:15:46 2001 +++ linux/include/asm-mips/bootinfo.h Tue Jun 26 11:30:02 2001 @@ -31,10 +31,12 @@ #define MACH_GROUP_SIBYTE 16 /* Sibyte Eval Boards */ #define MACH_GROUP_TOSHIBA 17 /* Toshiba Reference Systems TSBREF */ #define MACH_GROUP_ALCHEMY 18 /* Alchemy Semi Eval Boards*/ +#define MACH_GROUP_NEC_VR41XX 19 /* NEC Vr41xx based boards/gadgets */ #define GROUP_NAMES { "unknown", "Jazz", "Digital", "ARC", "SNI", "ACN", \ "SGI", "Cobalt", "NEC DDB", "Baget", "Cosine", "Galileo", "Momentum", \ - "ITE", "Philips", "Globepspan", "SiByte", "Toshiba", "Alchemy" } + "ITE", "Philips", "Globepspan", "SiByte", "Toshiba", "Alchemy", \ + "NEC Vr41xx" } /* * Valid machtype values for group unknown (low order halfword of mips_machtype) @@ -195,6 +197,14 @@ #define GROUP_ALCHEMY_NAMES { "PB1000" } /* the actual board name */ /* + * Valid machtype for group NEC_VR41XX + */ +#define MACH_NEC_OSPREY 0 /* Osprey eval board */ +#define MACH_NEC_EAGLE 1 /* NEC Eagle board */ + +#define GROUP_NEC_VR41XX_NAMES { "Osprey", "Eagle" } + +/* * Valid cputype values */ #define CPU_UNKNOWN 0 @@ -235,9 +245,10 @@ #define CPU_TX3922 35 #define CPU_TX3927 36 #define CPU_AU1000 37 -#define CPU_4KEC 37 -#define CPU_4KSC 38 -#define CPU_LAST 39 +#define CPU_4KEC 38 +#define CPU_4KSC 39 +#define CPU_VR41XX 40 +#define CPU_LAST 40 #define CPU_NAMES { "unknown", "R2000", "R3000", "R3000A", "R3041", "R3051", \ "R3052", "R3081", "R3081E", "R4000PC", "R4000SC", "R4000MC", \ @@ -245,7 +256,7 @@ "R6000A", "R8000", "R10000", "R4300", "R4650", "R4700", "R5000", \ "R5000A", "R4640", "Nevada", "RM7000", "R5432", "MIPS 4Kc", \ "MIPS 5Kc", "R4310", "SiByte SB1", "TX3912", "TX3922", "TX3927", \ - "Au1000", "MIPS 4KEc", "MIPS 4KSc" } + "Au1000", "MIPS 4KEc", "MIPS 4KSc", "NEC Vr41xx" } #define COMMAND_LINE_SIZE 256 diff -Nru linux/include/asm-mips/vr4181/vr4181.h.orig linux/include/asm-mips/vr4181/vr4181.h --- linux/include/asm-mips/vr4181/vr4181.h.orig Tue Jun 26 10:27:45 2001 +++ linux/include/asm-mips/vr4181/vr4181.h Tue Jun 26 10:27:45 2001 @@ -0,0 +1,477 @@ +/* $Id: vr4181.h,v 1.2 2000/10/30 04:53:03 brad Exp $ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1999 by Michael Klar + * Copyright (C) 2001 Monta Vista Software, jsun@mvista.com + */ +#ifndef __ASM_MIPS_VR4181_H +#define __ASM_MIPS_VR4181_H + +#include + +// CPU interrupts +#define VR4181_IRQ_SW1 0 // IP0 - Software interrupt +#define VR4181_IRQ_SW2 1 // IP1 - Software interrupt +#define VR4181_IRQ_INT0 2 // IP2 - All but battery, high speed modem, and real time clock +#define VR4181_IRQ_INT1 3 // IP3 - RTC Long1 (system timer) +#define VR4181_IRQ_INT2 4 // IP4 - RTC Long2 +#define VR4181_IRQ_INT3 5 // IP5 - High Speed Modem (unused on VR4181) +#define VR4181_IRQ_INT4 6 // IP6 - Unused +#define VR4181_IRQ_TIMER 7 // IP7 - Timer interrupt from CPO_COMPARE (Note: RTC Long1 is the system timer.) + +// Cascaded from VR4181_IRQ_INT0 (ICU mapped interrupts) +#define VR4181_IRQ_BATTERY 8 +#define VR4181_IRQ_POWER 9 +#define VR4181_IRQ_RTCL1 10 // Use VR4181_IRQ_INT1 instead. +#define VR4181_IRQ_ETIMER 11 +#define VR4181_IRQ_RFU12 12 +#define VR4181_IRQ_PIU 13 +#define VR4181_IRQ_AIU 14 +#define VR4181_IRQ_KIU 15 +#define VR4181_IRQ_GIU 16 // This is a cascade to IRQs 40-71. Do not use. +#define VR4181_IRQ_SIU 17 +#define VR4181_IRQ_RFU18 18 +#define VR4181_IRQ_SOFT 19 +#define VR4181_IRQ_RFU20 20 +#define VR4181_IRQ_DOZEPIU 21 +#define VR4181_IRQ_RFU22 22 +#define VR4181_IRQ_RFU23 23 +#define VR4181_IRQ_RTCL2 24 // Use VR4181_IRQ_INT2 instead. +#define VR4181_IRQ_LED 25 +#define VR4181_IRQ_ECU 26 // (CompactFlash) +#define VR4181_IRQ_CSU 27 +#define VR4181_IRQ_USB 28 +#define VR4181_IRQ_DMA 29 +#define VR4181_IRQ_LCD 30 +#define VR4181_IRQ_RFU31 31 +#define VR4181_IRQ_RFU32 32 +#define VR4181_IRQ_RFU33 33 +#define VR4181_IRQ_RFU34 34 +#define VR4181_IRQ_RFU35 35 +#define VR4181_IRQ_RFU36 36 +#define VR4181_IRQ_RFU37 37 +#define VR4181_IRQ_RFU38 38 +#define VR4181_IRQ_RFU39 39 +// Note: Still need to do the extra VR4181 IRQ definitions + +// Cascaded from VR4181_IRQ_GIU +#define VR4181_IRQ_GPIO0 40 +#define VR4181_IRQ_GPIO1 41 +#define VR4181_IRQ_GPIO2 42 +#define VR4181_IRQ_GPIO3 43 +#define VR4181_IRQ_GPIO4 44 +#define VR4181_IRQ_GPIO5 45 +#define VR4181_IRQ_GPIO6 46 +#define VR4181_IRQ_GPIO7 47 +#define VR4181_IRQ_GPIO8 48 +#define VR4181_IRQ_GPIO9 49 +#define VR4181_IRQ_GPIO10 50 +#define VR4181_IRQ_GPIO11 51 +#define VR4181_IRQ_GPIO12 52 +#define VR4181_IRQ_GPIO13 53 +#define VR4181_IRQ_GPIO14 54 +#define VR4181_IRQ_GPIO15 55 + +// Alternative to above GPIO IRQ defines +#define VR4181_IRQ_GPIO(pin) ((VR4181_IRQ_GPIO0) + (pin)) + +#define VR4181_IRQ_MAX 55 + +#ifndef _LANGUAGE_ASSEMBLY +#define __preg8 (volatile unsigned char*) +#define __preg16 (volatile unsigned short*) +#define __preg32 (volatile unsigned int*) +#else +#define __preg8 +#define __preg16 +#define __preg32 +#endif + +// Embedded CPU peripheral registers +// Note that many of the registers have different physical address for VR4181 + +// Bus Control Unit (BCU) +#define VR4181_BCUCNTREG1 __preg16(KSEG1 + 0x0A000000) /* BCU control register 1 (R/W) */ +#define VR4181_CMUCLKMSK __preg16(KSEG1 + 0x0A000004) /* Clock mask register (R/W) */ +#define VR4181_CMUCLKMSK_MSKCSUPCLK 0x0040 +#define VR4181_CMUCLKMSK_MSKAIUPCLK 0x0020 +#define VR4181_CMUCLKMSK_MSKPIUPCLK 0x0010 +#define VR4181_CMUCLKMSK_MSKADUPCLK 0x0008 +#define VR4181_CMUCLKMSK_MSKSIU18M 0x0004 +#define VR4181_CMUCLKMSK_MSKADU18M 0x0002 +#define VR4181_CMUCLKMSK_MSKUSB 0x0001 +#define VR4181_CMUCLKMSK_MSKSIU VR4181_CMUCLKMSK_MSKSIU18M +#define VR4181_BCUSPEEDREG __preg16(KSEG1 + 0x0A00000C) /* BCU access time parameter (R/W) */ +#define VR4181_BCURFCNTREG __preg16(KSEG1 + 0x0A000010) /* BCU refresh control register (R/W) */ +#define VR4181_REVIDREG __preg16(KSEG1 + 0x0A000014) /* Revision ID register (R) */ +#define VR4181_CLKSPEEDREG __preg16(KSEG1 + 0x0A000018) /* Clock speed register (R) */ +#define VR4181_EDOMCYTREG __preg16(KSEG1 + 0x0A000300) /* Memory cycle timing register (R/W) */ +#define VR4181_MEMCFG_REG __preg16(KSEG1 + 0x0A000304) /* Memory configuration register (R/W) */ +#define VR4181_MODE_REG __preg16(KSEG1 + 0x0A000308) /* SDRAM mode register (R/W) */ +#define VR4181_SDTIMINGREG __preg16(KSEG1 + 0x0A00030C) /* SDRAM timing register (R/W) */ + +// DMA Control Unit (DCU) +#define VR4181_MICDEST1REG1 __preg16(KSEG1 + 0x0A000020) /* Microphone destination 1 address register 1 (R/W) */ +#define VR4181_MICDEST1REG2 __preg16(KSEG1 + 0x0A000022) /* Microphone destination 1 address register 2 (R/W) */ +#define VR4181_MICDEST2REG1 __preg16(KSEG1 + 0x0A000024) /* Microphone destination 2 address register 1 (R/W) */ +#define VR4181_MICDEST2REG2 __preg16(KSEG1 + 0x0A000026) /* Microphone destination 2 address register 2 (R/W) */ +#define VR4181_SPKRRC1REG1 __preg16(KSEG1 + 0x0A000028) /* Speaker Source 1 address register 1 (R/W) */ +#define VR4181_SPKRRC1REG2 __preg16(KSEG1 + 0x0A00002A) /* Speaker Source 1 address register 2 (R/W) */ +#define VR4181_SPKRRC2REG1 __preg16(KSEG1 + 0x0A00002C) /* Speaker Source 2 address register 1 (R/W) */ +#define VR4181_SPKRRC2REG2 __preg16(KSEG1 + 0x0A00002E) /* Speaker Source 2 address register 2 (R/W) */ +#define VR4181_DMARSTREG __preg16(KSEG1 + 0x0A000040) /* DMA Reset register (R/W) */ +#define VR4181_AIUDMAMSKREG __preg16(KSEG1 + 0x0A000046) /* Audio DMA mask register (R/W) */ +#define VR4181_USBDMAMSKREG __preg16(KSEG1 + 0x0A000600) /* USB DMA Mask register (R/W) */ +#define VR4181_USBRXS1AREG1 __preg16(KSEG1 + 0x0A000602) /* USB Rx source 1 address register 1 (R/W) */ +#define VR4181_USBRXS1AREG2 __preg16(KSEG1 + 0x0A000604) /* USB Rx source 1 address register 2 (R/W) */ +#define VR4181_USBRXS2AREG1 __preg16(KSEG1 + 0x0A000606) /* USB Rx source 2 address register 1 (R/W) */ +#define VR4181_USBRXS2AREG2 __preg16(KSEG1 + 0x0A000608) /* USB Rx source 2 address register 2 (R/W) */ +#define VR4181_USBTXS1AREG1 __preg16(KSEG1 + 0x0A00060A) /* USB Tx source 1 address register 1 (R/W) */ +#define VR4181_USBTXS1AREG2 __preg16(KSEG1 + 0x0A00060C) /* USB Tx source 1 address register 2 (R/W) */ +#define VR4181_USBTXS2AREG1 __preg16(KSEG1 + 0x0A00060E) /* USB Tx source 2 address register 1 (R/W) */ +#define VR4181_USBTXS2AREG2 __preg16(KSEG1 + 0x0A000610) /* USB Tx source 2 address register 2 (R/W) */ +#define VR4181_USBRXD1AREG1 __preg16(KSEG1 + 0x0A00062A) /* USB Rx destination 1 address register 1 (R/W) */ +#define VR4181_USBRXD1AREG2 __preg16(KSEG1 + 0x0A00062C) /* USB Rx destination 1 address register 2 (R/W) */ +#define VR4181_USBRXD2AREG1 __preg16(KSEG1 + 0x0A00062E) /* USB Rx destination 2 address register 1 (R/W) */ +#define VR4181_USBRXD2AREG2 __preg16(KSEG1 + 0x0A000630) /* USB Rx destination 2 address register 2 (R/W) */ +#define VR4181_USBTXD1AREG1 __preg16(KSEG1 + 0x0A000632) /* USB Tx destination 1 address register 1 (R/W) */ +#define VR4181_USBTXD1AREG2 __preg16(KSEG1 + 0x0A000634) /* USB Tx destination 1 address register 2 (R/W) */ +#define VR4181_USBTXD2AREG1 __preg16(KSEG1 + 0x0A000636) /* USB Tx destination 2 address register 1 (R/W) */ +#define VR4181_USBTXD2AREG2 __preg16(KSEG1 + 0x0A000638) /* USB Tx destination 2 address register 2 (R/W) */ +#define VR4181_RxRCLENREG __preg16(KSEG1 + 0x0A000652) /* USB Rx record length register (R/W) */ +#define VR4181_TxRCLENREG __preg16(KSEG1 + 0x0A000654) /* USB Tx record length register (R/W) */ +#define VR4181_MICRCLENREG __preg16(KSEG1 + 0x0A000658) /* Microphone record length register (R/W) */ +#define VR4181_SPKRCLENREG __preg16(KSEG1 + 0x0A00065A) /* Speaker record length register (R/W) */ +#define VR4181_USBCFGREG __preg16(KSEG1 + 0x0A00065C) /* USB configuration register (R/W) */ +#define VR4181_MICDMACFGREG __preg16(KSEG1 + 0x0A00065E) /* Microphone DMA configuration register (R/W) */ +#define VR4181_SPKDMACFGREG __preg16(KSEG1 + 0x0A000660) /* Speaker DMA configuration register (R/W) */ +#define VR4181_DMAITRQREG __preg16(KSEG1 + 0x0A000662) /* DMA interrupt request register (R/W) */ +#define VR4181_DMACLTREG __preg16(KSEG1 + 0x0A000664) /* DMA control register (R/W) */ +#define VR4181_DMAITMKREG __preg16(KSEG1 + 0x0A000666) /* DMA interrupt mask register (R/W) */ + +// ISA Bridge +#define VR4181_ISABRGCTL __preg16(KSEG1 + 0x0B0002C0) /* ISA Bridge Control Register (R/W) */ +#define VR4181_ISABRGSTS __preg16(KSEG1 + 0x0B0002C2) /* ISA Bridge Status Register (R/W) */ +#define VR4181_XISACTL __preg16(KSEG1 + 0x0B0002C4) /* External ISA Control Register (R/W) */ + +// Clocked Serial Interface (CSI) +#define VR4181_CSIMODE __preg16(KSEG1 + 0x0B000900) /* CSI Mode Register (R/W) */ +#define VR4181_CSIRXDATA __preg16(KSEG1 + 0x0B000902) /* CSI Receive Data Register (R) */ +#define VR4181_CSITXDATA __preg16(KSEG1 + 0x0B000904) /* CSI Transmit Data Register (R/W) */ +#define VR4181_CSILSTAT __preg16(KSEG1 + 0x0B000906) /* CSI Line Status Register (R/W) */ +#define VR4181_CSIINTMSK __preg16(KSEG1 + 0x0B000908) /* CSI Interrupt Mask Register (R/W) */ +#define VR4181_CSIINTSTAT __preg16(KSEG1 + 0x0B00090a) /* CSI Interrupt Status Register (R/W) */ +#define VR4181_CSITXBLEN __preg16(KSEG1 + 0x0B00090c) /* CSI Transmit Burst Length Register (R/W) */ +#define VR4181_CSIRXBLEN __preg16(KSEG1 + 0x0B00090e) /* CSI Receive Burst Length Register (R/W) */ + +// Interrupt Control Unit (ICU) +#define VR4181_SYSINT1REG __preg16(KSEG1 + 0x0A000080) /* Level 1 System interrupt register 1 (R) */ +#define VR4181_MSYSINT1REG __preg16(KSEG1 + 0x0A00008C) /* Level 1 mask system interrupt register 1 (R/W) */ +#define VR4181_NMIREG __preg16(KSEG1 + 0x0A000098) /* NMI register (R/W) */ +#define VR4181_SOFTINTREG __preg16(KSEG1 + 0x0A00009A) /* Software interrupt register (R/W) */ +#define VR4181_SYSINT2REG __preg16(KSEG1 + 0x0A000200) /* Level 1 System interrupt register 2 (R) */ +#define VR4181_MSYSINT2REG __preg16(KSEG1 + 0x0A000206) /* Level 1 mask system interrupt register 2 (R/W) */ +#define VR4181_PIUINTREGro __preg16(KSEG1 + 0x0B000082) /* Level 2 PIU interrupt register (R) */ +#define VR4181_AIUINTREG __preg16(KSEG1 + 0x0B000084) /* Level 2 AIU interrupt register (R) */ +#define VR4181_MPIUINTREG __preg16(KSEG1 + 0x0B00008E) /* Level 2 mask PIU interrupt register (R/W) */ +#define VR4181_MAIUINTREG __preg16(KSEG1 + 0x0B000090) /* Level 2 mask AIU interrupt register (R/W) */ +#define VR4181_MKIUINTREG __preg16(KSEG1 + 0x0B000092) /* Level 2 mask KIU interrupt register (R/W) */ +#define VR4181_KIUINTREG __preg16(KSEG1 + 0x0B000198) /* Level 2 KIU interrupt register (R) */ + +// Power Management Unit (PMU) +#define VR4181_PMUINTREG __preg16(KSEG1 + 0x0B0000A0) /* PMU Status Register (R/W) */ +#define VR4181_PMUINT_POWERSW 0x1 /* Power switch */ +#define VR4181_PMUINT_BATT 0x2 /* Low batt during normal operation */ +#define VR4181_PMUINT_DEADMAN 0x4 /* Deadman's switch */ +#define VR4181_PMUINT_RESET 0x8 /* Reset switch */ +#define VR4181_PMUINT_RTCRESET 0x10 /* RTC Reset */ +#define VR4181_PMUINT_TIMEOUT 0x20 /* HAL Timer Reset */ +#define VR4181_PMUINT_BATTLOW 0x100 /* Battery low */ +#define VR4181_PMUINT_RTC 0x200 /* RTC Alarm */ +#define VR4181_PMUINT_DCD 0x400 /* DCD# */ +#define VR4181_PMUINT_GPIO0 0x1000 /* GPIO0 */ +#define VR4181_PMUINT_GPIO1 0x2000 /* GPIO1 */ +#define VR4181_PMUINT_GPIO2 0x4000 /* GPIO2 */ +#define VR4181_PMUINT_GPIO3 0x8000 /* GPIO3 */ + +#define VR4181_PMUCNTREG __preg16(KSEG1 + 0x0B0000A2) /* PMU Control Register (R/W) */ +#define VR4181_PMUWAITREG __preg16(KSEG1 + 0x0B0000A8) /* PMU Wait Counter Register (R/W) */ +#define VR4181_PMUDIVREG __preg16(KSEG1 + 0x0B0000AC) /* PMU Divide Mode Register (R/W) */ +#define VR4181_DRAMHIBCTL __preg16(KSEG1 + 0x0B0000B2) /* DRAM Hibernate Control Register (R/W) */ + +// Real Time Clock Unit (RTC) +#define VR4181_ETIMELREG __preg16(KSEG1 + 0x0B0000C0) /* Elapsed Time L Register (R/W) */ +#define VR4181_ETIMEMREG __preg16(KSEG1 + 0x0B0000C2) /* Elapsed Time M Register (R/W) */ +#define VR4181_ETIMEHREG __preg16(KSEG1 + 0x0B0000C4) /* Elapsed Time H Register (R/W) */ +#define VR4181_ECMPLREG __preg16(KSEG1 + 0x0B0000C8) /* Elapsed Compare L Register (R/W) */ +#define VR4181_ECMPMREG __preg16(KSEG1 + 0x0B0000CA) /* Elapsed Compare M Register (R/W) */ +#define VR4181_ECMPHREG __preg16(KSEG1 + 0x0B0000CC) /* Elapsed Compare H Register (R/W) */ +#define VR4181_RTCL1LREG __preg16(KSEG1 + 0x0B0000D0) /* RTC Long 1 L Register (R/W) */ +#define VR4181_RTCL1HREG __preg16(KSEG1 + 0x0B0000D2) /* RTC Long 1 H Register (R/W) */ +#define VR4181_RTCL1CNTLREG __preg16(KSEG1 + 0x0B0000D4) /* RTC Long 1 Count L Register (R) */ +#define VR4181_RTCL1CNTHREG __preg16(KSEG1 + 0x0B0000D6) /* RTC Long 1 Count H Register (R) */ +#define VR4181_RTCL2LREG __preg16(KSEG1 + 0x0B0000D8) /* RTC Long 2 L Register (R/W) */ +#define VR4181_RTCL2HREG __preg16(KSEG1 + 0x0B0000DA) /* RTC Long 2 H Register (R/W) */ +#define VR4181_RTCL2CNTLREG __preg16(KSEG1 + 0x0B0000DC) /* RTC Long 2 Count L Register (R) */ +#define VR4181_RTCL2CNTHREG __preg16(KSEG1 + 0x0B0000DE) /* RTC Long 2 Count H Register (R) */ +#define VR4181_RTCINTREG __preg16(KSEG1 + 0x0B0001DE) /* RTC Interrupt Register (R/W) */ + +// Deadman's Switch Unit (DSU) +#define VR4181_DSUCNTREG __preg16(KSEG1 + 0x0B0000E0) /* DSU Control Register (R/W) */ +#define VR4181_DSUSETREG __preg16(KSEG1 + 0x0B0000E2) /* DSU Dead Time Set Register (R/W) */ +#define VR4181_DSUCLRREG __preg16(KSEG1 + 0x0B0000E4) /* DSU Clear Register (W) */ +#define VR4181_DSUTIMREG __preg16(KSEG1 + 0x0B0000E6) /* DSU Elapsed Time Register (R/W) */ + +// General Purpose I/O Unit (GIU) +#define VR4181_GPMD0REG __preg16(KSEG1 + 0x0B000300) /* GPIO Mode 0 Register (R/W) */ +#define VR4181_GPMD1REG __preg16(KSEG1 + 0x0B000302) /* GPIO Mode 1 Register (R/W) */ +#define VR4181_GPMD2REG __preg16(KSEG1 + 0x0B000304) /* GPIO Mode 2 Register (R/W) */ +#define VR4181_GPMD3REG __preg16(KSEG1 + 0x0B000306) /* GPIO Mode 3 Register (R/W) */ +#define VR4181_GPDATHREG __preg16(KSEG1 + 0x0B000308) /* GPIO Data High Register (R/W) */ +#define VR4181_GPDATHREG_GPIO16 0x0001 +#define VR4181_GPDATHREG_GPIO17 0x0002 +#define VR4181_GPDATHREG_GPIO18 0x0004 +#define VR4181_GPDATHREG_GPIO19 0x0008 +#define VR4181_GPDATHREG_GPIO20 0x0010 +#define VR4181_GPDATHREG_GPIO21 0x0020 +#define VR4181_GPDATHREG_GPIO22 0x0040 +#define VR4181_GPDATHREG_GPIO23 0x0080 +#define VR4181_GPDATHREG_GPIO24 0x0100 +#define VR4181_GPDATHREG_GPIO25 0x0200 +#define VR4181_GPDATHREG_GPIO26 0x0400 +#define VR4181_GPDATHREG_GPIO27 0x0800 +#define VR4181_GPDATHREG_GPIO28 0x1000 +#define VR4181_GPDATHREG_GPIO29 0x2000 +#define VR4181_GPDATHREG_GPIO30 0x4000 +#define VR4181_GPDATHREG_GPIO31 0x8000 +#define VR4181_GPDATLREG __preg16(KSEG1 + 0x0B00030A) /* GPIO Data Low Register (R/W) */ +#define VR4181_GPDATLREG_GPIO0 0x0001 +#define VR4181_GPDATLREG_GPIO1 0x0002 +#define VR4181_GPDATLREG_GPIO2 0x0004 +#define VR4181_GPDATLREG_GPIO3 0x0008 +#define VR4181_GPDATLREG_GPIO4 0x0010 +#define VR4181_GPDATLREG_GPIO5 0x0020 +#define VR4181_GPDATLREG_GPIO6 0x0040 +#define VR4181_GPDATLREG_GPIO7 0x0080 +#define VR4181_GPDATLREG_GPIO8 0x0100 +#define VR4181_GPDATLREG_GPIO9 0x0200 +#define VR4181_GPDATLREG_GPIO10 0x0400 +#define VR4181_GPDATLREG_GPIO11 0x0800 +#define VR4181_GPDATLREG_GPIO12 0x1000 +#define VR4181_GPDATLREG_GPIO13 0x2000 +#define VR4181_GPDATLREG_GPIO14 0x4000 +#define VR4181_GPDATLREG_GPIO15 0x8000 +#define VR4181_GPINTEN __preg16(KSEG1 + 0x0B00030C) /* GPIO Interrupt Enable Register (R/W) */ +#define VR4181_GPINTMSK __preg16(KSEG1 + 0x0B00030E) /* GPIO Interrupt Mask Register (R/W) */ +#define VR4181_GPINTTYPH __preg16(KSEG1 + 0x0B000310) /* GPIO Interrupt Type High Register (R/W) */ +#define VR4181_GPINTTYPL __preg16(KSEG1 + 0x0B000312) /* GPIO Interrupt Type Low Register (R/W) */ +#define VR4181_GPINTSTAT __preg16(KSEG1 + 0x0B000314) /* GPIO Interrupt Status Register (R/W) */ +#define VR4181_GPHIBSTH __preg16(KSEG1 + 0x0B000316) /* GPIO Hibernate Pin State High Register (R/W) */ +#define VR4181_GPHIBSTL __preg16(KSEG1 + 0x0B000318) /* GPIO Hibernate Pin State Low Register (R/W) */ +#define VR4181_GPSICTL __preg16(KSEG1 + 0x0B00031A) /* GPIO Serial Interface Control Register (R/W) */ +#define VR4181_KEYEN __preg16(KSEG1 + 0x0B00031C) /* Keyboard Scan Pin Enable Register (R/W) */ +#define VR4181_PCS0STRA __preg16(KSEG1 + 0x0B000320) /* Programmable Chip Select [0] Start Address Register (R/W) */ +#define VR4181_PCS0STPA __preg16(KSEG1 + 0x0B000322) /* Programmable Chip Select [0] Stop Address Register (R/W) */ +#define VR4181_PCS0HIA __preg16(KSEG1 + 0x0B000324) /* Programmable Chip Select [0] High Address Register (R/W) */ +#define VR4181_PCS1STRA __preg16(KSEG1 + 0x0B000326) /* Programmable Chip Select [1] Start Address Register (R/W) */ +#define VR4181_PCS1STPA __preg16(KSEG1 + 0x0B000328) /* Programmable Chip Select [1] Stop Address Register (R/W) */ +#define VR4181_PCS1HIA __preg16(KSEG1 + 0x0B00032A) /* Programmable Chip Select [1] High Address Register (R/W) */ +#define VR4181_PCSMODE __preg16(KSEG1 + 0x0B00032C) /* Programmable Chip Select Mode Register (R/W) */ +#define VR4181_LCDGPMODE __preg16(KSEG1 + 0x0B00032E) /* LCD General Purpose Mode Register (R/W) */ +#define VR4181_MISCREG0 __preg16(KSEG1 + 0x0B000330) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG1 __preg16(KSEG1 + 0x0B000332) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG2 __preg16(KSEG1 + 0x0B000334) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG3 __preg16(KSEG1 + 0x0B000336) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG4 __preg16(KSEG1 + 0x0B000338) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG5 __preg16(KSEG1 + 0x0B00033A) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG6 __preg16(KSEG1 + 0x0B00033C) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG7 __preg16(KSEG1 + 0x0B00033D) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG8 __preg16(KSEG1 + 0x0B000340) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG9 __preg16(KSEG1 + 0x0B000342) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG10 __preg16(KSEG1 + 0x0B000344) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG11 __preg16(KSEG1 + 0x0B000346) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG12 __preg16(KSEG1 + 0x0B000348) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG13 __preg16(KSEG1 + 0x0B00034A) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG14 __preg16(KSEG1 + 0x0B00034C) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_MISCREG15 __preg16(KSEG1 + 0x0B00034E) /* Misc. R/W Battery Backed Registers for Non-Volatile Storage (R/W) */ +#define VR4181_SECIRQMASKL VR4181_GPINTEN +// No SECIRQMASKH for VR4181 + +// Touch Panel Interface Unit (PIU) +#define VR4181_PIUCNTREG __preg16(KSEG1 + 0x0B000122) /* PIU Control register (R/W) */ +#define VR4181_PIUCNTREG_PIUSEQEN 0x0004 +#define VR4181_PIUCNTREG_PIUPWR 0x0002 +#define VR4181_PIUCNTREG_PADRST 0x0001 + +#define VR4181_PIUINTREG __preg16(KSEG1 + 0x0B000124) /* PIU Interrupt cause register (R/W) */ +#define VR4181_PIUINTREG_OVP 0x8000 +#define VR4181_PIUINTREG_PADCMD 0x0040 +#define VR4181_PIUINTREG_PADADP 0x0020 +#define VR4181_PIUINTREG_PADPAGE1 0x0010 +#define VR4181_PIUINTREG_PADPAGE0 0x0008 +#define VR4181_PIUINTREG_PADDLOST 0x0004 +#define VR4181_PIUINTREG_PENCHG 0x0001 + +#define VR4181_PIUSIVLREG __preg16(KSEG1 + 0x0B000126) /* PIU Data sampling interval register (R/W) */ +#define VR4181_PIUSTBLREG __preg16(KSEG1 + 0x0B000128) /* PIU A/D converter start delay register (R/W) */ +#define VR4181_PIUCMDREG __preg16(KSEG1 + 0x0B00012A) /* PIU A/D command register (R/W) */ +#define VR4181_PIUASCNREG __preg16(KSEG1 + 0x0B000130) /* PIU A/D port scan register (R/W) */ +#define VR4181_PIUAMSKREG __preg16(KSEG1 + 0x0B000132) /* PIU A/D scan mask register (R/W) */ +#define VR4181_PIUCIVLREG __preg16(KSEG1 + 0x0B00013E) /* PIU Check interval register (R) */ +#define VR4181_PIUPB00REG __preg16(KSEG1 + 0x0B0002A0) /* PIU Page 0 Buffer 0 register (R/W) */ +#define VR4181_PIUPB01REG __preg16(KSEG1 + 0x0B0002A2) /* PIU Page 0 Buffer 1 register (R/W) */ +#define VR4181_PIUPB02REG __preg16(KSEG1 + 0x0B0002A4) /* PIU Page 0 Buffer 2 register (R/W) */ +#define VR4181_PIUPB03REG __preg16(KSEG1 + 0x0B0002A6) /* PIU Page 0 Buffer 3 register (R/W) */ +#define VR4181_PIUPB10REG __preg16(KSEG1 + 0x0B0002A8) /* PIU Page 1 Buffer 0 register (R/W) */ +#define VR4181_PIUPB11REG __preg16(KSEG1 + 0x0B0002AA) /* PIU Page 1 Buffer 1 register (R/W) */ +#define VR4181_PIUPB12REG __preg16(KSEG1 + 0x0B0002AC) /* PIU Page 1 Buffer 2 register (R/W) */ +#define VR4181_PIUPB13REG __preg16(KSEG1 + 0x0B0002AE) /* PIU Page 1 Buffer 3 register (R/W) */ +#define VR4181_PIUAB0REG __preg16(KSEG1 + 0x0B0002B0) /* PIU A/D scan Buffer 0 register (R/W) */ +#define VR4181_PIUAB1REG __preg16(KSEG1 + 0x0B0002B2) /* PIU A/D scan Buffer 1 register (R/W) */ +#define VR4181_PIUAB2REG __preg16(KSEG1 + 0x0B0002B4) /* PIU A/D scan Buffer 2 register (R/W) */ +#define VR4181_PIUAB3REG __preg16(KSEG1 + 0x0B0002B6) /* PIU A/D scan Buffer 3 register (R/W) */ +#define VR4181_PIUPB04REG __preg16(KSEG1 + 0x0B0002BC) /* PIU Page 0 Buffer 4 register (R/W) */ +#define VR4181_PIUPB14REG __preg16(KSEG1 + 0x0B0002BE) /* PIU Page 1 Buffer 4 register (R/W) */ + +// Audio Interface Unit (AIU) +#define VR4181_SODATREG __preg16(KSEG1 + 0x0B000166) /* Speaker Output Data Register (R/W) */ +#define VR4181_SCNTREG __preg16(KSEG1 + 0x0B000168) /* Speaker Output Control Register (R/W) */ +#define VR4181_MIDATREG __preg16(KSEG1 + 0x0B000170) /* Mike Input Data Register (R/W) */ +#define VR4181_MCNTREG __preg16(KSEG1 + 0x0B000172) /* Mike Input Control Register (R/W) */ +#define VR4181_DVALIDREG __preg16(KSEG1 + 0x0B000178) /* Data Valid Register (R/W) */ +#define VR4181_SEQREG __preg16(KSEG1 + 0x0B00017A) /* Sequential Register (R/W) */ +#define VR4181_INTREG __preg16(KSEG1 + 0x0B00017C) /* Interrupt Register (R/W) */ +#define VR4181_SDMADATREG __preg16(KSEG1 + 0x0B000160) /* Speaker DMA Data Register (R/W) */ +#define VR4181_MDMADATREG __preg16(KSEG1 + 0x0B000162) /* Microphone DMA Data Register (R/W) */ +#define VR4181_DAVREF_SETUP __preg16(KSEG1 + 0x0B000164) /* DAC Vref setup register (R/W) */ +#define VR4181_SCNVC_END __preg16(KSEG1 + 0x0B00016E) /* Speaker sample rate control (R/W) */ +#define VR4181_MIDATREG __preg16(KSEG1 + 0x0B000170) /* Microphone Input Data Register (R/W) */ +#define VR4181_MCNTREG __preg16(KSEG1 + 0x0B000172) /* Microphone Input Control Register (R/W) */ +#define VR4181_MCNVC_END __preg16(KSEG1 + 0x0B00017E) /* Microphone sample rate control (R/W) */ + +// Keyboard Interface Unit (KIU) +#define VR4181_KIUDAT0 __preg16(KSEG1 + 0x0B000180) /* KIU Data0 Register (R/W) */ +#define VR4181_KIUDAT1 __preg16(KSEG1 + 0x0B000182) /* KIU Data1 Register (R/W) */ +#define VR4181_KIUDAT2 __preg16(KSEG1 + 0x0B000184) /* KIU Data2 Register (R/W) */ +#define VR4181_KIUDAT3 __preg16(KSEG1 + 0x0B000186) /* KIU Data3 Register (R/W) */ +#define VR4181_KIUDAT4 __preg16(KSEG1 + 0x0B000188) /* KIU Data4 Register (R/W) */ +#define VR4181_KIUDAT5 __preg16(KSEG1 + 0x0B00018A) /* KIU Data5 Register (R/W) */ +#define VR4181_KIUSCANREP __preg16(KSEG1 + 0x0B000190) /* KIU Scan/Repeat Register (R/W) */ +#define VR4181_KIUSCANREP_KEYEN 0x8000 +#define VR4181_KIUSCANREP_SCANSTP 0x0008 +#define VR4181_KIUSCANREP_SCANSTART 0x0004 +#define VR4181_KIUSCANREP_ATSTP 0x0002 +#define VR4181_KIUSCANREP_ATSCAN 0x0001 +#define VR4181_KIUSCANS __preg16(KSEG1 + 0x0B000192) /* KIU Scan Status Register (R) */ +#define VR4181_KIUWKS __preg16(KSEG1 + 0x0B000194) /* KIU Wait Keyscan Stable Register (R/W) */ +#define VR4181_KIUWKI __preg16(KSEG1 + 0x0B000196) /* KIU Wait Keyscan Interval Register (R/W) */ +#define VR4181_KIUINT __preg16(KSEG1 + 0x0B000198) /* KIU Interrupt Register (R/W) */ +#define VR4181_KIUINT_KDATLOST 0x0004 +#define VR4181_KIUINT_KDATRDY 0x0002 +#define VR4181_KIUINT_SCANINT 0x0001 +#define VR4181_KIUDAT6 __preg16(KSEG1 + 0x0B00018C) /* Scan Line 6 Key Data Register (R) */ +#define VR4181_KIUDAT7 __preg16(KSEG1 + 0x0B00018E) /* Scan Line 7 Key Data Register (R) */ + +// CompactFlash Controller +#define VR4181_PCCARDINDEX __preg8(KSEG1 + 0x0B0008E0) /* PC Card Controller Index Register */ +#define VR4181_PCCARDDATA __preg8(KSEG1 + 0x0B0008E1) /* PC Card Controller Data Register */ +#define VR4181_INTSTATREG __preg16(KSEG1 + 0x0B0008F8) /* Interrupt Status Register (R/W) */ +#define VR4181_INTMSKREG __preg16(KSEG1 + 0x0B0008FA) /* Interrupt Mask Register (R/W) */ +#define VR4181_CFG_REG_1 __preg16(KSEG1 + 0x0B0008FE) /* Configuration Register 1 */ + +// LED Control Unit (LED) +#define VR4181_LEDHTSREG __preg16(KSEG1 + 0x0B000240) /* LED H Time Set register (R/W) */ +#define VR4181_LEDLTSREG __preg16(KSEG1 + 0x0B000242) /* LED L Time Set register (R/W) */ +#define VR4181_LEDCNTREG __preg16(KSEG1 + 0x0B000248) /* LED Control register (R/W) */ +#define VR4181_LEDASTCREG __preg16(KSEG1 + 0x0B00024A) /* LED Auto Stop Time Count register (R/W) */ +#define VR4181_LEDINTREG __preg16(KSEG1 + 0x0B00024C) /* LED Interrupt register (R/W) */ + +// Serial Interface Unit (SIU / SIU1 and SIU2) +#define VR4181_SIURB __preg8(KSEG1 + 0x0C000010) /* Receiver Buffer Register (Read) DLAB = 0 (R) */ +#define VR4181_SIUTH __preg8(KSEG1 + 0x0C000010) /* Transmitter Holding Register (Write) DLAB = 0 (W) */ +#define VR4181_SIUDLL __preg8(KSEG1 + 0x0C000010) /* Divisor Latch (Least Significant Byte) DLAB = 1 (R/W) */ +#define VR4181_SIUIE __preg8(KSEG1 + 0x0C000011) /* Interrupt Enable DLAB = 0 (R/W) */ +#define VR4181_SIUDLM __preg8(KSEG1 + 0x0C000011) /* Divisor Latch (Most Significant Byte) DLAB = 1 (R/W) */ +#define VR4181_SIUIID __preg8(KSEG1 + 0x0C000012) /* Interrupt Identification Register (Read) (R) */ +#define VR4181_SIUFC __preg8(KSEG1 + 0x0C000012) /* FIFO Control Register (Write) (W) */ +#define VR4181_SIULC __preg8(KSEG1 + 0x0C000013) /* Line Control Register (R/W) */ +#define VR4181_SIUMC __preg8(KSEG1 + 0x0C000014) /* MODEM Control Register (R/W) */ +#define VR4181_SIULS __preg8(KSEG1 + 0x0C000015) /* Line Status Register (R/W) */ +#define VR4181_SIUMS __preg8(KSEG1 + 0x0C000016) /* MODEM Status Register (R/W) */ +#define VR4181_SIUSC __preg8(KSEG1 + 0x0C000017) /* Scratch Register (R/W) */ +#define VR4181_SIURESET __preg8(KSEG1 + 0x0C000019) /* SIU Reset Register (R/W) */ +#define VR4181_SIUACTMSK __preg8(KSEG1 + 0x0C00001C) /* SIU Activity Mask (R/W) */ +#define VR4181_SIUACTTMR __preg8(KSEG1 + 0x0C00001E) /* SIU Activity Timer (R/W) */ +#define VR4181_SIURB_2 __preg8(KSEG1 + 0x0C000000) /* Receive Buffer Register (Read) (R) */ +#define VR4181_SIUTH_2 __preg8(KSEG1 + 0x0C000000) /* Transmitter Holding Register (Write) (W) */ +#define VR4181_SIUDLL_2 __preg8(KSEG1 + 0x0C000000) /* Divisor Latch (Least Significant Byte) (R/W) */ +#define VR4181_SIUIE_2 __preg8(KSEG1 + 0x0C000001) /* Interrupt Enable (DLAB = 0) (R/W) */ +#define VR4181_SIUDLM_2 __preg8(KSEG1 + 0x0C000001) /* Divisor Latch (Most Significant Byte) (DLAB = 1) (R/W) */ +#define VR4181_SIUIID_2 __preg8(KSEG1 + 0x0C000002) /* Interrupt Identification Register (Read) (R) */ +#define VR4181_SIUFC_2 __preg8(KSEG1 + 0x0C000002) /* FIFO Control Register (Write) (W) */ +#define VR4181_SIULC_2 __preg8(KSEG1 + 0x0C000003) /* Line Control Register (R/W) */ +#define VR4181_SIUMC_2 __preg8(KSEG1 + 0x0C000004) /* Modem Control Register (R/W) */ +#define VR4181_SIULS_2 __preg8(KSEG1 + 0x0C000005) /* Line Status Register (R/W) */ +#define VR4181_SIUMS_2 __preg8(KSEG1 + 0x0C000006) /* Modem Status Register (R/W) */ +#define VR4181_SIUSC_2 __preg8(KSEG1 + 0x0C000007) /* Scratch Register (R/W) */ +#define VR4181_SIUIRSEL_2 __preg8(KSEG1 + 0x0C000008) /* SIU IrDA Selectot (R/W) */ +#define VR4181_SIURESET_2 __preg8(KSEG1 + 0x0C000009) /* SIU Reset Register (R/W) */ +#define VR4181_SIUCSEL_2 __preg8(KSEG1 + 0x0C00000A) /* IrDA Echo-back Control (R/W) */ +#define VR4181_SIUACTMSK_2 __preg8(KSEG1 + 0x0C00000C) /* SIU Activity Mask Register (R/W) */ +#define VR4181_SIUACTTMR_2 __preg8(KSEG1 + 0x0C00000E) /* SIU Activity Timer Register (R/W) */ + + +// USB Module +#define VR4181_USBINFIFO __preg16(KSEG1 + 0x0B000780) /* USB Bulk Input FIFO (Bulk In End Point) (W) */ +#define VR4181_USBOUTFIFO __preg16(KSEG1 + 0x0B000782) /* USB Bulk Output FIFO (Bulk Out End Point) (R) */ +#define VR4181_USBCTLFIFO __preg16(KSEG1 + 0x0B000784) /* USB Control FIFO (Control End Point) (W) */ +#define VR4181_USBSTAT __preg16(KSEG1 + 0x0B000786) /* Interrupt Status Register (R/W) */ +#define VR4181_USBINTMSK __preg16(KSEG1 + 0x0B000788) /* Interrupt Mask Register (R/W) */ +#define VR4181_USBCTLREG __preg16(KSEG1 + 0x0B00078A) /* Control Register (R/W) */ +#define VR4181_USBSTPREG __preg16(KSEG1 + 0x0B00078C) /* USB Transfer Stop Register (R/W) */ + +// LCD Controller +#define VR4181_HRTOTALREG __preg16(KSEG1 + 0x0A000400) /* Horizontal total Register (R/W) */ +#define VR4181_HRVISIBREG __preg16(KSEG1 + 0x0A000402) /* Horizontal Visible Register (R/W) */ +#define VR4181_LDCLKSTREG __preg16(KSEG1 + 0x0A000404) /* Load clock start Register (R/W) */ +#define VR4181_LDCLKNDREG __preg16(KSEG1 + 0x0A000406) /* Load clock end Register (R/W) */ +#define VR4181_VRTOTALREG __preg16(KSEG1 + 0x0A000408) /* Vertical Total Register (R/W) */ +#define VR4181_VRVISIBREG __preg16(KSEG1 + 0x0A00040A) /* Vertical Visible Register (R/W) */ +#define VR4181_FVSTARTREG __preg16(KSEG1 + 0x0A00040C) /* FLM vertical start Register (R/W) */ +#define VR4181_FVENDREG __preg16(KSEG1 + 0x0A00040E) /* FLM vertical end Register (R/W) */ +#define VR4181_LCDCTRLREG __preg16(KSEG1 + 0x0A000410) /* LCD control Register (R/W) */ +#define VR4181_LCDINRQREG __preg16(KSEG1 + 0x0A000412) /* LCD Interrupt request Register (R/W) */ +#define VR4181_LCDCFGREG0 __preg16(KSEG1 + 0x0A000414) /* LCD Configuration Register 0 (R/W) */ +#define VR4181_LCDCFGREG1 __preg16(KSEG1 + 0x0A000416) /* LCD Configuration Register 1 (R/W) */ +#define VR4181_FBSTAD1REG __preg16(KSEG1 + 0x0A000418) /* Frame Buffer Start Address 1 Register (R/W) */ +#define VR4181_FBSTAD2REG __preg16(KSEG1 + 0x0A00041A) /* Frame Buffer Start Address 2 Register (R/W) */ +#define VR4181_FBNDAD1REG __preg16(KSEG1 + 0x0A000420) /* Frame Buffer End Address 1 Register (R/W) */ +#define VR4181_FBNDAD2REG __preg16(KSEG1 + 0x0A000422) /* Frame Buffer End Address 2 register (R/W) */ +#define VR4181_FHSTARTREG __preg16(KSEG1 + 0x0A000424) /* FLM horizontal Start Register (R/W) */ +#define VR4181_FHENDREG __preg16(KSEG1 + 0x0A000426) /* FLM horizontal End Register (R/W) */ +#define VR4181_PWRCONREG1 __preg16(KSEG1 + 0x0A000430) /* Power Control register 1 (R/W) */ +#define VR4181_PWRCONREG2 __preg16(KSEG1 + 0x0A000432) /* Power Control register 2 (R/W) */ +#define VR4181_LCDIMSKREG __preg16(KSEG1 + 0x0A000434) /* LCD Interrupt Mask register (R/W) */ +#define VR4181_CPINDCTREG __preg16(KSEG1 + 0x0A00047E) /* Color palette Index and control Register (R/W) */ +#define VR4181_CPALDATREG __preg32(KSEG1 + 0x0A000480) /* Color palette data register (32bits Register) (R/W) */ + +// physical address spaces +#define VR4181_LCD 0x0a000000 +#define VR4181_INTERNAL_IO_2 0x0b000000 +#define VR4181_INTERNAL_IO_1 0x0c000000 +#define VR4181_ISA_MEM 0x10000000 +#define VR4181_ISA_IO 0x14000000 +#define VR4181_ROM 0x18000000 + +// This is the base address for IO port decoding to which the 16 bit IO port address +// is added. Defining it to 0 will usually cause a kernel oops any time port IO is +// attempted, which can be handy for turning up parts of the kernel that make +// incorrect architecture assumptions (by assuming that everything acts like a PC), +// but we need it correctly defined to use the PCMCIA/CF controller: +#define VR4181_PORT_BASE (KSEG1 + VR4181_ISA_IO) +#define VR4181_ISAMEM_BASE (KSEG1 + VR4181_ISA_MEM) + +#endif /* __ASM_MIPS_VR4181_H */ diff -Nru linux/include/asm-mips/mipsregs.h.orig linux/include/asm-mips/mipsregs.h --- linux/include/asm-mips/mipsregs.h.orig Tue Jun 19 14:30:34 2001 +++ linux/include/asm-mips/mipsregs.h Tue Jun 26 11:40:26 2001 @@ -139,6 +139,14 @@ /* * Values for PageMask register */ +#include +#ifdef CONFIG_CPU_VR41XX +#define PM_1K 0x00000000 +#define PM_4K 0x00001800 +#define PM_16K 0x00007800 +#define PM_64K 0x0001f800 +#define PM_256K 0x0007f800 +#else #define PM_4K 0x00000000 #define PM_16K 0x00006000 #define PM_64K 0x0001e000 @@ -146,6 +154,7 @@ #define PM_1M 0x001fe000 #define PM_4M 0x007fe000 #define PM_16M 0x01ffe000 +#endif /* * Values used for computation of new tlb entries diff -Nru linux/include/asm-mips/pgtable.h.orig linux/include/asm-mips/pgtable.h --- linux/include/asm-mips/pgtable.h.orig Tue Jun 19 14:30:34 2001 +++ linux/include/asm-mips/pgtable.h Tue Jun 26 11:40:26 2001 @@ -334,7 +334,11 @@ * is simple. */ #define page_address(page) ((page)->virtual) +#ifdef CONFIG_CPU_VR41XX +#define pte_page(x) (mem_map+(unsigned long)((pte_val(x) >> (PAGE_SHIFT + 2)))) +#else #define pte_page(x) (mem_map+(unsigned long)((pte_val(x) >> PAGE_SHIFT))) +#endif /* * The following only work if pte_present() is true. @@ -405,6 +409,17 @@ * Conversion functions: convert a page and protection to a page entry, * and a page entry and page directory to the page they refer to. */ +#ifdef CONFIG_CPU_VR41XX +#define mk_pte(page, pgprot) \ +({ \ + pte_t __pte; \ + \ + pte_val(__pte) = ((unsigned long)(page - mem_map) << (PAGE_SHIFT + 2)) | \ + pgprot_val(pgprot); \ + \ + __pte; \ +}) +#else #define mk_pte(page, pgprot) \ ({ \ pte_t __pte; \ @@ -414,10 +429,15 @@ \ __pte; \ }) +#endif extern inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) { +#ifdef CONFIG_CPU_VR41XX + return __pte((physpage << 2) | pgprot_val(pgprot)); +#else return __pte(physpage | pgprot_val(pgprot)); +#endif } extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) diff -Nru linux/net/ipv4/ipconfig.c.orig linux/net/ipv4/ipconfig.c --- linux/net/ipv4/ipconfig.c.orig Tue Jun 19 10:39:51 2001 +++ linux/net/ipv4/ipconfig.c Tue Jun 26 12:53:55 2001 @@ -100,7 +100,7 @@ */ int ic_set_manually __initdata = 0; /* IPconfig parameters set manually */ -int ic_enable __initdata = 0; /* IP config enabled? */ +int ic_enable __initdata = 1; /* IP config enabled? */ /* Protocol choice */ int ic_proto_enabled __initdata = 0