f0b5d12f2b
- allow people to select the feature that is unavailable to the kernel: NMI, JTAG, or CYCLES. - change default NMI handler to simply dump hardware trace buffer. - remove default NMI handler completely as calling into kernel code is not safe move example handler to wiki so people dont haphazardly copy and paste this stuff thinking its safe Signed-off-by: Mike Frysinger <michael.frysinger@analog.com> Signed-off-by: Bryan Wu <bryan.wu@analog.com>
485 lines
12 KiB
C
485 lines
12 KiB
C
/*
|
|
* File: arch/blackfin/mach-common/ints-priority-dc.c
|
|
* Based on:
|
|
* Author:
|
|
*
|
|
* Created: ?
|
|
* Description: Set up the interrupt priorities
|
|
*
|
|
* Modified:
|
|
* 1996 Roman Zippel
|
|
* 1999 D. Jeff Dionne <jeff@uclinux.org>
|
|
* 2000-2001 Lineo, Inc. D. Jefff Dionne <jeff@lineo.ca>
|
|
* 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca>
|
|
* 2003 Metrowerks/Motorola
|
|
* 2003 Bas Vermeulen <bas@buyways.nl>
|
|
* Copyright 2004-2006 Analog Devices Inc.
|
|
*
|
|
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
|
*
|
|
* 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, see the file COPYING, or write
|
|
* to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/irq.h>
|
|
#ifdef CONFIG_KGDB
|
|
#include <linux/kgdb.h>
|
|
#endif
|
|
#include <asm/traps.h>
|
|
#include <asm/blackfin.h>
|
|
#include <asm/gpio.h>
|
|
#include <asm/irq_handler.h>
|
|
|
|
/*
|
|
* NOTES:
|
|
* - we have separated the physical Hardware interrupt from the
|
|
* levels that the LINUX kernel sees (see the description in irq.h)
|
|
* -
|
|
*/
|
|
|
|
unsigned long irq_flags = 0;
|
|
|
|
/* The number of spurious interrupts */
|
|
atomic_t num_spurious;
|
|
|
|
struct ivgx {
|
|
/* irq number for request_irq, available in mach-bf561/irq.h */
|
|
int irqno;
|
|
/* corresponding bit in the SICA_ISR0 register */
|
|
int isrflag0;
|
|
/* corresponding bit in the SICA_ISR1 register */
|
|
int isrflag1;
|
|
} ivg_table[NR_PERI_INTS];
|
|
|
|
struct ivg_slice {
|
|
/* position of first irq in ivg_table for given ivg */
|
|
struct ivgx *ifirst;
|
|
struct ivgx *istop;
|
|
} ivg7_13[IVG13 - IVG7 + 1];
|
|
|
|
static void search_IAR(void);
|
|
|
|
/*
|
|
* Search SIC_IAR and fill tables with the irqvalues
|
|
* and their positions in the SIC_ISR register.
|
|
*/
|
|
static void __init search_IAR(void)
|
|
{
|
|
unsigned ivg, irq_pos = 0;
|
|
for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) {
|
|
int irqn;
|
|
|
|
ivg7_13[ivg].istop = ivg7_13[ivg].ifirst = &ivg_table[irq_pos];
|
|
|
|
for (irqn = 0; irqn < NR_PERI_INTS; irqn++) {
|
|
int iar_shift = (irqn & 7) * 4;
|
|
if (ivg ==
|
|
(0xf &
|
|
bfin_read32((unsigned long *)SICA_IAR0 +
|
|
(irqn >> 3)) >> iar_shift)) {
|
|
ivg_table[irq_pos].irqno = IVG7 + irqn;
|
|
ivg_table[irq_pos].isrflag0 =
|
|
(irqn < 32 ? (1 << irqn) : 0);
|
|
ivg_table[irq_pos].isrflag1 =
|
|
(irqn < 32 ? 0 : (1 << (irqn - 32)));
|
|
ivg7_13[ivg].istop++;
|
|
irq_pos++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is for BF561 internal IRQs
|
|
*/
|
|
|
|
static void ack_noop(unsigned int irq)
|
|
{
|
|
/* Dummy function. */
|
|
}
|
|
|
|
static void bf561_core_mask_irq(unsigned int irq)
|
|
{
|
|
irq_flags &= ~(1 << irq);
|
|
if (!irqs_disabled())
|
|
local_irq_enable();
|
|
}
|
|
|
|
static void bf561_core_unmask_irq(unsigned int irq)
|
|
{
|
|
irq_flags |= 1 << irq;
|
|
/*
|
|
* If interrupts are enabled, IMASK must contain the same value
|
|
* as irq_flags. Make sure that invariant holds. If interrupts
|
|
* are currently disabled we need not do anything; one of the
|
|
* callers will take care of setting IMASK to the proper value
|
|
* when reenabling interrupts.
|
|
* local_irq_enable just does "STI irq_flags", so it's exactly
|
|
* what we need.
|
|
*/
|
|
if (!irqs_disabled())
|
|
local_irq_enable();
|
|
return;
|
|
}
|
|
|
|
static void bf561_internal_mask_irq(unsigned int irq)
|
|
{
|
|
unsigned long irq_mask;
|
|
if ((irq - (IRQ_CORETMR + 1)) < 32) {
|
|
irq_mask = (1 << (irq - (IRQ_CORETMR + 1)));
|
|
bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() & ~irq_mask);
|
|
} else {
|
|
irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32));
|
|
bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() & ~irq_mask);
|
|
}
|
|
}
|
|
|
|
static void bf561_internal_unmask_irq(unsigned int irq)
|
|
{
|
|
unsigned long irq_mask;
|
|
|
|
if ((irq - (IRQ_CORETMR + 1)) < 32) {
|
|
irq_mask = (1 << (irq - (IRQ_CORETMR + 1)));
|
|
bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() | irq_mask);
|
|
} else {
|
|
irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32));
|
|
bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() | irq_mask);
|
|
}
|
|
SSYNC();
|
|
}
|
|
|
|
static struct irq_chip bf561_core_irqchip = {
|
|
.ack = ack_noop,
|
|
.mask = bf561_core_mask_irq,
|
|
.unmask = bf561_core_unmask_irq,
|
|
};
|
|
|
|
static struct irq_chip bf561_internal_irqchip = {
|
|
.ack = ack_noop,
|
|
.mask = bf561_internal_mask_irq,
|
|
.unmask = bf561_internal_unmask_irq,
|
|
};
|
|
|
|
#ifdef CONFIG_IRQCHIP_DEMUX_GPIO
|
|
static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)];
|
|
static unsigned short gpio_edge_triggered[gpio_bank(MAX_BLACKFIN_GPIOS)];
|
|
|
|
static void bf561_gpio_ack_irq(unsigned int irq)
|
|
{
|
|
u16 gpionr = irq - IRQ_PF0;
|
|
|
|
if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) {
|
|
set_gpio_data(gpionr, 0);
|
|
SSYNC();
|
|
}
|
|
}
|
|
|
|
static void bf561_gpio_mask_ack_irq(unsigned int irq)
|
|
{
|
|
u16 gpionr = irq - IRQ_PF0;
|
|
|
|
if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) {
|
|
set_gpio_data(gpionr, 0);
|
|
SSYNC();
|
|
}
|
|
|
|
set_gpio_maska(gpionr, 0);
|
|
SSYNC();
|
|
}
|
|
|
|
static void bf561_gpio_mask_irq(unsigned int irq)
|
|
{
|
|
set_gpio_maska(irq - IRQ_PF0, 0);
|
|
SSYNC();
|
|
}
|
|
|
|
static void bf561_gpio_unmask_irq(unsigned int irq)
|
|
{
|
|
set_gpio_maska(irq - IRQ_PF0, 1);
|
|
SSYNC();
|
|
}
|
|
|
|
static unsigned int bf561_gpio_irq_startup(unsigned int irq)
|
|
{
|
|
unsigned int ret;
|
|
u16 gpionr = irq - IRQ_PF0;
|
|
|
|
if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) {
|
|
|
|
ret = gpio_request(gpionr, "IRQ");
|
|
if (ret)
|
|
return ret;
|
|
|
|
}
|
|
|
|
gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr);
|
|
bf561_gpio_unmask_irq(irq);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static void bf561_gpio_irq_shutdown(unsigned int irq)
|
|
{
|
|
bf561_gpio_mask_irq(irq);
|
|
gpio_free(irq - IRQ_PF0);
|
|
gpio_enabled[gpio_bank(irq - IRQ_PF0)] &= ~gpio_bit(irq - IRQ_PF0);
|
|
}
|
|
|
|
static int bf561_gpio_irq_type(unsigned int irq, unsigned int type)
|
|
{
|
|
|
|
unsigned int ret;
|
|
u16 gpionr = irq - IRQ_PF0;
|
|
|
|
|
|
if (type == IRQ_TYPE_PROBE) {
|
|
/* only probe unenabled GPIO interrupt lines */
|
|
if (gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))
|
|
return 0;
|
|
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
|
|
|
|
}
|
|
|
|
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
|
|
IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
|
|
|
|
if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) {
|
|
|
|
ret = gpio_request(gpionr, "IRQ");
|
|
if (ret)
|
|
return ret;
|
|
|
|
}
|
|
|
|
gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr);
|
|
} else {
|
|
gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
set_gpio_dir(gpionr, 0);
|
|
set_gpio_inen(gpionr, 1);
|
|
|
|
|
|
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) {
|
|
gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr);
|
|
set_gpio_edge(gpionr, 1);
|
|
} else {
|
|
set_gpio_edge(gpionr, 0);
|
|
gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr);
|
|
}
|
|
|
|
if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
|
|
== (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
|
|
set_gpio_both(gpionr, 1);
|
|
else
|
|
set_gpio_both(gpionr, 0);
|
|
|
|
if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)))
|
|
set_gpio_polar(gpionr, 1); /* low or falling edge denoted by one */
|
|
else
|
|
set_gpio_polar(gpionr, 0); /* high or rising edge denoted by zero */
|
|
|
|
SSYNC();
|
|
|
|
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
|
|
set_irq_handler(irq, handle_edge_irq);
|
|
else
|
|
set_irq_handler(irq, handle_level_irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct irq_chip bf561_gpio_irqchip = {
|
|
.ack = bf561_gpio_ack_irq,
|
|
.mask = bf561_gpio_mask_irq,
|
|
.mask_ack = bf561_gpio_mask_ack_irq,
|
|
.unmask = bf561_gpio_unmask_irq,
|
|
.set_type = bf561_gpio_irq_type,
|
|
.startup = bf561_gpio_irq_startup,
|
|
.shutdown = bf561_gpio_irq_shutdown
|
|
};
|
|
|
|
static void bf561_demux_gpio_irq(unsigned int inta_irq,
|
|
struct irq_desc *intb_desc)
|
|
{
|
|
int irq, flag_d, mask;
|
|
u16 gpio;
|
|
|
|
switch (inta_irq) {
|
|
case IRQ_PROG0_INTA:
|
|
irq = IRQ_PF0;
|
|
break;
|
|
case IRQ_PROG1_INTA:
|
|
irq = IRQ_PF16;
|
|
break;
|
|
case IRQ_PROG2_INTA:
|
|
irq = IRQ_PF32;
|
|
break;
|
|
default:
|
|
dump_stack();
|
|
return;
|
|
}
|
|
|
|
gpio = irq - IRQ_PF0;
|
|
|
|
flag_d = get_gpiop_data(gpio);
|
|
mask = flag_d & (gpio_enabled[gpio_bank(gpio)] &
|
|
get_gpiop_maska(gpio));
|
|
|
|
do {
|
|
if (mask & 1) {
|
|
struct irq_desc *desc = irq_desc + irq;
|
|
desc->handle_irq(irq, desc);
|
|
}
|
|
irq++;
|
|
mask >>= 1;
|
|
} while (mask);
|
|
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_IRQCHIP_DEMUX_GPIO */
|
|
|
|
void __init init_exception_vectors(void)
|
|
{
|
|
SSYNC();
|
|
|
|
/* cannot program in software:
|
|
* evt0 - emulation (jtag)
|
|
* evt1 - reset
|
|
*/
|
|
bfin_write_EVT2(evt_nmi);
|
|
bfin_write_EVT3(trap);
|
|
bfin_write_EVT5(evt_ivhw);
|
|
bfin_write_EVT6(evt_timer);
|
|
bfin_write_EVT7(evt_evt7);
|
|
bfin_write_EVT8(evt_evt8);
|
|
bfin_write_EVT9(evt_evt9);
|
|
bfin_write_EVT10(evt_evt10);
|
|
bfin_write_EVT11(evt_evt11);
|
|
bfin_write_EVT12(evt_evt12);
|
|
bfin_write_EVT13(evt_evt13);
|
|
bfin_write_EVT14(evt14_softirq);
|
|
bfin_write_EVT15(evt_system_call);
|
|
CSYNC();
|
|
}
|
|
|
|
/*
|
|
* This function should be called during kernel startup to initialize
|
|
* the BFin IRQ handling routines.
|
|
*/
|
|
int __init init_arch_irq(void)
|
|
{
|
|
int irq;
|
|
unsigned long ilat = 0;
|
|
/* Disable all the peripheral intrs - page 4-29 HW Ref manual */
|
|
bfin_write_SICA_IMASK0(SIC_UNMASK_ALL);
|
|
bfin_write_SICA_IMASK1(SIC_UNMASK_ALL);
|
|
SSYNC();
|
|
|
|
bfin_write_SICA_IWR0(IWR_ENABLE_ALL);
|
|
bfin_write_SICA_IWR1(IWR_ENABLE_ALL);
|
|
|
|
local_irq_disable();
|
|
|
|
init_exception_buff();
|
|
|
|
for (irq = 0; irq <= SYS_IRQS; irq++) {
|
|
if (irq <= IRQ_CORETMR)
|
|
set_irq_chip(irq, &bf561_core_irqchip);
|
|
else
|
|
set_irq_chip(irq, &bf561_internal_irqchip);
|
|
#ifdef CONFIG_IRQCHIP_DEMUX_GPIO
|
|
if ((irq != IRQ_PROG0_INTA) &&
|
|
(irq != IRQ_PROG1_INTA) && (irq != IRQ_PROG2_INTA)) {
|
|
#endif
|
|
set_irq_handler(irq, handle_simple_irq);
|
|
#ifdef CONFIG_IRQCHIP_DEMUX_GPIO
|
|
} else {
|
|
set_irq_chained_handler(irq, bf561_demux_gpio_irq);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef CONFIG_IRQCHIP_DEMUX_GPIO
|
|
for (irq = IRQ_PF0; irq <= IRQ_PF47; irq++) {
|
|
set_irq_chip(irq, &bf561_gpio_irqchip);
|
|
/* if configured as edge, then will be changed to do_edge_IRQ */
|
|
set_irq_handler(irq, handle_level_irq);
|
|
}
|
|
#endif
|
|
bfin_write_IMASK(0);
|
|
CSYNC();
|
|
ilat = bfin_read_ILAT();
|
|
CSYNC();
|
|
bfin_write_ILAT(ilat);
|
|
CSYNC();
|
|
|
|
printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n");
|
|
/* IMASK=xxx is equivalent to STI xx or irq_flags=xx,
|
|
* local_irq_enable()
|
|
*/
|
|
program_IAR();
|
|
/* Therefore it's better to setup IARs before interrupts enabled */
|
|
search_IAR();
|
|
|
|
/* Enable interrupts IVG7-15 */
|
|
irq_flags = irq_flags | IMASK_IVG15 |
|
|
IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 |
|
|
IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_DO_IRQ_L1
|
|
void do_irq(int vec, struct pt_regs *fp)__attribute__((l1_text));
|
|
#endif
|
|
|
|
void do_irq(int vec, struct pt_regs *fp)
|
|
{
|
|
if (vec == EVT_IVTMR_P) {
|
|
vec = IRQ_CORETMR;
|
|
} else {
|
|
struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst;
|
|
struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop;
|
|
unsigned long sic_status0, sic_status1;
|
|
|
|
SSYNC();
|
|
sic_status0 = bfin_read_SICA_IMASK0() & bfin_read_SICA_ISR0();
|
|
sic_status1 = bfin_read_SICA_IMASK1() & bfin_read_SICA_ISR1();
|
|
|
|
for (;; ivg++) {
|
|
if (ivg >= ivg_stop) {
|
|
atomic_inc(&num_spurious);
|
|
return;
|
|
} else if ((sic_status0 & ivg->isrflag0) ||
|
|
(sic_status1 & ivg->isrflag1))
|
|
break;
|
|
}
|
|
vec = ivg->irqno;
|
|
}
|
|
asm_do_IRQ(vec, fp);
|
|
|
|
#ifdef CONFIG_KGDB
|
|
kgdb_process_breakpoint();
|
|
#endif
|
|
}
|