830531b7d4
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30747 a1c6a512-1295-4272-9138-f99709370657
237 lines
7.7 KiB
C
237 lines
7.7 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2007 by James Espinoza
|
|
*
|
|
* 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.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include <stdio.h>
|
|
#include "system.h"
|
|
#include "imx31l.h"
|
|
#include "avic-imx31.h"
|
|
#include "panic.h"
|
|
#include "debug.h"
|
|
|
|
#define avic ((unsigned long * const)AVIC_BASE_ADDR)
|
|
#define INTCNTL (0x000 / sizeof (unsigned long)) /* 000h */
|
|
#define NIMASK (0x004 / sizeof (unsigned long)) /* 004h */
|
|
#define INTENNUM (0x008 / sizeof (unsigned long)) /* 008h */
|
|
#define INTDISNUM (0x00c / sizeof (unsigned long)) /* 00Ch */
|
|
#define INTENABLE (0x010 / sizeof (unsigned long)) /* 010h H,L */
|
|
#define INTTYPE (0x018 / sizeof (unsigned long)) /* 018h H,L */
|
|
#define NIPRIORITY (0x020 / sizeof (unsigned long)) /* 020h 7-0 */
|
|
#define NIVECSR (0x040 / sizeof (unsigned long)) /* 040h */
|
|
#define FIVECSR (0x044 / sizeof (unsigned long)) /* 044h */
|
|
#define INTSRC (0x048 / sizeof (unsigned long)) /* 048h H,L */
|
|
#define INTFRC (0x050 / sizeof (unsigned long)) /* 050h H,L */
|
|
#define NIPND (0x058 / sizeof (unsigned long)) /* 058h H,L */
|
|
#define FIPND (0x060 / sizeof (unsigned long)) /* 060h H,L */
|
|
#define VECTOR (0x100 / sizeof (unsigned long)) /* 100h */
|
|
|
|
static const char * const avic_int_names[64] =
|
|
{
|
|
"RESERVED0", "RESERVED1", "RESERVED2", "I2C3",
|
|
"I2C2", "MPEG4_ENCODER", "RTIC", "FIR",
|
|
"MMC/SDHC2", "MMC/SDHC1", "I2C1", "SSI2",
|
|
"SSI1", "CSPI2", "CSPI1", "ATA",
|
|
"MBX", "CSPI3", "UART3", "IIM",
|
|
"SIM1", "SIM2", "RNGA", "EVTMON",
|
|
"KPP", "RTC", "PWN", "EPIT2",
|
|
"EPIT1", "GPT", "PWR_FAIL", "CCM_DVFS",
|
|
"UART2", "NANDFC", "SDMA", "USB_HOST1",
|
|
"USB_HOST2", "USB_OTG", "RESERVED3", "MSHC1",
|
|
"MSHC2", "IPU_ERR", "IPU", "RESERVED4",
|
|
"RESERVED5", "UART1", "UART4", "UART5",
|
|
"ETC_IRQ", "SCC_SCM", "SCC_SMN", "GPIO2",
|
|
"GPIO1", "CCM_CLK", "PCMCIA", "WDOG",
|
|
"GPIO3", "RESERVED6", "EXT_PWMG", "EXT_TEMP",
|
|
"EXT_SENS1", "EXT_SENS2", "EXT_WDOG", "EXT_TV"
|
|
};
|
|
|
|
void UIE_VECTOR(void)
|
|
{
|
|
unsigned long mode;
|
|
int offset;
|
|
|
|
asm volatile (
|
|
"mrs %0, cpsr \n" /* Mask core IRQ/FIQ */
|
|
"orr %0, %0, #0xc0 \n"
|
|
"msr cpsr_c, %0 \n"
|
|
"and %0, %0, #0x1f \n" /* Get mode bits */
|
|
: "=&r"(mode)
|
|
);
|
|
|
|
offset = mode == 0x11 ? (long)avic[FIVECSR] : ((long)avic[NIVECSR] >> 16);
|
|
|
|
panicf("Unhandled %s %d: %s",
|
|
mode == 0x11 ? "FIQ" : "IRQ", offset,
|
|
offset >= 0 ? avic_int_names[offset] : "<Unknown>");
|
|
}
|
|
|
|
#if 0
|
|
/* We use the AVIC */
|
|
void __attribute__((interrupt("IRQ"))) irq_handler(void)
|
|
{
|
|
int offset = (long)avic[NIVECSR] >> 16;
|
|
|
|
if (offset == -1)
|
|
{
|
|
/* This is called occasionally for some unknown reason even with the
|
|
* avic enabled but returning normally appears to cause no harm. The
|
|
* KPP and ATA seem to have a part in it (common but multiplexed pins
|
|
* that can interfere). It will be investigated more thoroughly but
|
|
* for now it is simply an occasional irritant. */
|
|
return;
|
|
}
|
|
|
|
disable_interrupt(IRQ_FIQ_STATUS);
|
|
panicf("Unhandled IRQ %d in irq_handler: %s", offset,
|
|
offset >= 0 ? avic_int_names[offset] : "<Unknown>");
|
|
}
|
|
#endif /* 0 */
|
|
|
|
#if 0
|
|
/* Accoring to section 9.3.5 of the UM, the AVIC doesn't accelerate
|
|
* fast interrupts and they must be dispatched */
|
|
void __attribute__((naked)) fiq_handler(void)
|
|
{
|
|
asm volatile (
|
|
"mov r10, #0x68000000 \n" /* load AVIC base address */
|
|
"ldr r9, [r10, #0x44] \n" /* read FIVECSR of AVIC */
|
|
"add r10, r10, #0x100 \n" /* move pointer to base of VECTOR table */
|
|
"ldr r8, [r10, r9, lsl #2] \n" /* read FIQ vector from VECTOR table */
|
|
"bx r8 \n" /* jump to FIQ service routine */
|
|
);
|
|
}
|
|
#endif /* 0 */
|
|
|
|
void INIT_ATTR avic_init(void)
|
|
{
|
|
int i;
|
|
|
|
/* Disable all interrupts and set to unhandled */
|
|
avic_disable_int(INT_ALL);
|
|
|
|
/* Reset AVIC control */
|
|
avic[INTCNTL] = 0;
|
|
|
|
/* Init all interrupts to type IRQ */
|
|
avic_set_int_type(INT_ALL, INT_TYPE_IRQ);
|
|
|
|
/* Set all normal to lowest priority */
|
|
for (i = 0; i < 8; i++)
|
|
avic[NIPRIORITY + i] = 0;
|
|
|
|
/* Set NM bit to enable VIC. Mask fast interrupts. Core arbiter rise
|
|
* for normal interrupts (for lowest latency). */
|
|
avic[INTCNTL] |= AVIC_INTCNTL_NM | AVIC_INTCNTL_FIDIS |
|
|
AVIC_INTCNTL_NIAD;
|
|
|
|
/* Enable VE bit in CP15 Control reg to enable VIC */
|
|
asm volatile (
|
|
"mrc p15, 0, r0, c1, c0, 0 \n"
|
|
"orr r0, r0, #(1 << 24) \n"
|
|
"mcr p15, 0, r0, c1, c0, 0 \n"
|
|
: : : "r0");
|
|
|
|
/* Enable normal interrupts at all priorities */
|
|
avic[NIMASK] = AVIC_NIL_ENABLE;
|
|
}
|
|
|
|
void avic_set_int_priority(enum IMX31_INT_LIST ints,
|
|
unsigned long ni_priority)
|
|
{
|
|
volatile unsigned long * const reg = &avic[NIPRIORITY + 7 - (ints >> 3)];
|
|
unsigned int shift = (ints & 0x7) << 2;
|
|
unsigned long mask = 0xful << shift;
|
|
*reg = (*reg & ~mask) | ((ni_priority << shift) & mask);
|
|
}
|
|
|
|
void avic_enable_int(enum IMX31_INT_LIST ints, enum INT_TYPE intstype,
|
|
unsigned long ni_priority, void (*handler)(void))
|
|
{
|
|
int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS);
|
|
|
|
if (ints != INT_ALL) /* No mass-enable allowed */
|
|
{
|
|
avic_set_int_type(ints, intstype);
|
|
avic[VECTOR + ints] = (unsigned long)handler;
|
|
avic[INTENNUM] = ints;
|
|
avic_set_int_priority(ints, ni_priority);
|
|
}
|
|
|
|
restore_interrupt(oldstatus);
|
|
}
|
|
|
|
void avic_disable_int(enum IMX31_INT_LIST ints)
|
|
{
|
|
if (ints == INT_ALL)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
avic[INTDISNUM] = i;
|
|
avic[VECTOR + i] = (unsigned long)UIE_VECTOR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
avic[INTDISNUM] = ints;
|
|
avic[VECTOR + ints] = (unsigned long)UIE_VECTOR;
|
|
}
|
|
}
|
|
|
|
static void set_int_type(int i, enum INT_TYPE intstype)
|
|
{
|
|
/* INTTYPEH: vectors 63-32, INTTYPEL: vectors 31-0 */
|
|
volatile unsigned long * const reg = &avic[INTTYPE + 1 - (i >> 5)];
|
|
unsigned long val = 1L << (i & 0x1f);
|
|
|
|
if (intstype == INT_TYPE_IRQ)
|
|
val = *reg & ~val;
|
|
else
|
|
val = *reg | val;
|
|
|
|
*reg = val;
|
|
}
|
|
|
|
void avic_set_int_type(enum IMX31_INT_LIST ints, enum INT_TYPE intstype)
|
|
{
|
|
int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS);
|
|
|
|
if (ints == INT_ALL)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 64; i++)
|
|
set_int_type(i, intstype);
|
|
}
|
|
else
|
|
{
|
|
set_int_type(ints, intstype);
|
|
}
|
|
|
|
restore_interrupt(oldstatus);
|
|
}
|
|
|
|
void avic_set_ni_level(int level)
|
|
{
|
|
if (level < 0)
|
|
level = 0x1f; /* -1 */
|
|
else if (level > 15)
|
|
level = 15;
|
|
|
|
avic[NIMASK] = level;
|
|
}
|