From: Paul M. <le...@us...> - 2002-06-14 13:55:02
|
Update of /cvsroot/linux-mips/linux/arch/mips/vr41xx/vr4181/common In directory usw-pr-cvs1:/tmp/cvs-serv28233 Added Files: Makefile int_handler.S irq.c serial.c time.c Log Message: Re-add Vr4181 common. --- NEW FILE: Makefile --- # # 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 int_handler.o serial.o time.o include $(TOPDIR)/Rules.make --- NEW FILE: int_handler.S --- /* * arch/mips/vr4181/common/int_handler.S * * Adapted to the VR4181 and almost entirely rewritten: * Copyright (C) 1999 Bradley D. LaRonde and Michael Klar * * Clean up to conform to the new IRQ * Copyright (C) 2001 MontaVista Software Inc. * Author: Jun Sun, js...@mv... or js...@ju... * * 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 <asm/asm.h> #include <asm/regdef.h> #include <asm/mipsregs.h> #include <asm/stackframe.h> #include <asm/vr4181/vr4181.h> /* * [jsun] * See include/asm/vr4181/irq.h for IRQ assignment and strategy. */ .text .set noreorder .align 5 NESTED(vr4181_handle_irq, PT_SIZE, ra) .set noat SAVE_ALL CLI .set at .set noreorder mfc0 t0, CP0_CAUSE mfc0 t2, CP0_STATUS and t0, t2 /* we check IP3 first; it happens most frequently */ andi t1, t0, STATUSF_IP3 bnez t1, ll_cpu_ip3 andi t1, t0, STATUSF_IP2 bnez t1, ll_cpu_ip2 andi t1, t0, STATUSF_IP7 /* cpu timer */ bnez t1, ll_cputimer_irq andi t1, t0, STATUSF_IP4 bnez t1, ll_cpu_ip4 andi t1, t0, STATUSF_IP5 bnez t1, ll_cpu_ip5 andi t1, t0, STATUSF_IP6 bnez t1, ll_cpu_ip6 andi t1, t0, STATUSF_IP0 /* software int 0 */ bnez t1, ll_cpu_ip0 andi t1, t0, STATUSF_IP1 /* software int 1 */ bnez t1, ll_cpu_ip1 nop .set reorder do_spurious: j spurious_interrupt /* * regular CPU irqs */ ll_cputimer_irq: li a0, VR4181_IRQ_TIMER move a1, sp jal do_IRQ j ret_from_irq ll_cpu_ip0: li a0, VR4181_IRQ_SW1 move a1, sp jal do_IRQ j ret_from_irq ll_cpu_ip1: li a0, VR4181_IRQ_SW2 move a1, sp jal do_IRQ j ret_from_irq ll_cpu_ip3: li a0, VR4181_IRQ_INT1 move a1, sp jal do_IRQ j ret_from_irq ll_cpu_ip4: li a0, VR4181_IRQ_INT2 move a1, sp jal do_IRQ j ret_from_irq ll_cpu_ip5: li a0, VR4181_IRQ_INT3 move a1, sp jal do_IRQ j ret_from_irq ll_cpu_ip6: li a0, VR4181_IRQ_INT4 move a1, sp jal do_IRQ j ret_from_irq /* * One of the sys irq has happend. * * In the interest of speed, we first determine in the following order * which 16-irq block have pending interrupts: * sysint1 (16 sources, including cascading intrs from GPIO) * sysint2 * gpio (16 intr sources) * * Then we do binary search to find the exact interrupt source. */ ll_cpu_ip2: lui t3,%hi(VR4181_SYSINT1REG) lhu t0,%lo(VR4181_SYSINT1REG)(t3) lhu t2,%lo(VR4181_MSYSINT1REG)(t3) and t0, 0xfffb /* hack - remove RTC Long 1 intr */ and t0, t2 beqz t0, check_sysint2 /* check for GPIO interrupts */ andi t1, t0, 0x0100 bnez t1, check_gpio_int /* so we have an interrupt in sysint1 which is not gpio int */ li a0, VR4181_SYS_IRQ_BASE - 1 j check_16 check_sysint2: lhu t0,%lo(VR4181_SYSINT2REG)(t3) lhu t2,%lo(VR4181_MSYSINT2REG)(t3) and t0, 0xfffe /* hack - remove RTC Long 2 intr */ and t0, t2 li a0, VR4181_SYS_IRQ_BASE + 16 - 1 j check_16 check_gpio_int: lui t3,%hi(VR4181_GPINTMSK) lhu t0,%lo(VR4181_GPINTMSK)(t3) lhu t2,%lo(VR4181_GPINTSTAT)(t3) xori t0, 0xffff /* why? reverse logic? */ and t0, t2 li a0, VR4181_GPIO_IRQ_BASE - 1 j check_16 /* * When we reach check_16, we have 16-bit status in t0 and base irq number * in a0. */ check_16: andi t1, t0, 0xff bnez t1, check_8 srl t0, 8 addi a0, 8 j check_8 /* * When we reach check_8, we have 8-bit status in t0 and base irq number * in a0. */ check_8: andi t1, t0, 0xf bnez t1, check_4 srl t0, 4 addi a0, 4 j check_4 /* * When we reach check_4, we have 4-bit status in t0 and base irq number * in a0. */ check_4: andi t0, t0, 0xf beqz t0, do_spurious loop: andi t2, t0, 0x1 srl t0, 1 addi a0, 1 beqz t2, loop found_it: move a1, sp jal do_IRQ j ret_from_irq END(vr4181_handle_irq) --- NEW FILE: irq.c --- /* * Copyright (C) 2001 MontaVista Software Inc. * Author: Jun Sun, js...@mv... or js...@ju... * * linux/arch/mips/vr4181/common/irq.c * Completely re-written to use the new irq.c * * Credits to Bradley D. LaRonde and Michael Klar for writing the original * irq.c file which was derived from the common irq.c file. * * 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 <linux/types.h> #include <linux/init.h> #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/random.h> #include <asm/irq.h> #include <asm/mipsregs.h> #include <asm/gdb-stub.h> #include <asm/vr4181/vr4181.h> /* * Strategy: * * We essentially have three irq controllers, CPU, system, and gpio. * * CPU irq controller is taken care by arch/mips/kernel/irq_cpu.c and * CONFIG_IRQ_CPU config option. * * We here provide sys_irq and gpio_irq controller code. */ static int sys_irq_base; static int gpio_irq_base; /* ---------------------- sys irq ------------------------ */ static void sys_irq_enable(unsigned int irq) { irq -= sys_irq_base; if (irq < 16) { *VR4181_MSYSINT1REG |= (u16)(1 << irq); } else { irq -= 16; *VR4181_MSYSINT2REG |= (u16)(1 << irq); } } static void sys_irq_disable(unsigned int irq) { irq -= sys_irq_base; if (irq < 16) { *VR4181_MSYSINT1REG &= ~((u16)(1 << irq)); } else { irq -= 16; *VR4181_MSYSINT2REG &= ~((u16)(1 << irq)); } } static unsigned int sys_irq_startup(unsigned int irq) { sys_irq_enable(irq); return 0; } #define sys_irq_shutdown sys_irq_disable #define sys_irq_ack sys_irq_disable static void sys_irq_end(unsigned int irq) { if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) sys_irq_enable(irq); } static hw_irq_controller sys_irq_controller = { "vr4181_sys_irq", sys_irq_startup, sys_irq_shutdown, sys_irq_enable, sys_irq_disable, sys_irq_ack, sys_irq_end, NULL /* no affinity stuff for UP */ }; /* ---------------------- gpio irq ------------------------ */ /* gpio irq lines use reverse logic */ static void gpio_irq_enable(unsigned int irq) { irq -= gpio_irq_base; *VR4181_GPINTMSK &= ~((u16)(1 << irq)); } static void gpio_irq_disable(unsigned int irq) { irq -= gpio_irq_base; *VR4181_GPINTMSK |= (u16)(1 << irq); } static unsigned int gpio_irq_startup(unsigned int irq) { gpio_irq_enable(irq); irq -= gpio_irq_base; *VR4181_GPINTEN |= (u16)(1 << irq ); return 0; } static void gpio_irq_shutdown(unsigned int irq) { gpio_irq_disable(irq); irq -= gpio_irq_base; *VR4181_GPINTEN &= ~((u16)(1 << irq )); } static void gpio_irq_ack(unsigned int irq) { u16 irqtype; u16 irqshift; gpio_irq_disable(irq); /* we clear interrupt if it is edge triggered */ irq -= gpio_irq_base; if (irq < 8) { irqtype = *VR4181_GPINTTYPL; irqshift = 2 << (irq*2); } else { irqtype = *VR4181_GPINTTYPH; irqshift = 2 << ((irq-8)*2); } if ( ! (irqtype & irqshift) ) { *VR4181_GPINTSTAT = (u16) (1 << irq); } } static void gpio_irq_end(unsigned int irq) { if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) gpio_irq_enable(irq); } static hw_irq_controller gpio_irq_controller = { "vr4181_gpio_irq", gpio_irq_startup, gpio_irq_shutdown, gpio_irq_enable, gpio_irq_disable, gpio_irq_ack, gpio_irq_end, NULL /* no affinity stuff for UP */ }; /* --------------------- IRQ init stuff ---------------------- */ extern asmlinkage void vr4181_handle_irq(void); extern void breakpoint(void); extern int setup_irq(unsigned int irq, struct irqaction *irqaction); extern void mips_cpu_irq_init(u32 irq_base); static struct irqaction cascade = { no_action, SA_INTERRUPT, 0, "cascade", NULL, NULL }; static struct irqaction reserved = { no_action, SA_INTERRUPT, 0, "cascade", NULL, NULL }; void __init init_IRQ(void) { int i; extern irq_desc_t irq_desc[]; set_except_vector(0, vr4181_handle_irq); /* init CPU irqs */ mips_cpu_irq_init(VR4181_CPU_IRQ_BASE); /* init sys irqs */ sys_irq_base = VR4181_SYS_IRQ_BASE; for (i=sys_irq_base; i < sys_irq_base + VR4181_NUM_SYS_IRQ; i++) { irq_desc[i].status = IRQ_DISABLED; irq_desc[i].action = NULL; irq_desc[i].depth = 1; irq_desc[i].handler = &sys_irq_controller; } /* init gpio irqs */ gpio_irq_base = VR4181_GPIO_IRQ_BASE; for (i=gpio_irq_base; i < gpio_irq_base + VR4181_NUM_GPIO_IRQ; i++) { irq_desc[i].status = IRQ_DISABLED; irq_desc[i].action = NULL; irq_desc[i].depth = 1; irq_desc[i].handler = &gpio_irq_controller; } /* Default all ICU IRQs to off ... */ *VR4181_MSYSINT1REG = 0; *VR4181_MSYSINT2REG = 0; /* We initialize the level 2 ICU registers to all bits disabled. */ *VR4181_MPIUINTREG = 0; *VR4181_MAIUINTREG = 0; *VR4181_MKIUINTREG = 0; /* disable all GPIO intrs */ *VR4181_GPINTMSK = 0xffff; /* vector handler. What these do is register the IRQ as non-sharable */ setup_irq(VR4181_IRQ_INT0, &cascade); setup_irq(VR4181_IRQ_GIU, &cascade); /* * RTC interrupts are interesting. They have two destinations. * One is at sys irq controller, and the other is at CPU IP3 and IP4. * RTC timer is used as system timer. * We enable them here, but timer routine will register later * with CPU IP3/IP4. */ setup_irq(VR4181_IRQ_RTCL1, &reserved); setup_irq(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 } --- NEW FILE: serial.c --- /* * Copyright 2001 MontaVista Software Inc. * Author: Jun Sun, js...@mv... or js...@ju... * * arch/mips/vr4181/common/serial.c * initialize serial port on vr4181. * * 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. * */ /* * [jsun, 010925] * You need to make sure rs_table has at least one element in * drivers/char/serial.c file. There is no good way to do it right * now. A workaround is to include CONFIG_SERIAL_MANY_PORTS in your * configure file, which would gives you 64 ports and wastes 11K ram. */ #include <linux/types.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/serial.h> #include <asm/vr4181/vr4181.h> void __init vr4181_init_serial(void) { struct serial_struct s; /* turn on UART clock */ *VR4181_CMUCLKMSK |= VR4181_CMUCLKMSK_MSKSIU; /* clear memory */ memset(&s, 0, sizeof(s)); s.line = 0; /* we set the first one */ s.baud_base = 1152000; s.irq = VR4181_IRQ_SIU; s.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; /* STD_COM_FLAGS */ s.iomem_base = (u8*)VR4181_SIURB; s.iomem_reg_shift = 0; s.io_type = SERIAL_IO_MEM; if (early_serial_setup(&s) != 0) { panic("vr4181_init_serial() failed!"); } } --- NEW FILE: time.c --- /* * Copyright 2001 MontaVista Software Inc. * Author: js...@mv... or js...@ju... * * rtc and time ops for vr4181. Part of code is drived from * linux-vr, originally written by Bradley D. LaRonde & Michael Klar. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #include <linux/kernel.h> #include <linux/spinlock.h> #include <linux/param.h> /* for HZ */ #include <linux/time.h> #include <linux/interrupt.h> #include <asm/system.h> #include <asm/time.h> #include <asm/vr4181/vr4181.h> #define COUNTS_PER_JIFFY ((32768 + HZ/2) / HZ) /* * RTC ops */ spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; /* 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; } static unsigned long vr4181_rtc_get_time(void) { 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(VR4181_ETIMEMREG); barrier(); regh = read_time_reg(VR4181_ETIMEHREG); barrier(); regl = read_time_reg(VR4181_ETIMELREG); } while (regm != read_time_reg(VR4181_ETIMEMREG)); return ((regh << 17) | (regm << 1) | (regl >> 15)); } static int vr4181_rtc_set_time(unsigned long timeval) { unsigned short intreg; unsigned long flags; spin_lock_irqsave(&rtc_lock, flags); intreg = *VR4181_RTCINTREG & 0x05; barrier(); *VR4181_ETIMELREG = timeval << 15; *VR4181_ETIMEMREG = timeval >> 1; *VR4181_ETIMEHREG = 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); return 0; } /* * timer interrupt routine (wrapper) * * we need our own interrupt routine because we need to clear * RTC1 interrupt. */ static void vr4181_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { /* Clear the interrupt. */ *VR4181_RTCINTREG = 0x2; /* call the generic one */ timer_interrupt(irq, dev_id, regs); } /* * vr4181_time_init: * * We pick the following choices: * . we use elapsed timer as the RTC. We set some reasonable init data since * it does not persist across reset * . we use RTC1 as the system timer interrupt source. * . we use CPU counter for fast_gettimeoffset and we calivrate the cpu * frequency. In other words, we use calibrate_div64_gettimeoffset(). * . we use our own timer interrupt routine which clears the interrupt * and then calls the generic high-level timer interrupt routine. * */ extern int setup_irq(unsigned int irq, struct irqaction *irqaction); static void vr4181_timer_setup(struct irqaction *irq) { /* over-write the handler to be our own one */ irq->handler = vr4181_timer_interrupt; /* sets up the frequency */ *VR4181_RTCL1LREG = COUNTS_PER_JIFFY; *VR4181_RTCL1HREG = 0; /* and ack any pending ints */ *VR4181_RTCINTREG = 0x2; /* setup irqaction */ setup_irq(VR4181_IRQ_INT1, irq); } void vr4181_init_time(void) { /* setup hookup functions */ rtc_get_time = vr4181_rtc_get_time; rtc_set_time = vr4181_rtc_set_time; board_timer_setup = vr4181_timer_setup; } |