diff options
Diffstat (limited to 'arch/mips/vr4181/common')
-rw-r--r-- | arch/mips/vr4181/common/Makefile | 7 | ||||
-rw-r--r-- | arch/mips/vr4181/common/int_handler.S | 206 | ||||
-rw-r--r-- | arch/mips/vr4181/common/irq.c | 239 | ||||
-rw-r--r-- | arch/mips/vr4181/common/serial.c | 51 | ||||
-rw-r--r-- | arch/mips/vr4181/common/time.c | 145 |
5 files changed, 648 insertions, 0 deletions
diff --git a/arch/mips/vr4181/common/Makefile b/arch/mips/vr4181/common/Makefile new file mode 100644 index 00000000000..f7587ca64ea --- /dev/null +++ b/arch/mips/vr4181/common/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for common code of NEC vr4181 based boards +# + +obj-y := irq.o int_handler.o serial.o time.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/vr4181/common/int_handler.S b/arch/mips/vr4181/common/int_handler.S new file mode 100644 index 00000000000..2c041b8ee52 --- /dev/null +++ b/arch/mips/vr4181/common/int_handler.S @@ -0,0 +1,206 @@ +/* + * 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, jsun@mvista.com or jsun@junsun.net + * + * 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) diff --git a/arch/mips/vr4181/common/irq.c b/arch/mips/vr4181/common/irq.c new file mode 100644 index 00000000000..2cdf77c5cb3 --- /dev/null +++ b/arch/mips/vr4181/common/irq.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) + * + * 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, CPU_MASK_NONE, "cascade", NULL, NULL }; +static struct irqaction reserved = + { no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL }; + +void __init arch_init_irq(void) +{ + int i; + + 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); +} diff --git a/arch/mips/vr4181/common/serial.c b/arch/mips/vr4181/common/serial.c new file mode 100644 index 00000000000..3f62c62b107 --- /dev/null +++ b/arch/mips/vr4181/common/serial.c @@ -0,0 +1,51 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 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!"); + } +} + diff --git a/arch/mips/vr4181/common/time.c b/arch/mips/vr4181/common/time.c new file mode 100644 index 00000000000..17814076b6f --- /dev/null +++ b/arch/mips/vr4181/common/time.c @@ -0,0 +1,145 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: jsun@mvista.com or jsun@junsun.net + * + * 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 + */ + +DEFINE_SPINLOCK(rtc_lock); + +/* 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; +} + |