irq_domain/microblaze: Convert microblaze to use irq_domains
This patch converts Microblaze to use the irq_domain remapper and get away from hard coding the offset between hwirq number and the linux irq number space. This also paves the way for multiple interrupt controllers. v2: Don't enable SPARSE_IRQ and keep NR_IRQS set to 33 Signed-off-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Michal Simek <monstr@monstr.eu> Cc: Rob Herring <rob.herring@calxeda.com> Cc: John Williams <john.williams@petalogix.com> Cc: John Linn <john.linn@xilinx.com>
This commit is contained in:
parent
ff8c3ab816
commit
2462bacd03
|
@ -14,6 +14,7 @@ config MICROBLAZE
|
||||||
select TRACING_SUPPORT
|
select TRACING_SUPPORT
|
||||||
select OF
|
select OF
|
||||||
select OF_EARLY_FLATTREE
|
select OF_EARLY_FLATTREE
|
||||||
|
select IRQ_DOMAIN
|
||||||
select HAVE_GENERIC_HARDIRQS
|
select HAVE_GENERIC_HARDIRQS
|
||||||
select GENERIC_IRQ_PROBE
|
select GENERIC_IRQ_PROBE
|
||||||
select GENERIC_IRQ_SHOW
|
select GENERIC_IRQ_SHOW
|
||||||
|
|
|
@ -1,17 +1 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2006 Atmark Techno, Inc.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _ASM_MICROBLAZE_HARDIRQ_H
|
|
||||||
#define _ASM_MICROBLAZE_HARDIRQ_H
|
|
||||||
|
|
||||||
/* should be defined in each interrupt controller driver */
|
|
||||||
extern unsigned int get_irq(struct pt_regs *regs);
|
|
||||||
|
|
||||||
#include <asm-generic/hardirq.h>
|
#include <asm-generic/hardirq.h>
|
||||||
|
|
||||||
#endif /* _ASM_MICROBLAZE_HARDIRQ_H */
|
|
||||||
|
|
|
@ -9,49 +9,13 @@
|
||||||
#ifndef _ASM_MICROBLAZE_IRQ_H
|
#ifndef _ASM_MICROBLAZE_IRQ_H
|
||||||
#define _ASM_MICROBLAZE_IRQ_H
|
#define _ASM_MICROBLAZE_IRQ_H
|
||||||
|
|
||||||
|
#define NR_IRQS (32 + 1)
|
||||||
/*
|
|
||||||
* Linux IRQ# is currently offset by one to map to the hardware
|
|
||||||
* irq number. So hardware IRQ0 maps to Linux irq 1.
|
|
||||||
*/
|
|
||||||
#define NO_IRQ_OFFSET 1
|
|
||||||
#define IRQ_OFFSET NO_IRQ_OFFSET
|
|
||||||
#define NR_IRQS (32 + IRQ_OFFSET)
|
|
||||||
#include <asm-generic/irq.h>
|
#include <asm-generic/irq.h>
|
||||||
|
|
||||||
/* This type is the placeholder for a hardware interrupt number. It has to
|
|
||||||
* be big enough to enclose whatever representation is used by a given
|
|
||||||
* platform.
|
|
||||||
*/
|
|
||||||
typedef unsigned long irq_hw_number_t;
|
|
||||||
|
|
||||||
extern unsigned int nr_irq;
|
|
||||||
|
|
||||||
struct pt_regs;
|
struct pt_regs;
|
||||||
extern void do_IRQ(struct pt_regs *regs);
|
extern void do_IRQ(struct pt_regs *regs);
|
||||||
|
|
||||||
/** FIXME - not implement
|
/* should be defined in each interrupt controller driver */
|
||||||
* irq_dispose_mapping - Unmap an interrupt
|
extern unsigned int get_irq(void);
|
||||||
* @virq: linux virq number of the interrupt to unmap
|
|
||||||
*/
|
|
||||||
static inline void irq_dispose_mapping(unsigned int virq)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct irq_domain;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* irq_create_mapping - Map a hardware interrupt into linux virq space
|
|
||||||
* @host: host owning this hardware interrupt or NULL for default host
|
|
||||||
* @hwirq: hardware irq number in that host space
|
|
||||||
*
|
|
||||||
* Only one mapping per hardware interrupt is permitted. Returns a linux
|
|
||||||
* virq number.
|
|
||||||
* If the sense/trigger is to be specified, set_irq_type() should be called
|
|
||||||
* on the number returned from that call.
|
|
||||||
*/
|
|
||||||
extern unsigned int irq_create_mapping(struct irq_domain *host,
|
|
||||||
irq_hw_number_t hwirq);
|
|
||||||
|
|
||||||
#endif /* _ASM_MICROBLAZE_IRQ_H */
|
#endif /* _ASM_MICROBLAZE_IRQ_H */
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
@ -25,8 +26,6 @@ static unsigned int intc_baseaddr;
|
||||||
#define INTC_BASE intc_baseaddr
|
#define INTC_BASE intc_baseaddr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned int nr_irq;
|
|
||||||
|
|
||||||
/* No one else should require these constants, so define them locally here. */
|
/* No one else should require these constants, so define them locally here. */
|
||||||
#define ISR 0x00 /* Interrupt Status Register */
|
#define ISR 0x00 /* Interrupt Status Register */
|
||||||
#define IPR 0x04 /* Interrupt Pending Register */
|
#define IPR 0x04 /* Interrupt Pending Register */
|
||||||
|
@ -84,24 +83,45 @@ static struct irq_chip intc_dev = {
|
||||||
.irq_mask_ack = intc_mask_ack,
|
.irq_mask_ack = intc_mask_ack,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int get_irq(struct pt_regs *regs)
|
static struct irq_domain *root_domain;
|
||||||
{
|
|
||||||
int irq;
|
|
||||||
|
|
||||||
/*
|
unsigned int get_irq(void)
|
||||||
* NOTE: This function is the one that needs to be improved in
|
{
|
||||||
* order to handle multiple interrupt controllers. It currently
|
unsigned int hwirq, irq = -1;
|
||||||
* is hardcoded to check for interrupts only on the first INTC.
|
|
||||||
*/
|
hwirq = in_be32(INTC_BASE + IVR);
|
||||||
irq = in_be32(INTC_BASE + IVR) + NO_IRQ_OFFSET;
|
if (hwirq != -1U)
|
||||||
pr_debug("get_irq: %d\n", irq);
|
irq = irq_find_mapping(root_domain, hwirq);
|
||||||
|
|
||||||
|
pr_debug("get_irq: hwirq=%d, irq=%d\n", hwirq, irq);
|
||||||
|
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||||
|
{
|
||||||
|
u32 intr_mask = (u32)d->host_data;
|
||||||
|
|
||||||
|
if (intr_mask & (1 << hw)) {
|
||||||
|
irq_set_chip_and_handler_name(irq, &intc_dev,
|
||||||
|
handle_edge_irq, "edge");
|
||||||
|
irq_clear_status_flags(irq, IRQ_LEVEL);
|
||||||
|
} else {
|
||||||
|
irq_set_chip_and_handler_name(irq, &intc_dev,
|
||||||
|
handle_level_irq, "level");
|
||||||
|
irq_set_status_flags(irq, IRQ_LEVEL);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_domain_ops xintc_irq_domain_ops = {
|
||||||
|
.xlate = irq_domain_xlate_onetwocell,
|
||||||
|
.map = xintc_map,
|
||||||
|
};
|
||||||
|
|
||||||
void __init init_IRQ(void)
|
void __init init_IRQ(void)
|
||||||
{
|
{
|
||||||
u32 i, intr_mask;
|
u32 nr_irq, intr_mask;
|
||||||
struct device_node *intc = NULL;
|
struct device_node *intc = NULL;
|
||||||
#ifdef CONFIG_SELFMOD_INTC
|
#ifdef CONFIG_SELFMOD_INTC
|
||||||
unsigned int intc_baseaddr = 0;
|
unsigned int intc_baseaddr = 0;
|
||||||
|
@ -146,16 +166,9 @@ void __init init_IRQ(void)
|
||||||
/* Turn on the Master Enable. */
|
/* Turn on the Master Enable. */
|
||||||
out_be32(intc_baseaddr + MER, MER_HIE | MER_ME);
|
out_be32(intc_baseaddr + MER, MER_HIE | MER_ME);
|
||||||
|
|
||||||
for (i = IRQ_OFFSET; i < (nr_irq + IRQ_OFFSET); ++i) {
|
/* Yeah, okay, casting the intr_mask to a void* is butt-ugly, but I'm
|
||||||
if (intr_mask & (0x00000001 << (i - IRQ_OFFSET))) {
|
* lazy and Michal can clean it up to something nicer when he tests
|
||||||
irq_set_chip_and_handler_name(i, &intc_dev,
|
* and commits this patch. ~~gcl */
|
||||||
handle_edge_irq, "edge");
|
root_domain = irq_domain_add_linear(intc, nr_irq, &xintc_irq_domain_ops,
|
||||||
irq_clear_status_flags(i, IRQ_LEVEL);
|
(void *)intr_mask);
|
||||||
} else {
|
|
||||||
irq_set_chip_and_handler_name(i, &intc_dev,
|
|
||||||
handle_level_irq, "level");
|
|
||||||
irq_set_status_flags(i, IRQ_LEVEL);
|
|
||||||
}
|
|
||||||
irq_get_irq_data(i)->hwirq = i - IRQ_OFFSET;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,13 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
|
||||||
trace_hardirqs_off();
|
trace_hardirqs_off();
|
||||||
|
|
||||||
irq_enter();
|
irq_enter();
|
||||||
irq = get_irq(regs);
|
irq = get_irq();
|
||||||
next_irq:
|
next_irq:
|
||||||
BUG_ON(!irq);
|
BUG_ON(!irq);
|
||||||
/* Substract 1 because of get_irq */
|
generic_handle_irq(irq);
|
||||||
generic_handle_irq(irq + IRQ_OFFSET - NO_IRQ_OFFSET);
|
|
||||||
|
|
||||||
irq = get_irq(regs);
|
irq = get_irq();
|
||||||
if (irq) {
|
if (irq != -1U) {
|
||||||
pr_debug("next irq: %d\n", irq);
|
pr_debug("next irq: %d\n", irq);
|
||||||
++concurrent_irq;
|
++concurrent_irq;
|
||||||
goto next_irq;
|
goto next_irq;
|
||||||
|
@ -48,18 +47,3 @@ next_irq:
|
||||||
set_irq_regs(old_regs);
|
set_irq_regs(old_regs);
|
||||||
trace_hardirqs_on();
|
trace_hardirqs_on();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MS: There is no any advance mapping mechanism. We are using simple 32bit
|
|
||||||
intc without any cascades or any connection that's why mapping is 1:1 */
|
|
||||||
unsigned int irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq)
|
|
||||||
{
|
|
||||||
return hwirq + IRQ_OFFSET;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(irq_create_mapping);
|
|
||||||
|
|
||||||
unsigned int irq_create_of_mapping(struct device_node *controller,
|
|
||||||
const u32 *intspec, unsigned int intsize)
|
|
||||||
{
|
|
||||||
return intspec[0] + IRQ_OFFSET;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
|
|
||||||
|
|
Loading…
Reference in a new issue