168 строки
4.2 KiB
C
168 строки
4.2 KiB
C
/*
|
||
* arch/v850/kernel/fpga85e2c.h -- Machine-dependent defs for
|
||
* FPGA implementation of V850E2/NA85E2C
|
||
*
|
||
* Copyright (C) 2002,03 NEC Electronics Corporation
|
||
* Copyright (C) 2002,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/module.h>
|
||
#include <linux/init.h>
|
||
#include <linux/mm.h>
|
||
#include <linux/swap.h>
|
||
#include <linux/bootmem.h>
|
||
#include <linux/irq.h>
|
||
#include <linux/bitops.h>
|
||
|
||
#include <asm/atomic.h>
|
||
#include <asm/page.h>
|
||
#include <asm/machdep.h>
|
||
|
||
#include "mach.h"
|
||
|
||
extern void memcons_setup (void);
|
||
|
||
|
||
#define REG_DUMP_ADDR 0x220000
|
||
|
||
|
||
extern struct irqaction reg_snap_action; /* fwd decl */
|
||
|
||
|
||
void __init mach_early_init (void)
|
||
{
|
||
int i;
|
||
const u32 *src;
|
||
register u32 *dst asm ("ep");
|
||
extern u32 _intv_end, _intv_load_start;
|
||
|
||
/* Set bus sizes: CS0 32-bit, CS1 16-bit, CS7 8-bit,
|
||
everything else 32-bit. */
|
||
V850E2_BSC = 0x2AA6;
|
||
for (i = 2; i <= 6; i++)
|
||
CSDEV(i) = 0; /* 32 bit */
|
||
|
||
/* Ensure that the simulator halts on a panic, instead of going
|
||
into an infinite loop inside the panic function. */
|
||
panic_timeout = -1;
|
||
|
||
/* Move the interrupt vectors into their real location. Note that
|
||
any relocations there are relative to the real location, so we
|
||
don't have to fix anything up. We use a loop instead of calling
|
||
memcpy to keep this a leaf function (to avoid a function
|
||
prologue being generated). */
|
||
dst = 0x10; /* &_intv_start + 0x10. */
|
||
src = &_intv_load_start;
|
||
do {
|
||
u32 t0 = src[0], t1 = src[1], t2 = src[2], t3 = src[3];
|
||
u32 t4 = src[4], t5 = src[5], t6 = src[6], t7 = src[7];
|
||
dst[0] = t0; dst[1] = t1; dst[2] = t2; dst[3] = t3;
|
||
dst[4] = t4; dst[5] = t5; dst[6] = t6; dst[7] = t7;
|
||
dst += 8;
|
||
src += 8;
|
||
} while (dst < &_intv_end);
|
||
}
|
||
|
||
void __init mach_setup (char **cmdline)
|
||
{
|
||
memcons_setup ();
|
||
|
||
/* Setup up NMI0 to copy the registers to a known memory location.
|
||
The FGPA board has a button that produces NMI0 when pressed, so
|
||
this allows us to push the button, and then look at memory to see
|
||
what's in the registers (there's no other way to easily do so).
|
||
We have to use `setup_irq' instead of `request_irq' because it's
|
||
still too early to do memory allocation. */
|
||
setup_irq (IRQ_NMI (0), ®_snap_action);
|
||
}
|
||
|
||
void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len)
|
||
{
|
||
*ram_start = ERAM_ADDR;
|
||
*ram_len = ERAM_SIZE;
|
||
}
|
||
|
||
void __init mach_sched_init (struct irqaction *timer_action)
|
||
{
|
||
/* Setup up the timer interrupt. The FPGA peripheral control
|
||
registers _only_ work with single-bit writes (set1/clr1)! */
|
||
__clear_bit (RPU_GTMC_CE_BIT, &RPU_GTMC);
|
||
__clear_bit (RPU_GTMC_CLK_BIT, &RPU_GTMC);
|
||
__set_bit (RPU_GTMC_CE_BIT, &RPU_GTMC);
|
||
|
||
/* We use the first RPU interrupt, which occurs every 8.192ms. */
|
||
setup_irq (IRQ_RPU (0), timer_action);
|
||
}
|
||
|
||
|
||
void mach_gettimeofday (struct timespec *tv)
|
||
{
|
||
tv->tv_sec = 0;
|
||
tv->tv_nsec = 0;
|
||
}
|
||
|
||
void machine_halt (void) __attribute__ ((noreturn));
|
||
void machine_halt (void)
|
||
{
|
||
for (;;) {
|
||
DWC(0) = 0x7777;
|
||
DWC(1) = 0x7777;
|
||
ASC = 0xffff;
|
||
FLGREG(0) = 1; /* Halt immediately. */
|
||
asm ("di; halt; nop; nop; nop; nop; nop");
|
||
}
|
||
}
|
||
|
||
void machine_restart (char *__unused)
|
||
{
|
||
machine_halt ();
|
||
}
|
||
|
||
void machine_power_off (void)
|
||
{
|
||
machine_halt ();
|
||
}
|
||
|
||
|
||
/* Interrupts */
|
||
|
||
struct v850e_intc_irq_init irq_inits[] = {
|
||
{ "IRQ", 0, NUM_MACH_IRQS, 1, 7 },
|
||
{ "RPU", IRQ_RPU(0), IRQ_RPU_NUM, 1, 6 },
|
||
{ 0 }
|
||
};
|
||
#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1)
|
||
|
||
struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS];
|
||
|
||
/* Initialize interrupts. */
|
||
void __init mach_init_irqs (void)
|
||
{
|
||
v850e_intc_init_irq_types (irq_inits, hw_itypes);
|
||
}
|
||
|
||
|
||
/* An interrupt handler that copies the registers to a known memory location,
|
||
for debugging purposes. */
|
||
|
||
static void make_reg_snap (int irq, void *dummy, struct pt_regs *regs)
|
||
{
|
||
(*(unsigned *)REG_DUMP_ADDR)++;
|
||
(*(struct pt_regs *)(REG_DUMP_ADDR + sizeof (unsigned))) = *regs;
|
||
}
|
||
|
||
static int reg_snap_dev_id;
|
||
static struct irqaction reg_snap_action = {
|
||
.handler = make_reg_snap,
|
||
.mask = CPU_MASK_NONE,
|
||
.name = "reg_snap",
|
||
.dev_id = ®_snap_dev_id,
|
||
};
|