/*
* linux/arch/arm/mach-versatile/core.c
*
* Copyright (C) 1999 - 2003 ARM Limited
* Copyright (C) 2000 Deep Blue Solutions Ltd
*
* 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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/sysdev.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/hardware/amba.h>
#include <asm/hardware/amba_clcd.h>
#include <asm/hardware/icst307.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
#include <asm/mach/irq.h>
#include <asm/mach/time.h>
#include <asm/mach/map.h>
#include <asm/mach/mmc.h>
#include "core.h"
#include "clock.h"
/*
* All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx
* is the (PA >> 12).
*
* Setup a VA for the Versatile Vectored Interrupt Controller.
*/
#define VA_VIC_BASE IO_ADDRESS(VERSATILE_VIC_BASE)
#define VA_SIC_BASE IO_ADDRESS(VERSATILE_SIC_BASE)
static void vic_mask_irq(unsigned int irq)
{
irq -= IRQ_VIC_START;
writel(1 << irq, VA_VIC_BASE + VIC_IRQ_ENABLE_CLEAR);
}
static void vic_unmask_irq(unsigned int irq)
{
irq -= IRQ_VIC_START;
writel(1 << irq, VA_VIC_BASE + VIC_IRQ_ENABLE);
}
static struct irqchip vic_chip = {
.ack = vic_mask_irq,
.mask = vic_mask_irq,
.unmask = vic_unmask_irq,
};
static void sic_mask_irq(unsigned int irq)
{
irq -= IRQ_SIC_START;
writel(1 << irq, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
}
static void sic_unmask_irq(unsigned int irq)
{
irq -= IRQ_SIC_START;
writel(1 << irq, VA_SIC_BASE + SIC_IRQ_ENABLE_SET);
}
static struct irqchip sic_chip = {
.ack = sic_mask_irq,
.mask = sic_mask_irq,
.unmask = sic_unmask_irq,
};
static void
sic_handle_irq(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
{
unsigned long status = readl(VA_SIC_BASE + SIC_IRQ_STATUS);
if (status == 0) {
do_bad_IRQ(irq, desc, regs);
return;
}
do {
irq = ffs(status) - 1;
status &= ~(1 << irq);
irq += IRQ_SIC_START;
desc = irq_desc + irq;
desc->handle(irq, desc, regs);
} while (status);
}
#if 1
#define IRQ_MMCI0A IRQ_VICSOURCE22
#define IRQ_AACI IRQ_VICSOURCE24
#define IRQ_ETH IRQ_VICSOURCE25
#define PIC_MASK 0xFFD00000
#else
#define IRQ_MMCI0A IRQ_SIC_MMCI0A
#define IRQ_AACI IRQ_SIC_AACI
#define IRQ_ETH IRQ_SIC_ETH
#define PIC_MASK 0
#endif
void __init versatile_init_irq(void)
{
unsigned int i, value;
/* Disable all interrupts initially. */
writel(0, VA_VIC_BASE + VIC_INT_SELECT);
writel(0, VA_VIC_BASE + VIC_IRQ_ENABLE);
writel(~0, VA_VIC_BASE + VIC_IRQ_ENABLE_CLEAR);
writel(0, VA_VIC_BASE + VIC_IRQ_STATUS);
writel(0, VA_VIC_BASE + VIC_ITCR);
writel(~0, VA_VIC_BASE + VIC_IRQ_SOFT_CLEAR);
/*
* Make sure we clear all existing interrupts
*/
writel(0, VA_VIC_BASE + VIC_VECT_ADDR);
for (i = 0; i < 19; i++) {
value = readl(VA_VIC_BASE + VIC_VECT_ADDR);
writel(value, VA_VIC_BASE + VIC_VECT_ADDR);
}
for (i = 0; i < 16; i++) {
value = readl(VA_VIC_BASE + VIC_VECT_CNTL0 + (i * 4));
writel(value | VICVectCntl_Enable | i, VA_VIC_BASE + VIC_VECT_CNTL0 + (i * 4));
}
writel(32, VA_VIC_BASE + VIC_DEF_VECT_ADDR);
for (i = IRQ_VIC_START; i <= IRQ_VIC_END; i++) {
if (i != IRQ_VICSOURCE31) {
set_irq_chip(i, &vic_chip);
set_irq_handler(i, do_level_IRQ);
set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
}
}
set_irq_handler(IRQ_VICSOURCE31, sic_handle_irq);
vic_unmask_irq(IRQ_VICSOURCE31);
/* Do second interrupt controller */
writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
for (i = IRQ_SIC_START; i <= IRQ_SIC_END; i++) {
if ((PIC_MASK & (1 << (i - IRQ_SIC_START))) == 0) {
set_irq_chip(i, &sic_chip);
set_irq_handler(i, do_level_IRQ);
set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
}
}
/*
* Interrupts on secondary controller from 0