Commit 4f808391 authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'irqchip-core-3.17-2' of git://git.infradead.org/users/jcooper/linux into irq/core

irqchip core changes form Jason Cooper
 * or1k-pic: Migrate driver from arch/openrisc
 * crossbar: Cleanup series
parents e8d471ed 885d078b
...@@ -10,6 +10,7 @@ Required properties: ...@@ -10,6 +10,7 @@ Required properties:
- compatible : Should be "ti,irq-crossbar" - compatible : Should be "ti,irq-crossbar"
- reg: Base address and the size of the crossbar registers. - reg: Base address and the size of the crossbar registers.
- ti,max-irqs: Total number of irqs available at the interrupt controller. - ti,max-irqs: Total number of irqs available at the interrupt controller.
- ti,max-crossbar-sources: Maximum number of crossbar sources that can be routed.
- ti,reg-size: Size of a individual register in bytes. Every individual - ti,reg-size: Size of a individual register in bytes. Every individual
register is assumed to be of same size. Valid sizes are 1, 2, 4. register is assumed to be of same size. Valid sizes are 1, 2, 4.
- ti,irqs-reserved: List of the reserved irq lines that are not muxed using - ti,irqs-reserved: List of the reserved irq lines that are not muxed using
...@@ -17,11 +18,46 @@ Required properties: ...@@ -17,11 +18,46 @@ Required properties:
so crossbar bar driver should not consider them as free so crossbar bar driver should not consider them as free
lines. lines.
Optional properties:
- ti,irqs-skip: This is similar to "ti,irqs-reserved", but these are for
SOC-specific hard-wiring of those irqs which unexpectedly bypasses the
crossbar. These irqs have a crossbar register, but still cannot be used.
- ti,irqs-safe-map: integer which maps to a safe configuration to use
when the interrupt controller irq is unused (when not provided, default is 0)
Examples: Examples:
crossbar_mpu: @4a020000 { crossbar_mpu: @4a020000 {
compatible = "ti,irq-crossbar"; compatible = "ti,irq-crossbar";
reg = <0x4a002a48 0x130>; reg = <0x4a002a48 0x130>;
ti,max-irqs = <160>; ti,max-irqs = <160>;
ti,max-crossbar-sources = <400>;
ti,reg-size = <2>; ti,reg-size = <2>;
ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>;
ti,irqs-skip = <10 133 139 140>;
}; };
Consumer:
========
See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt and
Documentation/devicetree/bindings/arm/gic.txt for further details.
An interrupt consumer on an SoC using crossbar will use:
interrupts = <GIC_SPI request_number interrupt_level>
When the request number is between 0 to that described by
"ti,max-crossbar-sources", it is assumed to be a crossbar mapping. If the
request_number is greater than "ti,max-crossbar-sources", then it is mapped as a
quirky hardware mapping direct to GIC.
Example:
device_x@0x4a023000 {
/* Crossbar 8 used */
interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
...
};
device_y@0x4a033000 {
/* Direct mapped GIC SPI 1 used */
interrupts = <GIC_SPI DIRECT_IRQ(1) IRQ_TYPE_LEVEL_HIGH>;
...
};
OpenRISC 1000 Programmable Interrupt Controller
Required properties:
- compatible : should be "opencores,or1k-pic-level" for variants with
level triggered interrupt lines, "opencores,or1k-pic-edge" for variants with
edge triggered interrupt lines or "opencores,or1200-pic" for machines
with the non-spec compliant or1200 type implementation.
"opencores,or1k-pic" is also provided as an alias to "opencores,or1200-pic",
but this is only for backwards compatibility.
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. The value shall be 1.
Example:
intc: interrupt-controller {
compatible = "opencores,or1k-pic-level";
interrupt-controller;
#interrupt-cells = <1>;
};
...@@ -22,6 +22,7 @@ config OPENRISC ...@@ -22,6 +22,7 @@ config OPENRISC
select GENERIC_STRNLEN_USER select GENERIC_STRNLEN_USER
select MODULES_USE_ELF_RELA select MODULES_USE_ELF_RELA
select HAVE_DEBUG_STACKOVERFLOW select HAVE_DEBUG_STACKOVERFLOW
select OR1K_PIC
config MMU config MMU
def_bool y def_bool y
......
...@@ -24,4 +24,7 @@ ...@@ -24,4 +24,7 @@
#define NO_IRQ (-1) #define NO_IRQ (-1)
void handle_IRQ(unsigned int, struct pt_regs *);
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
#endif /* __ASM_OPENRISC_IRQ_H__ */ #endif /* __ASM_OPENRISC_IRQ_H__ */
...@@ -16,11 +16,10 @@ ...@@ -16,11 +16,10 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/of.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/irqdomain.h>
#include <linux/irqflags.h> #include <linux/irqflags.h>
/* read interrupt enabled status */ /* read interrupt enabled status */
...@@ -37,150 +36,31 @@ void arch_local_irq_restore(unsigned long flags) ...@@ -37,150 +36,31 @@ void arch_local_irq_restore(unsigned long flags)
} }
EXPORT_SYMBOL(arch_local_irq_restore); EXPORT_SYMBOL(arch_local_irq_restore);
void __init init_IRQ(void)
/* OR1K PIC implementation */
/* We're a couple of cycles faster than the generic implementations with
* these 'fast' versions.
*/
static void or1k_pic_mask(struct irq_data *data)
{
mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
}
static void or1k_pic_unmask(struct irq_data *data)
{
mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq));
}
static void or1k_pic_ack(struct irq_data *data)
{
/* EDGE-triggered interrupts need to be ack'ed in order to clear
* the latch.
* LEVEL-triggered interrupts do not need to be ack'ed; however,
* ack'ing the interrupt has no ill-effect and is quicker than
* trying to figure out what type it is...
*/
/* The OpenRISC 1000 spec says to write a 1 to the bit to ack the
* interrupt, but the OR1200 does this backwards and requires a 0
* to be written...
*/
#ifdef CONFIG_OR1K_1200
/* There are two oddities with the OR1200 PIC implementation:
* i) LEVEL-triggered interrupts are latched and need to be cleared
* ii) the interrupt latch is cleared by writing a 0 to the bit,
* as opposed to a 1 as mandated by the spec
*/
mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
#else
WARN(1, "Interrupt handling possibly broken\n");
mtspr(SPR_PICSR, (1UL << data->hwirq));
#endif
}
static void or1k_pic_mask_ack(struct irq_data *data)
{
/* Comments for pic_ack apply here, too */
#ifdef CONFIG_OR1K_1200
mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
#else
WARN(1, "Interrupt handling possibly broken\n");
mtspr(SPR_PICMR, (1UL << data->hwirq));
mtspr(SPR_PICSR, (1UL << data->hwirq));
#endif
}
#if 0
static int or1k_pic_set_type(struct irq_data *data, unsigned int flow_type)
{
/* There's nothing to do in the PIC configuration when changing
* flow type. Level and edge-triggered interrupts are both
* supported, but it's PIC-implementation specific which type
* is handled. */
return irq_setup_alt_chip(data, flow_type);
}
#endif
static struct irq_chip or1k_dev = {
.name = "or1k-PIC",
.irq_unmask = or1k_pic_unmask,
.irq_mask = or1k_pic_mask,
.irq_ack = or1k_pic_ack,
.irq_mask_ack = or1k_pic_mask_ack,
};
static struct irq_domain *root_domain;
static inline int pic_get_irq(int first)
{
int hwirq;
hwirq = ffs(mfspr(SPR_PICSR) >> first);
if (!hwirq)
return NO_IRQ;
else
hwirq = hwirq + first -1;
return irq_find_mapping(root_domain, hwirq);
}
static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{ {
irq_set_chip_and_handler_name(irq, &or1k_dev, irqchip_init();
handle_level_irq, "level");
irq_set_status_flags(irq, IRQ_LEVEL | IRQ_NOPROBE);
return 0;
} }
static const struct irq_domain_ops or1k_irq_domain_ops = { static void (*handle_arch_irq)(struct pt_regs *);
.xlate = irq_domain_xlate_onecell,
.map = or1k_map,
};
/*
* This sets up the IRQ domain for the PIC built in to the OpenRISC
* 1000 CPU. This is the "root" domain as these are the interrupts
* that directly trigger an exception in the CPU.
*/
static void __init or1k_irq_init(void)
{
struct device_node *intc = NULL;
/* The interrupt controller device node is mandatory */
intc = of_find_compatible_node(NULL, NULL, "opencores,or1k-pic");
BUG_ON(!intc);
/* Disable all interrupts until explicitly requested */
mtspr(SPR_PICMR, (0UL));
root_domain = irq_domain_add_linear(intc, 32,
&or1k_irq_domain_ops, NULL);
}
void __init init_IRQ(void) void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{ {
or1k_irq_init(); handle_arch_irq = handle_irq;
} }
void __irq_entry do_IRQ(struct pt_regs *regs) void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{ {
int irq = -1;
struct pt_regs *old_regs = set_irq_regs(regs); struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter(); irq_enter();
while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) generic_handle_irq(irq);
generic_handle_irq(irq);
irq_exit(); irq_exit();
set_irq_regs(old_regs); set_irq_regs(old_regs);
} }
void __irq_entry do_IRQ(struct pt_regs *regs)
{
handle_arch_irq(regs);
}
...@@ -53,6 +53,10 @@ config CLPS711X_IRQCHIP ...@@ -53,6 +53,10 @@ config CLPS711X_IRQCHIP
select SPARSE_IRQ select SPARSE_IRQ
default y default y
config OR1K_PIC
bool
select IRQ_DOMAIN
config ORION_IRQCHIP config ORION_IRQCHIP
bool bool
select IRQ_DOMAIN select IRQ_DOMAIN
......
...@@ -11,6 +11,7 @@ obj-$(CONFIG_METAG) += irq-metag-ext.o ...@@ -11,6 +11,7 @@ obj-$(CONFIG_METAG) += irq-metag-ext.o
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
......
...@@ -15,22 +15,31 @@ ...@@ -15,22 +15,31 @@
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/irqchip/arm-gic.h> #include <linux/irqchip/arm-gic.h>
#include <linux/irqchip/irq-crossbar.h>
#define IRQ_FREE -1 #define IRQ_FREE -1
#define IRQ_RESERVED -2
#define IRQ_SKIP -3
#define GIC_IRQ_START 32 #define GIC_IRQ_START 32
/* /**
* struct crossbar_device - crossbar device description
* @int_max: maximum number of supported interrupts * @int_max: maximum number of supported interrupts
* @safe_map: safe default value to initialize the crossbar
* @max_crossbar_sources: Maximum number of crossbar sources
* @irq_map: array of interrupts to crossbar number mapping * @irq_map: array of interrupts to crossbar number mapping
* @crossbar_base: crossbar base address * @crossbar_base: crossbar base address
* @register_offsets: offsets for each irq number * @register_offsets: offsets for each irq number
* @write: register write function pointer
*/ */
struct crossbar_device { struct crossbar_device {
uint int_max; uint int_max;
uint safe_map;
uint max_crossbar_sources;
uint *irq_map; uint *irq_map;
void __iomem *crossbar_base; void __iomem *crossbar_base;
int *register_offsets; int *register_offsets;
void (*write) (int, int); void (*write)(int, int);
}; };
static struct crossbar_device *cb; static struct crossbar_device *cb;
...@@ -50,11 +59,22 @@ static inline void crossbar_writeb(int irq_no, int cb_no) ...@@ -50,11 +59,22 @@ static inline void crossbar_writeb(int irq_no, int cb_no)
writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
} }
static inline int get_prev_map_irq(int cb_no)
{
int i;
for (i = cb->int_max - 1; i >= 0; i--)
if (cb->irq_map[i] == cb_no)
return i;
return -ENODEV;
}
static inline int allocate_free_irq(int cb_no) static inline int allocate_free_irq(int cb_no)
{ {
int i; int i;
for (i = 0; i < cb->int_max; i++) { for (i = cb->int_max - 1; i >= 0; i--) {
if (cb->irq_map[i] == IRQ_FREE) { if (cb->irq_map[i] == IRQ_FREE) {
cb->irq_map[i] = cb_no; cb->irq_map[i] = cb_no;
return i; return i;
...@@ -64,19 +84,47 @@ static inline int allocate_free_irq(int cb_no) ...@@ -64,19 +84,47 @@ static inline int allocate_free_irq(int cb_no)
return -ENODEV; return -ENODEV;
} }
static inline bool needs_crossbar_write(irq_hw_number_t hw)
{
int cb_no;
if (hw > GIC_IRQ_START) {
cb_no = cb->irq_map[hw - GIC_IRQ_START];
if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP)
return true;
}
return false;
}
static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw) irq_hw_number_t hw)
{ {
cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); if (needs_crossbar_write(hw))
cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
return 0; return 0;
} }
/**
* crossbar_domain_unmap - unmap a crossbar<->irq connection
* @d: domain of irq to unmap
* @irq: virq number
*
* We do not maintain a use count of total number of map/unmap
* calls for a particular irq to find out if a irq can be really
* unmapped. This is because unmap is called during irq_dispose_mapping(irq),
* after which irq is anyways unusable. So an explicit map has to be called
* after that.
*/
static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
{ {
irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
if (hw > GIC_IRQ_START) if (needs_crossbar_write(hw)) {
cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
cb->write(hw - GIC_IRQ_START, cb->safe_map);
}
} }
static int crossbar_domain_xlate(struct irq_domain *d, static int crossbar_domain_xlate(struct irq_domain *d,
...@@ -85,18 +133,41 @@ static int crossbar_domain_xlate(struct irq_domain *d, ...@@ -85,18 +133,41 @@ static int crossbar_domain_xlate(struct irq_domain *d,
unsigned long *out_hwirq, unsigned long *out_hwirq,
unsigned int *out_type) unsigned int *out_type)
{ {
unsigned long ret; int ret;
int req_num = intspec[1];
int direct_map_num;
if (req_num >= cb->max_crossbar_sources) {
direct_map_num = req_num - cb->max_crossbar_sources;
if (direct_map_num < cb->int_max) {
ret = cb->irq_map[direct_map_num];
if (ret == IRQ_RESERVED || ret == IRQ_SKIP) {
/* We use the interrupt num as h/w irq num */
ret = direct_map_num;
goto found;
}
}
pr_err("%s: requested crossbar number %d > max %d\n",
__func__, req_num, cb->max_crossbar_sources);
return -EINVAL;
}
ret = allocate_free_irq(intspec[1]); ret = get_prev_map_irq(req_num);
if (ret >= 0)
goto found;
if (IS_ERR_VALUE(ret)) ret = allocate_free_irq(req_num);
if (ret < 0)
return ret; return ret;
found:
*out_hwirq = ret + GIC_IRQ_START; *out_hwirq = ret + GIC_IRQ_START;
return 0; return 0;
} }
const struct irq_domain_ops routable_irq_domain_ops = { static const struct irq_domain_ops routable_irq_domain_ops = {
.map = crossbar_domain_map, .map = crossbar_domain_map,
.unmap = crossbar_domain_unmap, .unmap = crossbar_domain_unmap,
.xlate = crossbar_domain_xlate .xlate = crossbar_domain_xlate
...@@ -104,22 +175,36 @@ const struct irq_domain_ops routable_irq_domain_ops = { ...@@ -104,22 +175,36 @@ const struct irq_domain_ops routable_irq_domain_ops = {
static int __init crossbar_of_init(struct device_node *node) static int __init crossbar_of_init(struct device_node *node)
{ {
int i, size, max, reserved = 0, entry; int i, size, max = 0, reserved = 0, entry;
const __be32 *irqsr; const __be32 *irqsr;
int ret = -ENOMEM;
cb = kzalloc(sizeof(*cb), GFP_KERNEL); cb = kzalloc(sizeof(*cb), GFP_KERNEL);
if (!cb) if (!cb)
return -ENOMEM; return ret;
cb->crossbar_base = of_iomap(node, 0); cb->crossbar_base = of_iomap(node, 0);
if (!cb->crossbar_base) if (!cb->crossbar_base)
goto err1; goto err_cb;
of_property_read_u32(node, "ti,max-crossbar-sources",
&cb->max_crossbar_sources);
if (!cb->max_crossbar_sources) {
pr_err("missing 'ti,max-crossbar-sources' property\n");
ret = -EINVAL;
goto err_base;
}
of_property_read_u32(node, "ti,max-irqs", &max); of_property_read_u32(node, "ti,max-irqs", &max);
cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); if (!max) {
pr_err("missing 'ti,max-irqs' property\n");
ret = -EINVAL;
goto err_base;
}
cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL);
if (!cb->irq_map) if (!cb->irq_map)
goto err2; goto err_base;
cb->int_max = max; cb->int_max = max;
...@@ -137,15 +222,35 @@ static int __init crossbar_of_init(struct device_node *node) ...@@ -137,15 +222,35 @@ static int __init crossbar_of_init(struct device_node *node)
i, &entry); i, &entry);
if (entry > max) { if (entry > max) {
pr_err("Invalid reserved entry\n"); pr_err("Invalid reserved entry\n");
goto err3; ret = -EINVAL;
goto err_irq_map;
}
cb->irq_map[entry] = IRQ_RESERVED;
}
}
/* Skip irqs hardwired to bypass the crossbar */
irqsr = of_get_property(node, "ti,irqs-skip", &size);
if (irqsr) {
size /= sizeof(__be32);
for (i = 0; i < size; i++) {
of_property_read_u32_index(node,
"ti,irqs-skip",
i, &entry);
if (entry > max) {
pr_err("Invalid skip entry\n");
ret = -EINVAL;
goto err_irq_map;
} }
cb->irq_map[entry] = 0; cb->irq_map[entry] = IRQ_SKIP;
} }
} }
cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL);
cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL);
if (!cb->register_offsets) if (!cb->register_offsets)
goto err3; goto err_irq_map;
of_property_read_u32(node, "ti,reg-size", &size); of_property_read_u32(node, "ti,reg-size", &size);
...@@ -161,7 +266,8 @@ static int __init crossbar_of_init(struct device_node *node) ...@@ -161,7 +266,8 @@ static int __init crossbar_of_init(struct device_node *node)
break; break;
default: default:
pr_err("Invalid reg-size property\n"); pr_err("Invalid reg-size property\n");
goto err4; ret = -EINVAL;
goto err_reg_offset;
break; break;
} }
...@@ -170,25 +276,37 @@ static int __init crossbar_of_init(struct device_node *node) ...@@ -170,25 +276,37 @@ static int __init crossbar_of_init(struct device_node *node)
* reserved irqs. so find and store the offsets once. * reserved irqs. so find and store the offsets once.
*/ */
for (i = 0; i < max; i++) { for (i = 0; i < max; i++) {
if (!cb->irq_map[i]) if (cb->irq_map[i] == IRQ_RESERVED)
continue; continue;
cb->register_offsets[i] = reserved; cb->register_offsets[i] = reserved;
reserved += size; reserved += size;
} }
of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map);
/* Initialize the crossbar with safe map to start with */
for (i = 0; i < max; i++) {
if (cb->irq_map[i] == IRQ_RESERVED ||
cb->irq_map[i] == IRQ_SKIP)
continue;
cb->write(i, cb->safe_map);
}
register_routable_domain_ops(&routable_irq_domain_ops); register_routable_domain_ops(&routable_irq_domain_ops);
return 0; return 0;
err4: err_reg_offset:
kfree(cb->register_offsets); kfree(cb->register_offsets);
err3: err_irq_map:
kfree(cb->irq_map); kfree(cb->irq_map);
err2: err_base:
iounmap(cb->crossbar_base); iounmap(cb->crossbar_base);
err1: err_cb:
kfree(cb); kfree(cb);
return -ENOMEM;
cb = NULL;
return ret;
} }
static const struct of_device_id crossbar_match[] __initconst = { static const struct of_device_id crossbar_match[] __initconst = {
......
/*
* Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
* Copyright (C) 2014 Stefan Kristansson <stefan.kristiansson@saunalahti.fi>
*
* 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.
*/
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include "irqchip.h"
/* OR1K PIC implementation */
struct or1k_pic_dev {
struct irq_chip chip;
irq_flow_handler_t handle;
unsigned long flags;
};
/*
* We're a couple of cycles faster than the generic implementations with
* these 'fast' versions.
*/
static void or1k_pic_mask(struct irq_data *data)
{
mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
}
static void or1k_pic_unmask(struct irq_data *data)
{
mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq));
}
static void or1k_pic_ack(struct irq_data *data)
{
mtspr(SPR_PICSR, (1UL << data->hwirq));
}
static void or1k_pic_mask_ack(struct irq_data *data)
{
mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
mtspr(SPR_PICSR, (1UL << data->hwirq));
}
/*
* There are two oddities with the OR1200 PIC implementation:
* i) LEVEL-triggered interrupts are latched and need to be cleared
* ii) the interrupt latch is cleared by writing a 0 to the bit,
* as opposed to a 1 as mandated by the spec
*/
static void or1k_pic_or1200_ack(struct irq_data *data)
{
mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
}
static void or1k_pic_or1200_mask_ack(struct irq_data *data)
{
mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
}
static struct or1k_pic_dev or1k_pic_level = {
.chip = {
.name = "or1k-PIC-level",
.irq_unmask = or1k_pic_unmask,
.irq_mask = or1k_pic_mask,
.irq_mask_ack = or1k_pic_mask,
},
.handle = handle_level_irq,
.flags = IRQ_LEVEL | IRQ_NOPROBE,
};
static struct or1k_pic_dev or1k_pic_edge = {
.chip = {
.name = "or1k-PIC-edge",
.irq_unmask = or1k_pic_unmask,
.irq_mask = or1k_pic_mask,
.irq_ack = or1k_pic_ack,
.irq_mask_ack = or1k_pic_mask_ack,
},
.handle = handle_edge_irq,
.flags = IRQ_LEVEL | IRQ_NOPROBE,
};
static struct or1k_pic_dev or1k_pic_or1200 = {
.chip = {
.name = "or1200-PIC",
.irq_unmask = or1k_pic_unmask,
.irq_mask = or1k_pic_mask,
.irq_ack = or1k_pic_or1200_ack,
.irq_mask_ack = or1k_pic_or1200_mask_ack,
},
.handle = handle_level_irq,
.flags = IRQ_LEVEL | IRQ_NOPROBE,
};
static struct irq_domain *root_domain;
static inline int pic_get_irq(int first)
{
int hwirq;
hwirq = ffs(mfspr(SPR_PICSR) >> first);
if (!hwirq)
return NO_IRQ;
else
hwirq = hwirq + first - 1;
return irq_find_mapping(root_domain, hwirq);
}
static void or1k_pic_handle_irq(struct pt_regs *regs)
{
int irq = -1;
while ((irq = pic_get_irq(irq + 1)) != NO_IRQ)
handle_IRQ(irq, regs);
}
static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
struct or1k_pic_dev *pic = d->host_data;
irq_set_chip_and_handler(irq, &pic->chip, pic->handle);
irq_set_status_flags(irq, pic->flags);
return 0;
}
static const struct irq_domain_ops or1k_irq_domain_ops = {
.xlate = irq_domain_xlate_onecell,
.map = or1k_map,
};
/*
* This sets up the IRQ domain for the PIC built in to the OpenRISC
* 1000 CPU. This is the "root" domain as these are the interrupts
* that directly trigger an exception in the CPU.
*/
static int __init or1k_pic_init(struct device_node *node,
struct or1k_pic_dev *pic)
{
/* Disable all interrupts until explicitly requested */
mtspr(SPR_PICMR, (0UL));
root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops,
pic);
set_handle_irq(or1k_pic_handle_irq);
return 0;
}
static int __init or1k_pic_or1200_init(struct device_node *node,
struct device_node *parent)
{
return or1k_pic_init(node, &or1k_pic_or1200);
}
IRQCHIP_DECLARE(or1k_pic_or1200, "opencores,or1200-pic", or1k_pic_or1200_init);
IRQCHIP_DECLARE(or1k_pic, "opencores,or1k-pic", or1k_pic_or1200_init);
static int __init or1k_pic_level_init(struct device_node *node,
struct device_node *parent)
{
return or1k_pic_init(node, &or1k_pic_level);
}
IRQCHIP_DECLARE(or1k_pic_level, "opencores,or1k-pic-level",
or1k_pic_level_init);
static int __init or1k_pic_edge_init(struct device_node *node,
struct device_node *parent)
{
return or1k_pic_init(node, &or1k_pic_edge);
}
IRQCHIP_DECLARE(or1k_pic_edge, "opencores,or1k-pic-edge", or1k_pic_edge_init);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment