300 lines
7.1 KiB
C
300 lines
7.1 KiB
C
/*
|
||
* arch/v850/kernel/rte_me2_cb.c -- Midas labs RTE-V850E/ME2-CB board
|
||
*
|
||
* Copyright (C) 2001,02,03 NEC Electronics Corporation
|
||
* Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
|
||
*
|
||
* 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.
|
||
*
|
||
* Written by Miles Bader <miles@gnu.org>
|
||
*/
|
||
|
||
#include <linux/kernel.h>
|
||
#include <linux/init.h>
|
||
#include <linux/bootmem.h>
|
||
#include <linux/irq.h>
|
||
#include <linux/fs.h>
|
||
#include <linux/major.h>
|
||
#include <linux/sched.h>
|
||
#include <linux/delay.h>
|
||
|
||
#include <asm/atomic.h>
|
||
#include <asm/page.h>
|
||
#include <asm/me2.h>
|
||
#include <asm/rte_me2_cb.h>
|
||
#include <asm/machdep.h>
|
||
#include <asm/v850e_intc.h>
|
||
#include <asm/v850e_cache.h>
|
||
#include <asm/irq.h>
|
||
|
||
#include "mach.h"
|
||
|
||
extern unsigned long *_intv_start;
|
||
extern unsigned long *_intv_end;
|
||
|
||
/* LED access routines. */
|
||
extern unsigned read_leds (int pos, char *buf, int len);
|
||
extern unsigned write_leds (int pos, const char *buf, int len);
|
||
|
||
|
||
/* SDRAM are almost contiguous (with a small hole in between;
|
||
see mach_reserve_bootmem for details), so just use both as one big area. */
|
||
#define RAM_START SDRAM_ADDR
|
||
#define RAM_END (SDRAM_ADDR + SDRAM_SIZE)
|
||
|
||
|
||
void __init mach_get_physical_ram (unsigned long *ram_start,
|
||
unsigned long *ram_len)
|
||
{
|
||
*ram_start = RAM_START;
|
||
*ram_len = RAM_END - RAM_START;
|
||
}
|
||
|
||
void mach_gettimeofday (struct timespec *tv)
|
||
{
|
||
tv->tv_sec = 0;
|
||
tv->tv_nsec = 0;
|
||
}
|
||
|
||
/* Called before configuring an on-chip UART. */
|
||
void rte_me2_cb_uart_pre_configure (unsigned chan,
|
||
unsigned cflags, unsigned baud)
|
||
{
|
||
/* The RTE-V850E/ME2-CB connects some general-purpose I/O
|
||
pins on the CPU to the RTS/CTS lines of UARTB channel 0's
|
||
serial connection.
|
||
I/O pins P21 and P22 are RTS and CTS respectively. */
|
||
if (chan == 0) {
|
||
/* Put P21 & P22 in I/O port mode. */
|
||
ME2_PORT2_PMC &= ~0x6;
|
||
/* Make P21 and output, and P22 an input. */
|
||
ME2_PORT2_PM = (ME2_PORT2_PM & ~0xC) | 0x4;
|
||
}
|
||
|
||
me2_uart_pre_configure (chan, cflags, baud);
|
||
}
|
||
|
||
void __init mach_init_irqs (void)
|
||
{
|
||
/* Initialize interrupts. */
|
||
me2_init_irqs ();
|
||
rte_me2_cb_init_irqs ();
|
||
}
|
||
|
||
#ifdef CONFIG_ROM_KERNEL
|
||
/* Initialization for kernel in ROM. */
|
||
static inline rom_kernel_init (void)
|
||
{
|
||
/* If the kernel is in ROM, we have to copy any initialized data
|
||
from ROM into RAM. */
|
||
extern unsigned long _data_load_start, _sdata, _edata;
|
||
register unsigned long *src = &_data_load_start;
|
||
register unsigned long *dst = &_sdata, *end = &_edata;
|
||
|
||
while (dst != end)
|
||
*dst++ = *src++;
|
||
}
|
||
#endif /* CONFIG_ROM_KERNEL */
|
||
|
||
static void install_interrupt_vectors (void)
|
||
{
|
||
unsigned long *p1, *p2;
|
||
|
||
ME2_IRAMM = 0x03; /* V850E/ME2 iRAM write mode */
|
||
|
||
/* vector copy to iRAM */
|
||
p1 = (unsigned long *)0; /* v85x vector start */
|
||
p2 = (unsigned long *)&_intv_start;
|
||
while (p2 < (unsigned long *)&_intv_end)
|
||
*p1++ = *p2++;
|
||
|
||
ME2_IRAMM = 0x00; /* V850E/ME2 iRAM read mode */
|
||
}
|
||
|
||
/* CompactFlash */
|
||
|
||
static void cf_power_on (void)
|
||
{
|
||
/* CF card detected? */
|
||
if (CB_CF_STS0 & 0x0030)
|
||
return;
|
||
|
||
CB_CF_REG0 = 0x0002; /* reest on */
|
||
mdelay (10);
|
||
CB_CF_REG0 = 0x0003; /* power on */
|
||
mdelay (10);
|
||
CB_CF_REG0 = 0x0001; /* reset off */
|
||
mdelay (10);
|
||
}
|
||
|
||
static void cf_power_off (void)
|
||
{
|
||
CB_CF_REG0 = 0x0003; /* power on */
|
||
mdelay (10);
|
||
CB_CF_REG0 = 0x0002; /* reest on */
|
||
mdelay (10);
|
||
}
|
||
|
||
void __init mach_early_init (void)
|
||
{
|
||
install_interrupt_vectors ();
|
||
|
||
/* CS1 SDRAM instruction cache enable */
|
||
v850e_cache_enable (0x04, 0x03, 0);
|
||
|
||
rte_cb_early_init ();
|
||
|
||
/* CompactFlash power on */
|
||
cf_power_on ();
|
||
|
||
#if defined (CONFIG_ROM_KERNEL)
|
||
rom_kernel_init ();
|
||
#endif
|
||
}
|
||
|
||
|
||
/* RTE-V850E/ME2-CB Programmable Interrupt Controller. */
|
||
|
||
static struct cb_pic_irq_init cb_pic_irq_inits[] = {
|
||
{ "CB_EXTTM0", IRQ_CB_EXTTM0, 1, 1, 6 },
|
||
{ "CB_EXTSIO", IRQ_CB_EXTSIO, 1, 1, 6 },
|
||
{ "CB_TOVER", IRQ_CB_TOVER, 1, 1, 6 },
|
||
{ "CB_GINT0", IRQ_CB_GINT0, 1, 1, 6 },
|
||
{ "CB_USB", IRQ_CB_USB, 1, 1, 6 },
|
||
{ "CB_LANC", IRQ_CB_LANC, 1, 1, 6 },
|
||
{ "CB_USB_VBUS_ON", IRQ_CB_USB_VBUS_ON, 1, 1, 6 },
|
||
{ "CB_USB_VBUS_OFF", IRQ_CB_USB_VBUS_OFF, 1, 1, 6 },
|
||
{ "CB_EXTTM1", IRQ_CB_EXTTM1, 1, 1, 6 },
|
||
{ "CB_EXTTM2", IRQ_CB_EXTTM2, 1, 1, 6 },
|
||
{ 0 }
|
||
};
|
||
#define NUM_CB_PIC_IRQ_INITS \
|
||
((sizeof cb_pic_irq_inits / sizeof cb_pic_irq_inits[0]) - 1)
|
||
|
||
static struct hw_interrupt_type cb_pic_hw_itypes[NUM_CB_PIC_IRQ_INITS];
|
||
static unsigned char cb_pic_active_irqs = 0;
|
||
|
||
void __init rte_me2_cb_init_irqs (void)
|
||
{
|
||
cb_pic_init_irq_types (cb_pic_irq_inits, cb_pic_hw_itypes);
|
||
|
||
/* Initalize on board PIC1 (not PIC0) enable */
|
||
CB_PIC_INT0M = 0x0000;
|
||
CB_PIC_INT1M = 0x0000;
|
||
CB_PIC_INTR = 0x0000;
|
||
CB_PIC_INTEN |= CB_PIC_INT1EN;
|
||
|
||
ME2_PORT2_PMC |= 0x08; /* INTP23/SCK1 mode */
|
||
ME2_PORT2_PFC &= ~0x08; /* INTP23 mode */
|
||
ME2_INTR(2) &= ~0x08; /* INTP23 falling-edge detect */
|
||
ME2_INTF(2) &= ~0x08; /* " */
|
||
|
||
rte_cb_init_irqs (); /* gbus &c */
|
||
}
|
||
|
||
|
||
/* Enable interrupt handling for interrupt IRQ. */
|
||
void cb_pic_enable_irq (unsigned irq)
|
||
{
|
||
CB_PIC_INT1M |= 1 << (irq - CB_PIC_BASE_IRQ);
|
||
}
|
||
|
||
void cb_pic_disable_irq (unsigned irq)
|
||
{
|
||
CB_PIC_INT1M &= ~(1 << (irq - CB_PIC_BASE_IRQ));
|
||
}
|
||
|
||
void cb_pic_shutdown_irq (unsigned irq)
|
||
{
|
||
cb_pic_disable_irq (irq);
|
||
|
||
if (--cb_pic_active_irqs == 0)
|
||
free_irq (IRQ_CB_PIC, 0);
|
||
|
||
CB_PIC_INT1M &= ~(1 << (irq - CB_PIC_BASE_IRQ));
|
||
}
|
||
|
||
static irqreturn_t cb_pic_handle_irq (int irq, void *dev_id,
|
||
struct pt_regs *regs)
|
||
{
|
||
irqreturn_t rval = IRQ_NONE;
|
||
unsigned status = CB_PIC_INTR;
|
||
unsigned enable = CB_PIC_INT1M;
|
||
|
||
/* Only pay attention to enabled interrupts. */
|
||
status &= enable;
|
||
|
||
CB_PIC_INTEN &= ~CB_PIC_INT1EN;
|
||
|
||
if (status) {
|
||
unsigned mask = 1;
|
||
|
||
irq = CB_PIC_BASE_IRQ;
|
||
do {
|
||
/* There's an active interrupt, find out which one,
|
||
and call its handler. */
|
||
while (! (status & mask)) {
|
||
irq++;
|
||
mask <<= 1;
|
||
}
|
||
status &= ~mask;
|
||
|
||
CB_PIC_INTR = mask;
|
||
|
||
/* Recursively call handle_irq to handle it. */
|
||
handle_irq (irq, regs);
|
||
rval = IRQ_HANDLED;
|
||
} while (status);
|
||
}
|
||
|
||
CB_PIC_INTEN |= CB_PIC_INT1EN;
|
||
|
||
return rval;
|
||
}
|
||
|
||
|
||
static void irq_nop (unsigned irq) { }
|
||
|
||
static unsigned cb_pic_startup_irq (unsigned irq)
|
||
{
|
||
int rval;
|
||
|
||
if (cb_pic_active_irqs == 0) {
|
||
rval = request_irq (IRQ_CB_PIC, cb_pic_handle_irq,
|
||
IRQF_DISABLED, "cb_pic_handler", 0);
|
||
if (rval != 0)
|
||
return rval;
|
||
}
|
||
|
||
cb_pic_active_irqs++;
|
||
|
||
cb_pic_enable_irq (irq);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array
|
||
INITS (which is terminated by an entry with the name field == 0). */
|
||
void __init cb_pic_init_irq_types (struct cb_pic_irq_init *inits,
|
||
struct hw_interrupt_type *hw_irq_types)
|
||
{
|
||
struct cb_pic_irq_init *init;
|
||
for (init = inits; init->name; init++) {
|
||
struct hw_interrupt_type *hwit = hw_irq_types++;
|
||
|
||
hwit->typename = init->name;
|
||
|
||
hwit->startup = cb_pic_startup_irq;
|
||
hwit->shutdown = cb_pic_shutdown_irq;
|
||
hwit->enable = cb_pic_enable_irq;
|
||
hwit->disable = cb_pic_disable_irq;
|
||
hwit->ack = irq_nop;
|
||
hwit->end = irq_nop;
|
||
|
||
/* Initialize kernel IRQ infrastructure for this interrupt. */
|
||
init_irq_handlers(init->base, init->num, init->interval, hwit);
|
||
}
|
||
}
|