|
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;
}
|