Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner: "Nothing spectacular from the irq department this time: - overhaul of the crossbar chip driver - overhaul of the spear shirq chip driver - support for the atmel-aic chip - code move from arch to drivers - the usual tiny fixlets - two reverts worth to mention which undo the too simple attempt of supporting wakeup interrupts on shared interrupt lines" * 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (41 commits) Revert "irq: Warn when shared interrupts do not match on NO_SUSPEND" Revert "PM / sleep / irq: Do not suspend wakeup interrupts" irq: Warn when shared interrupts do not match on NO_SUSPEND irqchip: atmel-aic: Define irq fixups for atmel SoCs irqchip: atmel-aic: Implement RTC irq fixup irqchip: atmel-aic: Add irq fixup infrastructure irqchip: atmel-aic: Add atmel AIC/AIC5 drivers irqchip: atmel-aic: Move binding doc to interrupt-controller directory genirq: generic chip: Export irq_map_generic_chip function PM / sleep / irq: Do not suspend wakeup interrupts irqchip: or1k-pic: Migrate from arch/openrisc/ irqchip: crossbar: Allow for quirky hardware with direct hardwiring of GIC documentation: dt: omap: crossbar: Add description for interrupt consumer irqchip: crossbar: Introduce centralized check for crossbar write irqchip: crossbar: Introduce ti, max-crossbar-sources to identify valid crossbar mapping irqchip: crossbar: Add kerneldoc for crossbar_domain_unmap callback irqchip: crossbar: Set cb pointer to null in case of error irqchip: crossbar: Change the goto naming irqchip: crossbar: Return proper error value irqchip: crossbar: Fix kerneldoc warning ...
This commit is contained in:
commit
08d69a2571
@ -10,6 +10,7 @@ Required properties:
|
||||
- compatible : Should be "ti,irq-crossbar"
|
||||
- reg: Base address and the size of the crossbar registers.
|
||||
- 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
|
||||
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
|
||||
@ -17,11 +18,46 @@ Required properties:
|
||||
so crossbar bar driver should not consider them as free
|
||||
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:
|
||||
crossbar_mpu: @4a020000 {
|
||||
compatible = "ti,irq-crossbar";
|
||||
reg = <0x4a002a48 0x130>;
|
||||
ti,max-irqs = <160>;
|
||||
ti,max-crossbar-sources = <400>;
|
||||
ti,reg-size = <2>;
|
||||
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>;
|
||||
...
|
||||
};
|
||||
|
@ -0,0 +1,23 @@
|
||||
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
|
||||
select GENERIC_STRNLEN_USER
|
||||
select MODULES_USE_ELF_RELA
|
||||
select HAVE_DEBUG_STACKOVERFLOW
|
||||
select OR1K_PIC
|
||||
|
||||
config MMU
|
||||
def_bool y
|
||||
|
@ -24,4 +24,7 @@
|
||||
|
||||
#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__ */
|
||||
|
@ -16,11 +16,10 @@
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irqflags.h>
|
||||
|
||||
/* read interrupt enabled status */
|
||||
@ -37,150 +36,31 @@ void arch_local_irq_restore(unsigned long flags)
|
||||
}
|
||||
EXPORT_SYMBOL(arch_local_irq_restore);
|
||||
|
||||
|
||||
/* 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,
|
||||
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 = {
|
||||
.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)
|
||||
{
|
||||
or1k_irq_init();
|
||||
irqchip_init();
|
||||
}
|
||||
|
||||
void __irq_entry do_IRQ(struct pt_regs *regs)
|
||||
static void (*handle_arch_irq)(struct pt_regs *);
|
||||
|
||||
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
|
||||
{
|
||||
handle_arch_irq = handle_irq;
|
||||
}
|
||||
|
||||
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
|
||||
{
|
||||
int irq = -1;
|
||||
struct pt_regs *old_regs = set_irq_regs(regs);
|
||||
|
||||
irq_enter();
|
||||
|
||||
while ((irq = pic_get_irq(irq + 1)) != NO_IRQ)
|
||||
generic_handle_irq(irq);
|
||||
generic_handle_irq(irq);
|
||||
|
||||
irq_exit();
|
||||
set_irq_regs(old_regs);
|
||||
}
|
||||
|
||||
void __irq_entry do_IRQ(struct pt_regs *regs)
|
||||
{
|
||||
handle_arch_irq(regs);
|
||||
}
|
||||
|
@ -35,6 +35,20 @@ config ARM_VIC_NR
|
||||
The maximum number of VICs available in the system, for
|
||||
power management.
|
||||
|
||||
config ATMEL_AIC_IRQ
|
||||
bool
|
||||
select GENERIC_IRQ_CHIP
|
||||
select IRQ_DOMAIN
|
||||
select MULTI_IRQ_HANDLER
|
||||
select SPARSE_IRQ
|
||||
|
||||
config ATMEL_AIC5_IRQ
|
||||
bool
|
||||
select GENERIC_IRQ_CHIP
|
||||
select IRQ_DOMAIN
|
||||
select MULTI_IRQ_HANDLER
|
||||
select SPARSE_IRQ
|
||||
|
||||
config BRCMSTB_L2_IRQ
|
||||
bool
|
||||
depends on ARM
|
||||
@ -58,6 +72,10 @@ config CLPS711X_IRQCHIP
|
||||
select SPARSE_IRQ
|
||||
default y
|
||||
|
||||
config OR1K_PIC
|
||||
bool
|
||||
select IRQ_DOMAIN
|
||||
|
||||
config ORION_IRQCHIP
|
||||
bool
|
||||
select IRQ_DOMAIN
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_METAG) += irq-metag-ext.o
|
||||
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
|
||||
obj-$(CONFIG_ARCH_MOXART) += irq-moxart.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_ARCH_SUNXI) += irq-sun4i.o
|
||||
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
|
||||
@ -19,6 +20,8 @@ obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
|
||||
obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
|
||||
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
|
||||
obj-$(CONFIG_ARM_VIC) += irq-vic.o
|
||||
obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o
|
||||
obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o
|
||||
obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
|
||||
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
|
||||
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
|
||||
|
254
drivers/irqchip/irq-atmel-aic-common.c
Normal file
254
drivers/irqchip/irq-atmel-aic-common.c
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Atmel AT91 common AIC (Advanced Interrupt Controller) code shared by
|
||||
* irq-atmel-aic and irq-atmel-aic5 drivers
|
||||
*
|
||||
* Copyright (C) 2004 SAN People
|
||||
* Copyright (C) 2004 ATMEL
|
||||
* Copyright (C) Rick Bronson
|
||||
* Copyright (C) 2014 Free Electrons
|
||||
*
|
||||
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "irq-atmel-aic-common.h"
|
||||
|
||||
#define AT91_AIC_PRIOR GENMASK(2, 0)
|
||||
#define AT91_AIC_IRQ_MIN_PRIORITY 0
|
||||
#define AT91_AIC_IRQ_MAX_PRIORITY 7
|
||||
|
||||
#define AT91_AIC_SRCTYPE GENMASK(7, 6)
|
||||
#define AT91_AIC_SRCTYPE_LOW (0 << 5)
|
||||
#define AT91_AIC_SRCTYPE_FALLING (1 << 5)
|
||||
#define AT91_AIC_SRCTYPE_HIGH (2 << 5)
|
||||
#define AT91_AIC_SRCTYPE_RISING (3 << 5)
|
||||
|
||||
struct aic_chip_data {
|
||||
u32 ext_irqs;
|
||||
};
|
||||
|
||||
static void aic_common_shutdown(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip_type *ct = irq_data_get_chip_type(d);
|
||||
|
||||
ct->chip.irq_mask(d);
|
||||
}
|
||||
|
||||
int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct aic_chip_data *aic = gc->private;
|
||||
unsigned aic_type;
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
aic_type = AT91_AIC_SRCTYPE_HIGH;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
aic_type = AT91_AIC_SRCTYPE_RISING;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
if (!(d->mask & aic->ext_irqs))
|
||||
return -EINVAL;
|
||||
|
||||
aic_type = AT91_AIC_SRCTYPE_LOW;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
if (!(d->mask & aic->ext_irqs))
|
||||
return -EINVAL;
|
||||
|
||||
aic_type = AT91_AIC_SRCTYPE_FALLING;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val &= AT91_AIC_SRCTYPE;
|
||||
*val |= aic_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aic_common_set_priority(int priority, unsigned *val)
|
||||
{
|
||||
if (priority < AT91_AIC_IRQ_MIN_PRIORITY ||
|
||||
priority > AT91_AIC_IRQ_MAX_PRIORITY)
|
||||
return -EINVAL;
|
||||
|
||||
*val &= AT91_AIC_PRIOR;
|
||||
*val |= priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aic_common_irq_domain_xlate(struct irq_domain *d,
|
||||
struct device_node *ctrlr,
|
||||
const u32 *intspec,
|
||||
unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq,
|
||||
unsigned int *out_type)
|
||||
{
|
||||
if (WARN_ON(intsize < 3))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON((intspec[2] < AT91_AIC_IRQ_MIN_PRIORITY) ||
|
||||
(intspec[2] > AT91_AIC_IRQ_MAX_PRIORITY)))
|
||||
return -EINVAL;
|
||||
|
||||
*out_hwirq = intspec[0];
|
||||
*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init aic_common_ext_irq_of_init(struct irq_domain *domain)
|
||||
{
|
||||
struct device_node *node = domain->of_node;
|
||||
struct irq_chip_generic *gc;
|
||||
struct aic_chip_data *aic;
|
||||
struct property *prop;
|
||||
const __be32 *p;
|
||||
u32 hwirq;
|
||||
|
||||
gc = irq_get_domain_generic_chip(domain, 0);
|
||||
|
||||
aic = gc->private;
|
||||
aic->ext_irqs |= 1;
|
||||
|
||||
of_property_for_each_u32(node, "atmel,external-irqs", prop, p, hwirq) {
|
||||
gc = irq_get_domain_generic_chip(domain, hwirq);
|
||||
if (!gc) {
|
||||
pr_warn("AIC: external irq %d >= %d skip it\n",
|
||||
hwirq, domain->revmap_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
aic = gc->private;
|
||||
aic->ext_irqs |= (1 << (hwirq % 32));
|
||||
}
|
||||
}
|
||||
|
||||
#define AT91_RTC_IDR 0x24
|
||||
#define AT91_RTC_IMR 0x28
|
||||
#define AT91_RTC_IRQ_MASK 0x1f
|
||||
|
||||
void __init aic_common_rtc_irq_fixup(struct device_node *root)
|
||||
{
|
||||
struct device_node *np;
|
||||
void __iomem *regs;
|
||||
|
||||
np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc");
|
||||
if (!np)
|
||||
np = of_find_compatible_node(root, NULL,
|
||||
"atmel,at91sam9x5-rtc");
|
||||
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
regs = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
|
||||
if (!regs)
|
||||
return;
|
||||
|
||||
writel(AT91_RTC_IRQ_MASK, regs + AT91_RTC_IDR);
|
||||
|
||||
iounmap(regs);
|
||||
}
|
||||
|
||||
void __init aic_common_irq_fixup(const struct of_device_id *matches)
|
||||
{
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
match = of_match_node(matches, root);
|
||||
of_node_put(root);
|
||||
|
||||
if (match) {
|
||||
void (*fixup)(struct device_node *) = match->data;
|
||||
fixup(root);
|
||||
}
|
||||
|
||||
of_node_put(root);
|
||||
}
|
||||
|
||||
struct irq_domain *__init aic_common_of_init(struct device_node *node,
|
||||
const struct irq_domain_ops *ops,
|
||||
const char *name, int nirqs)
|
||||
{
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_domain *domain;
|
||||
struct aic_chip_data *aic;
|
||||
void __iomem *reg_base;
|
||||
int nchips;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
nchips = DIV_ROUND_UP(nirqs, 32);
|
||||
|
||||
reg_base = of_iomap(node, 0);
|
||||
if (!reg_base)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
aic = kcalloc(nchips, sizeof(*aic), GFP_KERNEL);
|
||||
if (!aic) {
|
||||
ret = -ENOMEM;
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
domain = irq_domain_add_linear(node, nchips * 32, ops, aic);
|
||||
if (!domain) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_aic;
|
||||
}
|
||||
|
||||
ret = irq_alloc_domain_generic_chips(domain, 32, 1, name,
|
||||
handle_level_irq, 0, 0,
|
||||
IRQCHIP_SKIP_SET_WAKE);
|
||||
if (ret)
|
||||
goto err_domain_remove;
|
||||
|
||||
for (i = 0; i < nchips; i++) {
|
||||
gc = irq_get_domain_generic_chip(domain, i * 32);
|
||||
|
||||
gc->reg_base = reg_base;
|
||||
|
||||
gc->unused = 0;
|
||||
gc->wake_enabled = ~0;
|
||||
gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK;
|
||||
gc->chip_types[0].handler = handle_fasteoi_irq;
|
||||
gc->chip_types[0].chip.irq_eoi = irq_gc_eoi;
|
||||
gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
|
||||
gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown;
|
||||
gc->private = &aic[i];
|
||||
}
|
||||
|
||||
aic_common_ext_irq_of_init(domain);
|
||||
|
||||
return domain;
|
||||
|
||||
err_domain_remove:
|
||||
irq_domain_remove(domain);
|
||||
|
||||
err_free_aic:
|
||||
kfree(aic);
|
||||
|
||||
err_iounmap:
|
||||
iounmap(reg_base);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
39
drivers/irqchip/irq-atmel-aic-common.h
Normal file
39
drivers/irqchip/irq-atmel-aic-common.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Atmel AT91 common AIC (Advanced Interrupt Controller) header file
|
||||
*
|
||||
* Copyright (C) 2004 SAN People
|
||||
* Copyright (C) 2004 ATMEL
|
||||
* Copyright (C) Rick Bronson
|
||||
* Copyright (C) 2014 Free Electrons
|
||||
*
|
||||
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __IRQ_ATMEL_AIC_COMMON_H
|
||||
#define __IRQ_ATMEL_AIC_COMMON_H
|
||||
|
||||
|
||||
int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val);
|
||||
|
||||
int aic_common_set_priority(int priority, unsigned *val);
|
||||
|
||||
int aic_common_irq_domain_xlate(struct irq_domain *d,
|
||||
struct device_node *ctrlr,
|
||||
const u32 *intspec,
|
||||
unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq,
|
||||
unsigned int *out_type);
|
||||
|
||||
struct irq_domain *__init aic_common_of_init(struct device_node *node,
|
||||
const struct irq_domain_ops *ops,
|
||||
const char *name, int nirqs);
|
||||
|
||||
void __init aic_common_rtc_irq_fixup(struct device_node *root);
|
||||
|
||||
void __init aic_common_irq_fixup(const struct of_device_id *matches);
|
||||
|
||||
#endif /* __IRQ_ATMEL_AIC_COMMON_H */
|
262
drivers/irqchip/irq-atmel-aic.c
Normal file
262
drivers/irqchip/irq-atmel-aic.c
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Atmel AT91 AIC (Advanced Interrupt Controller) driver
|
||||
*
|
||||
* Copyright (C) 2004 SAN People
|
||||
* Copyright (C) 2004 ATMEL
|
||||
* Copyright (C) Rick Bronson
|
||||
* Copyright (C) 2014 Free Electrons
|
||||
*
|
||||
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/exception.h>
|
||||
#include <asm/mach/irq.h>
|
||||
|
||||
#include "irq-atmel-aic-common.h"
|
||||
#include "irqchip.h"
|
||||
|
||||
/* Number of irq lines managed by AIC */
|
||||
#define NR_AIC_IRQS 32
|
||||
|
||||
#define AT91_AIC_SMR(n) ((n) * 4)
|
||||
|
||||
#define AT91_AIC_SVR(n) (0x80 + ((n) * 4))
|
||||
#define AT91_AIC_IVR 0x100
|
||||
#define AT91_AIC_FVR 0x104
|
||||
#define AT91_AIC_ISR 0x108
|
||||
|
||||
#define AT91_AIC_IPR 0x10c
|
||||
#define AT91_AIC_IMR 0x110
|
||||
#define AT91_AIC_CISR 0x114
|
||||
|
||||
#define AT91_AIC_IECR 0x120
|
||||
#define AT91_AIC_IDCR 0x124
|
||||
#define AT91_AIC_ICCR 0x128
|
||||
#define AT91_AIC_ISCR 0x12c
|
||||
#define AT91_AIC_EOICR 0x130
|
||||
#define AT91_AIC_SPU 0x134
|
||||
#define AT91_AIC_DCR 0x138
|
||||
|
||||
static struct irq_domain *aic_domain;
|
||||
|
||||
static asmlinkage void __exception_irq_entry
|
||||
aic_handle(struct pt_regs *regs)
|
||||
{
|
||||
struct irq_domain_chip_generic *dgc = aic_domain->gc;
|
||||
struct irq_chip_generic *gc = dgc->gc[0];
|
||||
u32 irqnr;
|
||||
u32 irqstat;
|
||||
|
||||
irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR);
|
||||
irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR);
|
||||
|
||||
irqnr = irq_find_mapping(aic_domain, irqnr);
|
||||
|
||||
if (!irqstat)
|
||||
irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
|
||||
else
|
||||
handle_IRQ(irqnr, regs);
|
||||
}
|
||||
|
||||
static int aic_retrigger(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
/* Enable interrupt on AIC5 */
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(d->mask, gc->reg_base + AT91_AIC_ISCR);
|
||||
irq_gc_unlock(gc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aic_set_type(struct irq_data *d, unsigned type)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
unsigned int smr;
|
||||
int ret;
|
||||
|
||||
smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(d->hwirq));
|
||||
ret = aic_common_set_type(d, type, &smr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(d->hwirq));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void aic_suspend(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IDCR);
|
||||
irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IECR);
|
||||
irq_gc_unlock(gc);
|
||||
}
|
||||
|
||||
static void aic_resume(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IDCR);
|
||||
irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IECR);
|
||||
irq_gc_unlock(gc);
|
||||
}
|
||||
|
||||
static void aic_pm_shutdown(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR);
|
||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR);
|
||||
irq_gc_unlock(gc);
|
||||
}
|
||||
#else
|
||||
#define aic_suspend NULL
|
||||
#define aic_resume NULL
|
||||
#define aic_pm_shutdown NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static void __init aic_hw_init(struct irq_domain *domain)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Perform 8 End Of Interrupt Command to make sure AIC
|
||||
* will not Lock out nIRQ
|
||||
*/
|
||||
for (i = 0; i < 8; i++)
|
||||
irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR);
|
||||
|
||||
/*
|
||||
* Spurious Interrupt ID in Spurious Vector Register.
|
||||
* When there is no current interrupt, the IRQ Vector Register
|
||||
* reads the value stored in AIC_SPU
|
||||
*/
|
||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_SPU);
|
||||
|
||||
/* No debugging in AIC: Debug (Protect) Control Register */
|
||||
irq_reg_writel(0, gc->reg_base + AT91_AIC_DCR);
|
||||
|
||||
/* Disable and clear all interrupts initially */
|
||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR);
|
||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR);
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
irq_reg_writel(i, gc->reg_base + AT91_AIC_SVR(i));
|
||||
}
|
||||
|
||||
static int aic_irq_domain_xlate(struct irq_domain *d,
|
||||
struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq,
|
||||
unsigned int *out_type)
|
||||
{
|
||||
struct irq_domain_chip_generic *dgc = d->gc;
|
||||
struct irq_chip_generic *gc;
|
||||
unsigned smr;
|
||||
int idx;
|
||||
int ret;
|
||||
|
||||
if (!dgc)
|
||||
return -EINVAL;
|
||||
|
||||
ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
|
||||
out_hwirq, out_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
idx = intspec[0] / dgc->irqs_per_chip;
|
||||
if (idx >= dgc->num_chips)
|
||||
return -EINVAL;
|
||||
|
||||
gc = dgc->gc[idx];
|
||||
|
||||
irq_gc_lock(gc);
|
||||
smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(*out_hwirq));
|
||||
ret = aic_common_set_priority(intspec[2], &smr);
|
||||
if (!ret)
|
||||
irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(*out_hwirq));
|
||||
irq_gc_unlock(gc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops aic_irq_ops = {
|
||||
.map = irq_map_generic_chip,
|
||||
.xlate = aic_irq_domain_xlate,
|
||||
};
|
||||
|
||||
static void __init at91sam9_aic_irq_fixup(struct device_node *root)
|
||||
{
|
||||
aic_common_rtc_irq_fixup(root);
|
||||
}
|
||||
|
||||
static const struct of_device_id __initdata aic_irq_fixups[] = {
|
||||
{ .compatible = "atmel,at91sam9g45", .data = at91sam9_aic_irq_fixup },
|
||||
{ .compatible = "atmel,at91sam9n12", .data = at91sam9_aic_irq_fixup },
|
||||
{ .compatible = "atmel,at91sam9rl", .data = at91sam9_aic_irq_fixup },
|
||||
{ .compatible = "atmel,at91sam9x5", .data = at91sam9_aic_irq_fixup },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static int __init aic_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_domain *domain;
|
||||
|
||||
if (aic_domain)
|
||||
return -EEXIST;
|
||||
|
||||
domain = aic_common_of_init(node, &aic_irq_ops, "atmel-aic",
|
||||
NR_AIC_IRQS);
|
||||
if (IS_ERR(domain))
|
||||
return PTR_ERR(domain);
|
||||
|
||||
aic_common_irq_fixup(aic_irq_fixups);
|
||||
|
||||
aic_domain = domain;
|
||||
gc = irq_get_domain_generic_chip(domain, 0);
|
||||
|
||||
gc->chip_types[0].regs.eoi = AT91_AIC_EOICR;
|
||||
gc->chip_types[0].regs.enable = AT91_AIC_IECR;
|
||||
gc->chip_types[0].regs.disable = AT91_AIC_IDCR;
|
||||
gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
|
||||
gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
|
||||
gc->chip_types[0].chip.irq_retrigger = aic_retrigger;
|
||||
gc->chip_types[0].chip.irq_set_type = aic_set_type;
|
||||
gc->chip_types[0].chip.irq_suspend = aic_suspend;
|
||||
gc->chip_types[0].chip.irq_resume = aic_resume;
|
||||
gc->chip_types[0].chip.irq_pm_shutdown = aic_pm_shutdown;
|
||||
|
||||
aic_hw_init(domain);
|
||||
set_handle_irq(aic_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
IRQCHIP_DECLARE(at91rm9200_aic, "atmel,at91rm9200-aic", aic_of_init);
|
353
drivers/irqchip/irq-atmel-aic5.c
Normal file
353
drivers/irqchip/irq-atmel-aic5.c
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Atmel AT91 AIC5 (Advanced Interrupt Controller) driver
|
||||
*
|
||||
* Copyright (C) 2004 SAN People
|
||||
* Copyright (C) 2004 ATMEL
|
||||
* Copyright (C) Rick Bronson
|
||||
* Copyright (C) 2014 Free Electrons
|
||||
*
|
||||
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/exception.h>
|
||||
#include <asm/mach/irq.h>
|
||||
|
||||
#include "irq-atmel-aic-common.h"
|
||||
#include "irqchip.h"
|
||||
|
||||
/* Number of irq lines managed by AIC */
|
||||
#define NR_AIC5_IRQS 128
|
||||
|
||||
#define AT91_AIC5_SSR 0x0
|
||||
#define AT91_AIC5_INTSEL_MSK (0x7f << 0)
|
||||
|
||||
#define AT91_AIC5_SMR 0x4
|
||||
|
||||
#define AT91_AIC5_SVR 0x8
|
||||
#define AT91_AIC5_IVR 0x10
|
||||
#define AT91_AIC5_FVR 0x14
|
||||
#define AT91_AIC5_ISR 0x18
|
||||
|
||||
#define AT91_AIC5_IPR0 0x20
|
||||
#define AT91_AIC5_IPR1 0x24
|
||||
#define AT91_AIC5_IPR2 0x28
|
||||
#define AT91_AIC5_IPR3 0x2c
|
||||
#define AT91_AIC5_IMR 0x30
|
||||
#define AT91_AIC5_CISR 0x34
|
||||
|
||||
#define AT91_AIC5_IECR 0x40
|
||||
#define AT91_AIC5_IDCR 0x44
|
||||
#define AT91_AIC5_ICCR 0x48
|
||||
#define AT91_AIC5_ISCR 0x4c
|
||||
#define AT91_AIC5_EOICR 0x38
|
||||
#define AT91_AIC5_SPU 0x3c
|
||||
#define AT91_AIC5_DCR 0x6c
|
||||
|
||||
#define AT91_AIC5_FFER 0x50
|
||||
#define AT91_AIC5_FFDR 0x54
|
||||
#define AT91_AIC5_FFSR 0x58
|
||||
|
||||
static struct irq_domain *aic5_domain;
|
||||
|
||||
static asmlinkage void __exception_irq_entry
|
||||
aic5_handle(struct pt_regs *regs)
|
||||
{
|
||||
struct irq_domain_chip_generic *dgc = aic5_domain->gc;
|
||||
struct irq_chip_generic *gc = dgc->gc[0];
|
||||
u32 irqnr;
|
||||
u32 irqstat;
|
||||
|
||||
irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR);
|
||||
irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR);
|
||||
|
||||
irqnr = irq_find_mapping(aic5_domain, irqnr);
|
||||
|
||||
if (!irqstat)
|
||||
irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
|
||||
else
|
||||
handle_IRQ(irqnr, regs);
|
||||
}
|
||||
|
||||
static void aic5_mask(struct irq_data *d)
|
||||
{
|
||||
struct irq_domain *domain = d->domain;
|
||||
struct irq_domain_chip_generic *dgc = domain->gc;
|
||||
struct irq_chip_generic *gc = dgc->gc[0];
|
||||
|
||||
/* Disable interrupt on AIC5 */
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
|
||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR);
|
||||
gc->mask_cache &= ~d->mask;
|
||||
irq_gc_unlock(gc);
|
||||
}
|
||||
|
||||
static void aic5_unmask(struct irq_data *d)
|
||||
{
|
||||
struct irq_domain *domain = d->domain;
|
||||
struct irq_domain_chip_generic *dgc = domain->gc;
|
||||
struct irq_chip_generic *gc = dgc->gc[0];
|
||||
|
||||
/* Enable interrupt on AIC5 */
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
|
||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_IECR);
|
||||
gc->mask_cache |= d->mask;
|
||||
irq_gc_unlock(gc);
|
||||
}
|
||||
|
||||
static int aic5_retrigger(struct irq_data *d)
|
||||
{
|
||||
struct irq_domain *domain = d->domain;
|
||||
struct irq_domain_chip_generic *dgc = domain->gc;
|
||||
struct irq_chip_generic *gc = dgc->gc[0];
|
||||
|
||||
/* Enable interrupt on AIC5 */
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
|
||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_ISCR);
|
||||
irq_gc_unlock(gc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aic5_set_type(struct irq_data *d, unsigned type)
|
||||
{
|
||||
struct irq_domain *domain = d->domain;
|
||||
struct irq_domain_chip_generic *dgc = domain->gc;
|
||||
struct irq_chip_generic *gc = dgc->gc[0];
|
||||
unsigned int smr;
|
||||
int ret;
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR);
|
||||
smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR);
|
||||
ret = aic_common_set_type(d, type, &smr);
|
||||
if (!ret)
|
||||
irq_reg_writel(smr, gc->reg_base + AT91_AIC5_SMR);
|
||||
irq_gc_unlock(gc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void aic5_suspend(struct irq_data *d)
|
||||
{
|
||||
struct irq_domain *domain = d->domain;
|
||||
struct irq_domain_chip_generic *dgc = domain->gc;
|
||||
struct irq_chip_generic *bgc = dgc->gc[0];
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
int i;
|
||||
u32 mask;
|
||||
|
||||
irq_gc_lock(bgc);
|
||||
for (i = 0; i < dgc->irqs_per_chip; i++) {
|
||||
mask = 1 << i;
|
||||
if ((mask & gc->mask_cache) == (mask & gc->wake_active))
|
||||
continue;
|
||||
|
||||
irq_reg_writel(i + gc->irq_base,
|
||||
bgc->reg_base + AT91_AIC5_SSR);
|
||||
if (mask & gc->wake_active)
|
||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR);
|
||||
else
|
||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
|
||||
}
|
||||
irq_gc_unlock(bgc);
|
||||
}
|
||||
|
||||
static void aic5_resume(struct irq_data *d)
|
||||
{
|
||||
struct irq_domain *domain = d->domain;
|
||||
struct irq_domain_chip_generic *dgc = domain->gc;
|
||||
struct irq_chip_generic *bgc = dgc->gc[0];
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
int i;
|
||||
u32 mask;
|
||||
|
||||
irq_gc_lock(bgc);
|
||||
for (i = 0; i < dgc->irqs_per_chip; i++) {
|
||||
mask = 1 << i;
|
||||
if ((mask & gc->mask_cache) == (mask & gc->wake_active))
|
||||
continue;
|
||||
|
||||
irq_reg_writel(i + gc->irq_base,
|
||||
bgc->reg_base + AT91_AIC5_SSR);
|
||||
if (mask & gc->mask_cache)
|
||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR);
|
||||
else
|
||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
|
||||
}
|
||||
irq_gc_unlock(bgc);
|
||||
}
|
||||
|
||||
static void aic5_pm_shutdown(struct irq_data *d)
|
||||
{
|
||||
struct irq_domain *domain = d->domain;
|
||||
struct irq_domain_chip_generic *dgc = domain->gc;
|
||||
struct irq_chip_generic *bgc = dgc->gc[0];
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
int i;
|
||||
|
||||
irq_gc_lock(bgc);
|
||||
for (i = 0; i < dgc->irqs_per_chip; i++) {
|
||||
irq_reg_writel(i + gc->irq_base,
|
||||
bgc->reg_base + AT91_AIC5_SSR);
|
||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR);
|
||||
irq_reg_writel(1, bgc->reg_base + AT91_AIC5_ICCR);
|
||||
}
|
||||
irq_gc_unlock(bgc);
|
||||
}
|
||||
#else
|
||||
#define aic5_suspend NULL
|
||||
#define aic5_resume NULL
|
||||
#define aic5_pm_shutdown NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static void __init aic5_hw_init(struct irq_domain *domain)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Perform 8 End Of Interrupt Command to make sure AIC
|
||||
* will not Lock out nIRQ
|
||||
*/
|
||||
for (i = 0; i < 8; i++)
|
||||
irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR);
|
||||
|
||||
/*
|
||||
* Spurious Interrupt ID in Spurious Vector Register.
|
||||
* When there is no current interrupt, the IRQ Vector Register
|
||||
* reads the value stored in AIC_SPU
|
||||
*/
|
||||
irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC5_SPU);
|
||||
|
||||
/* No debugging in AIC: Debug (Protect) Control Register */
|
||||
irq_reg_writel(0, gc->reg_base + AT91_AIC5_DCR);
|
||||
|
||||
/* Disable and clear all interrupts initially */
|
||||
for (i = 0; i < domain->revmap_size; i++) {
|
||||
irq_reg_writel(i, gc->reg_base + AT91_AIC5_SSR);
|
||||
irq_reg_writel(i, gc->reg_base + AT91_AIC5_SVR);
|
||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR);
|
||||
irq_reg_writel(1, gc->reg_base + AT91_AIC5_ICCR);
|
||||
}
|
||||
}
|
||||
|
||||
static int aic5_irq_domain_xlate(struct irq_domain *d,
|
||||
struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq,
|
||||
unsigned int *out_type)
|
||||
{
|
||||
struct irq_domain_chip_generic *dgc = d->gc;
|
||||
struct irq_chip_generic *gc;
|
||||
unsigned smr;
|
||||
int ret;
|
||||
|
||||
if (!dgc)
|
||||
return -EINVAL;
|
||||
|
||||
ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
|
||||
out_hwirq, out_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gc = dgc->gc[0];
|
||||
|
||||
irq_gc_lock(gc);
|
||||
irq_reg_writel(*out_hwirq, gc->reg_base + AT91_AIC5_SSR);
|
||||
smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR);
|
||||
ret = aic_common_set_priority(intspec[2], &smr);
|
||||
if (!ret)
|
||||
irq_reg_writel(intspec[2] | smr, gc->reg_base + AT91_AIC5_SMR);
|
||||
irq_gc_unlock(gc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops aic5_irq_ops = {
|
||||
.map = irq_map_generic_chip,
|
||||
.xlate = aic5_irq_domain_xlate,
|
||||
};
|
||||
|
||||
static void __init sama5d3_aic_irq_fixup(struct device_node *root)
|
||||
{
|
||||
aic_common_rtc_irq_fixup(root);
|
||||
}
|
||||
|
||||
static const struct of_device_id __initdata aic5_irq_fixups[] = {
|
||||
{ .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static int __init aic5_of_init(struct device_node *node,
|
||||
struct device_node *parent,
|
||||
int nirqs)
|
||||
{
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_domain *domain;
|
||||
int nchips;
|
||||
int i;
|
||||
|
||||
if (nirqs > NR_AIC5_IRQS)
|
||||
return -EINVAL;
|
||||
|
||||
if (aic5_domain)
|
||||
return -EEXIST;
|
||||
|
||||
domain = aic_common_of_init(node, &aic5_irq_ops, "atmel-aic5",
|
||||
nirqs);
|
||||
if (IS_ERR(domain))
|
||||
return PTR_ERR(domain);
|
||||
|
||||
aic_common_irq_fixup(aic5_irq_fixups);
|
||||
|
||||
aic5_domain = domain;
|
||||
nchips = aic5_domain->revmap_size / 32;
|
||||
for (i = 0; i < nchips; i++) {
|
||||
gc = irq_get_domain_generic_chip(domain, i * 32);
|
||||
|
||||
gc->chip_types[0].regs.eoi = AT91_AIC5_EOICR;
|
||||
gc->chip_types[0].chip.irq_mask = aic5_mask;
|
||||
gc->chip_types[0].chip.irq_unmask = aic5_unmask;
|
||||
gc->chip_types[0].chip.irq_retrigger = aic5_retrigger;
|
||||
gc->chip_types[0].chip.irq_set_type = aic5_set_type;
|
||||
gc->chip_types[0].chip.irq_suspend = aic5_suspend;
|
||||
gc->chip_types[0].chip.irq_resume = aic5_resume;
|
||||
gc->chip_types[0].chip.irq_pm_shutdown = aic5_pm_shutdown;
|
||||
}
|
||||
|
||||
aic5_hw_init(domain);
|
||||
set_handle_irq(aic5_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NR_SAMA5D3_IRQS 50
|
||||
|
||||
static int __init sama5d3_aic5_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return aic5_of_init(node, parent, NR_SAMA5D3_IRQS);
|
||||
}
|
||||
IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init);
|
@ -15,22 +15,31 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/irqchip/irq-crossbar.h>
|
||||
|
||||
#define IRQ_FREE -1
|
||||
#define IRQ_RESERVED -2
|
||||
#define IRQ_SKIP -3
|
||||
#define GIC_IRQ_START 32
|
||||
|
||||
/*
|
||||
/**
|
||||
* struct crossbar_device - crossbar device description
|
||||
* @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
|
||||
* @crossbar_base: crossbar base address
|
||||
* @register_offsets: offsets for each irq number
|
||||
* @write: register write function pointer
|
||||
*/
|
||||
struct crossbar_device {
|
||||
uint int_max;
|
||||
uint safe_map;
|
||||
uint max_crossbar_sources;
|
||||
uint *irq_map;
|
||||
void __iomem *crossbar_base;
|
||||
int *register_offsets;
|
||||
void (*write) (int, int);
|
||||
void (*write)(int, int);
|
||||
};
|
||||
|
||||
static struct crossbar_device *cb;
|
||||
@ -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]);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
cb->irq_map[i] = cb_no;
|
||||
return i;
|
||||
@ -64,19 +84,47 @@ static inline int allocate_free_irq(int cb_no)
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
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->write(hw - GIC_IRQ_START, cb->safe_map);
|
||||
}
|
||||
}
|
||||
|
||||
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 int *out_type)
|
||||
{
|
||||
unsigned long ret;
|
||||
int ret;
|
||||
int req_num = intspec[1];
|
||||
int direct_map_num;
|
||||
|
||||
ret = allocate_free_irq(intspec[1]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ERR_VALUE(ret))
|
||||
pr_err("%s: requested crossbar number %d > max %d\n",
|
||||
__func__, req_num, cb->max_crossbar_sources);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = get_prev_map_irq(req_num);
|
||||
if (ret >= 0)
|
||||
goto found;
|
||||
|
||||
ret = allocate_free_irq(req_num);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
found:
|
||||
*out_hwirq = ret + GIC_IRQ_START;
|
||||
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,
|
||||
.unmap = crossbar_domain_unmap,
|
||||
.xlate = crossbar_domain_xlate
|
||||
@ -104,22 +175,36 @@ const struct irq_domain_ops routable_irq_domain_ops = {
|
||||
|
||||
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;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
cb = kzalloc(sizeof(*cb), GFP_KERNEL);
|
||||
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
return ret;
|
||||
|
||||
cb->crossbar_base = of_iomap(node, 0);
|
||||
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);
|
||||
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)
|
||||
goto err2;
|
||||
goto err_base;
|
||||
|
||||
cb->int_max = max;
|
||||
|
||||
@ -137,15 +222,35 @@ static int __init crossbar_of_init(struct device_node *node)
|
||||
i, &entry);
|
||||
if (entry > max) {
|
||||
pr_err("Invalid reserved entry\n");
|
||||
goto err3;
|
||||
ret = -EINVAL;
|
||||
goto err_irq_map;
|
||||
}
|
||||
cb->irq_map[entry] = 0;
|
||||
cb->irq_map[entry] = IRQ_RESERVED;
|
||||
}
|
||||
}
|
||||
|
||||
cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL);
|
||||
/* 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] = IRQ_SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL);
|
||||
if (!cb->register_offsets)
|
||||
goto err3;
|
||||
goto err_irq_map;
|
||||
|
||||
of_property_read_u32(node, "ti,reg-size", &size);
|
||||
|
||||
@ -161,7 +266,8 @@ static int __init crossbar_of_init(struct device_node *node)
|
||||
break;
|
||||
default:
|
||||
pr_err("Invalid reg-size property\n");
|
||||
goto err4;
|
||||
ret = -EINVAL;
|
||||
goto err_reg_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -170,25 +276,37 @@ static int __init crossbar_of_init(struct device_node *node)
|
||||
* reserved irqs. so find and store the offsets once.
|
||||
*/
|
||||
for (i = 0; i < max; i++) {
|
||||
if (!cb->irq_map[i])
|
||||
if (cb->irq_map[i] == IRQ_RESERVED)
|
||||
continue;
|
||||
|
||||
cb->register_offsets[i] = reserved;
|
||||
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);
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
err_reg_offset:
|
||||
kfree(cb->register_offsets);
|
||||
err3:
|
||||
err_irq_map:
|
||||
kfree(cb->irq_map);
|
||||
err2:
|
||||
err_base:
|
||||
iounmap(cb->crossbar_base);
|
||||
err1:
|
||||
err_cb:
|
||||
kfree(cb);
|
||||
return -ENOMEM;
|
||||
|
||||
cb = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id crossbar_match[] __initconst = {
|
||||
|
@ -49,14 +49,6 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
|
||||
handle_IRQ(irq, regs);
|
||||
}
|
||||
|
||||
static void nvic_eoi(struct irq_data *d)
|
||||
{
|
||||
/*
|
||||
* This is a no-op as end of interrupt is signaled by the exception
|
||||
* return sequence.
|
||||
*/
|
||||
}
|
||||
|
||||
static int __init nvic_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
@ -102,7 +94,10 @@ static int __init nvic_of_init(struct device_node *node,
|
||||
gc->chip_types[0].regs.disable = NVIC_ICER;
|
||||
gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
|
||||
gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
|
||||
gc->chip_types[0].chip.irq_eoi = nvic_eoi;
|
||||
/* This is a no-op as end of interrupt is signaled by the
|
||||
* exception return sequence.
|
||||
*/
|
||||
gc->chip_types[0].chip.irq_eoi = irq_gc_noop;
|
||||
|
||||
/* disable interrupts */
|
||||
writel_relaxed(~0, gc->reg_base + NVIC_ICER);
|
||||
|
182
drivers/irqchip/irq-or1k-pic.c
Normal file
182
drivers/irqchip/irq-or1k-pic.c
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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);
|
@ -19,7 +19,6 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/irqchip/spear-shirq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
@ -27,20 +26,73 @@
|
||||
|
||||
#include "irqchip.h"
|
||||
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
/*
|
||||
* struct spear_shirq: shared irq structure
|
||||
*
|
||||
* base: Base register address
|
||||
* status_reg: Status register offset for chained interrupt handler
|
||||
* mask_reg: Mask register offset for irq chip
|
||||
* mask: Mask to apply to the status register
|
||||
* virq_base: Base virtual interrupt number
|
||||
* nr_irqs: Number of interrupts handled by this block
|
||||
* offset: Bit offset of the first interrupt
|
||||
* irq_chip: Interrupt controller chip used for this instance,
|
||||
* if NULL group is disabled, but accounted
|
||||
*/
|
||||
struct spear_shirq {
|
||||
void __iomem *base;
|
||||
u32 status_reg;
|
||||
u32 mask_reg;
|
||||
u32 mask;
|
||||
u32 virq_base;
|
||||
u32 nr_irqs;
|
||||
u32 offset;
|
||||
struct irq_chip *irq_chip;
|
||||
};
|
||||
|
||||
/* spear300 shared irq registers offsets and masks */
|
||||
#define SPEAR300_INT_ENB_MASK_REG 0x54
|
||||
#define SPEAR300_INT_STS_MASK_REG 0x58
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(shirq_lock);
|
||||
|
||||
static void shirq_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
|
||||
u32 val, shift = d->irq - shirq->virq_base + shirq->offset;
|
||||
u32 __iomem *reg = shirq->base + shirq->mask_reg;
|
||||
|
||||
raw_spin_lock(&shirq_lock);
|
||||
val = readl(reg) & ~(0x1 << shift);
|
||||
writel(val, reg);
|
||||
raw_spin_unlock(&shirq_lock);
|
||||
}
|
||||
|
||||
static void shirq_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
|
||||
u32 val, shift = d->irq - shirq->virq_base + shirq->offset;
|
||||
u32 __iomem *reg = shirq->base + shirq->mask_reg;
|
||||
|
||||
raw_spin_lock(&shirq_lock);
|
||||
val = readl(reg) | (0x1 << shift);
|
||||
writel(val, reg);
|
||||
raw_spin_unlock(&shirq_lock);
|
||||
}
|
||||
|
||||
static struct irq_chip shirq_chip = {
|
||||
.name = "spear-shirq",
|
||||
.irq_mask = shirq_irq_mask,
|
||||
.irq_unmask = shirq_irq_unmask,
|
||||
};
|
||||
|
||||
static struct spear_shirq spear300_shirq_ras1 = {
|
||||
.irq_nr = 9,
|
||||
.irq_bit_off = 0,
|
||||
.regs = {
|
||||
.enb_reg = SPEAR300_INT_ENB_MASK_REG,
|
||||
.status_reg = SPEAR300_INT_STS_MASK_REG,
|
||||
.clear_reg = -1,
|
||||
},
|
||||
.offset = 0,
|
||||
.nr_irqs = 9,
|
||||
.mask = ((0x1 << 9) - 1) << 0,
|
||||
.irq_chip = &shirq_chip,
|
||||
.status_reg = SPEAR300_INT_STS_MASK_REG,
|
||||
.mask_reg = SPEAR300_INT_ENB_MASK_REG,
|
||||
};
|
||||
|
||||
static struct spear_shirq *spear300_shirq_blocks[] = {
|
||||
@ -51,43 +103,35 @@ static struct spear_shirq *spear300_shirq_blocks[] = {
|
||||
#define SPEAR310_INT_STS_MASK_REG 0x04
|
||||
|
||||
static struct spear_shirq spear310_shirq_ras1 = {
|
||||
.irq_nr = 8,
|
||||
.irq_bit_off = 0,
|
||||
.regs = {
|
||||
.enb_reg = -1,
|
||||
.status_reg = SPEAR310_INT_STS_MASK_REG,
|
||||
.clear_reg = -1,
|
||||
},
|
||||
.offset = 0,
|
||||
.nr_irqs = 8,
|
||||
.mask = ((0x1 << 8) - 1) << 0,
|
||||
.irq_chip = &dummy_irq_chip,
|
||||
.status_reg = SPEAR310_INT_STS_MASK_REG,
|
||||
};
|
||||
|
||||
static struct spear_shirq spear310_shirq_ras2 = {
|
||||
.irq_nr = 5,
|
||||
.irq_bit_off = 8,
|
||||
.regs = {
|
||||
.enb_reg = -1,
|
||||
.status_reg = SPEAR310_INT_STS_MASK_REG,
|
||||
.clear_reg = -1,
|
||||
},
|
||||
.offset = 8,
|
||||
.nr_irqs = 5,
|
||||
.mask = ((0x1 << 5) - 1) << 8,
|
||||
.irq_chip = &dummy_irq_chip,
|
||||
.status_reg = SPEAR310_INT_STS_MASK_REG,
|
||||
};
|
||||
|
||||
static struct spear_shirq spear310_shirq_ras3 = {
|
||||
.irq_nr = 1,
|
||||
.irq_bit_off = 13,
|
||||
.regs = {
|
||||
.enb_reg = -1,
|
||||
.status_reg = SPEAR310_INT_STS_MASK_REG,
|
||||
.clear_reg = -1,
|
||||
},
|
||||
.offset = 13,
|
||||
.nr_irqs = 1,
|
||||
.mask = ((0x1 << 1) - 1) << 13,
|
||||
.irq_chip = &dummy_irq_chip,
|
||||
.status_reg = SPEAR310_INT_STS_MASK_REG,
|
||||
};
|
||||
|
||||
static struct spear_shirq spear310_shirq_intrcomm_ras = {
|
||||
.irq_nr = 3,
|
||||
.irq_bit_off = 14,
|
||||
.regs = {
|
||||
.enb_reg = -1,
|
||||
.status_reg = SPEAR310_INT_STS_MASK_REG,
|
||||
.clear_reg = -1,
|
||||
},
|
||||
.offset = 14,
|
||||
.nr_irqs = 3,
|
||||
.mask = ((0x1 << 3) - 1) << 14,
|
||||
.irq_chip = &dummy_irq_chip,
|
||||
.status_reg = SPEAR310_INT_STS_MASK_REG,
|
||||
};
|
||||
|
||||
static struct spear_shirq *spear310_shirq_blocks[] = {
|
||||
@ -102,50 +146,34 @@ static struct spear_shirq *spear310_shirq_blocks[] = {
|
||||
#define SPEAR320_INT_CLR_MASK_REG 0x04
|
||||
#define SPEAR320_INT_ENB_MASK_REG 0x08
|
||||
|
||||
static struct spear_shirq spear320_shirq_ras3 = {
|
||||
.offset = 0,
|
||||
.nr_irqs = 7,
|
||||
.mask = ((0x1 << 7) - 1) << 0,
|
||||
};
|
||||
|
||||
static struct spear_shirq spear320_shirq_ras1 = {
|
||||
.irq_nr = 3,
|
||||
.irq_bit_off = 7,
|
||||
.regs = {
|
||||
.enb_reg = -1,
|
||||
.status_reg = SPEAR320_INT_STS_MASK_REG,
|
||||
.clear_reg = SPEAR320_INT_CLR_MASK_REG,
|
||||
.reset_to_clear = 1,
|
||||
},
|
||||
.offset = 7,
|
||||
.nr_irqs = 3,
|
||||
.mask = ((0x1 << 3) - 1) << 7,
|
||||
.irq_chip = &dummy_irq_chip,
|
||||
.status_reg = SPEAR320_INT_STS_MASK_REG,
|
||||
};
|
||||
|
||||
static struct spear_shirq spear320_shirq_ras2 = {
|
||||
.irq_nr = 1,
|
||||
.irq_bit_off = 10,
|
||||
.regs = {
|
||||
.enb_reg = -1,
|
||||
.status_reg = SPEAR320_INT_STS_MASK_REG,
|
||||
.clear_reg = SPEAR320_INT_CLR_MASK_REG,
|
||||
.reset_to_clear = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct spear_shirq spear320_shirq_ras3 = {
|
||||
.irq_nr = 7,
|
||||
.irq_bit_off = 0,
|
||||
.invalid_irq = 1,
|
||||
.regs = {
|
||||
.enb_reg = SPEAR320_INT_ENB_MASK_REG,
|
||||
.reset_to_enb = 1,
|
||||
.status_reg = SPEAR320_INT_STS_MASK_REG,
|
||||
.clear_reg = SPEAR320_INT_CLR_MASK_REG,
|
||||
.reset_to_clear = 1,
|
||||
},
|
||||
.offset = 10,
|
||||
.nr_irqs = 1,
|
||||
.mask = ((0x1 << 1) - 1) << 10,
|
||||
.irq_chip = &dummy_irq_chip,
|
||||
.status_reg = SPEAR320_INT_STS_MASK_REG,
|
||||
};
|
||||
|
||||
static struct spear_shirq spear320_shirq_intrcomm_ras = {
|
||||
.irq_nr = 11,
|
||||
.irq_bit_off = 11,
|
||||
.regs = {
|
||||
.enb_reg = -1,
|
||||
.status_reg = SPEAR320_INT_STS_MASK_REG,
|
||||
.clear_reg = SPEAR320_INT_CLR_MASK_REG,
|
||||
.reset_to_clear = 1,
|
||||
},
|
||||
.offset = 11,
|
||||
.nr_irqs = 11,
|
||||
.mask = ((0x1 << 11) - 1) << 11,
|
||||
.irq_chip = &dummy_irq_chip,
|
||||
.status_reg = SPEAR320_INT_STS_MASK_REG,
|
||||
};
|
||||
|
||||
static struct spear_shirq *spear320_shirq_blocks[] = {
|
||||
@ -155,104 +183,46 @@ static struct spear_shirq *spear320_shirq_blocks[] = {
|
||||
&spear320_shirq_intrcomm_ras,
|
||||
};
|
||||
|
||||
static void shirq_irq_mask_unmask(struct irq_data *d, bool mask)
|
||||
{
|
||||
struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
|
||||
u32 val, offset = d->irq - shirq->irq_base;
|
||||
unsigned long flags;
|
||||
|
||||
if (shirq->regs.enb_reg == -1)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&lock, flags);
|
||||
val = readl(shirq->base + shirq->regs.enb_reg);
|
||||
|
||||
if (mask ^ shirq->regs.reset_to_enb)
|
||||
val &= ~(0x1 << shirq->irq_bit_off << offset);
|
||||
else
|
||||
val |= 0x1 << shirq->irq_bit_off << offset;
|
||||
|
||||
writel(val, shirq->base + shirq->regs.enb_reg);
|
||||
spin_unlock_irqrestore(&lock, flags);
|
||||
|
||||
}
|
||||
|
||||
static void shirq_irq_mask(struct irq_data *d)
|
||||
{
|
||||
shirq_irq_mask_unmask(d, 1);
|
||||
}
|
||||
|
||||
static void shirq_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
shirq_irq_mask_unmask(d, 0);
|
||||
}
|
||||
|
||||
static struct irq_chip shirq_chip = {
|
||||
.name = "spear-shirq",
|
||||
.irq_ack = shirq_irq_mask,
|
||||
.irq_mask = shirq_irq_mask,
|
||||
.irq_unmask = shirq_irq_unmask,
|
||||
};
|
||||
|
||||
static void shirq_handler(unsigned irq, struct irq_desc *desc)
|
||||
{
|
||||
u32 i, j, val, mask, tmp;
|
||||
struct irq_chip *chip;
|
||||
struct spear_shirq *shirq = irq_get_handler_data(irq);
|
||||
u32 pend;
|
||||
|
||||
chip = irq_get_chip(irq);
|
||||
chip->irq_ack(&desc->irq_data);
|
||||
pend = readl(shirq->base + shirq->status_reg) & shirq->mask;
|
||||
pend >>= shirq->offset;
|
||||
|
||||
mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off;
|
||||
while ((val = readl(shirq->base + shirq->regs.status_reg) &
|
||||
mask)) {
|
||||
while (pend) {
|
||||
int irq = __ffs(pend);
|
||||
|
||||
val >>= shirq->irq_bit_off;
|
||||
for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) {
|
||||
|
||||
if (!(j & val))
|
||||
continue;
|
||||
|
||||
generic_handle_irq(shirq->irq_base + i);
|
||||
|
||||
/* clear interrupt */
|
||||
if (shirq->regs.clear_reg == -1)
|
||||
continue;
|
||||
|
||||
tmp = readl(shirq->base + shirq->regs.clear_reg);
|
||||
if (shirq->regs.reset_to_clear)
|
||||
tmp &= ~(j << shirq->irq_bit_off);
|
||||
else
|
||||
tmp |= (j << shirq->irq_bit_off);
|
||||
writel(tmp, shirq->base + shirq->regs.clear_reg);
|
||||
}
|
||||
pend &= ~(0x1 << irq);
|
||||
generic_handle_irq(shirq->virq_base + irq);
|
||||
}
|
||||
chip->irq_unmask(&desc->irq_data);
|
||||
}
|
||||
|
||||
static void __init spear_shirq_register(struct spear_shirq *shirq)
|
||||
static void __init spear_shirq_register(struct spear_shirq *shirq,
|
||||
int parent_irq)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (shirq->invalid_irq)
|
||||
if (!shirq->irq_chip)
|
||||
return;
|
||||
|
||||
irq_set_chained_handler(shirq->irq, shirq_handler);
|
||||
for (i = 0; i < shirq->irq_nr; i++) {
|
||||
irq_set_chip_and_handler(shirq->irq_base + i,
|
||||
&shirq_chip, handle_simple_irq);
|
||||
set_irq_flags(shirq->irq_base + i, IRQF_VALID);
|
||||
irq_set_chip_data(shirq->irq_base + i, shirq);
|
||||
}
|
||||
irq_set_chained_handler(parent_irq, shirq_handler);
|
||||
irq_set_handler_data(parent_irq, shirq);
|
||||
|
||||
irq_set_handler_data(shirq->irq, shirq);
|
||||
for (i = 0; i < shirq->nr_irqs; i++) {
|
||||
irq_set_chip_and_handler(shirq->virq_base + i,
|
||||
shirq->irq_chip, handle_simple_irq);
|
||||
set_irq_flags(shirq->virq_base + i, IRQF_VALID);
|
||||
irq_set_chip_data(shirq->virq_base + i, shirq);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
|
||||
struct device_node *np)
|
||||
{
|
||||
int i, irq_base, hwirq = 0, irq_nr = 0;
|
||||
static struct irq_domain *shirq_domain;
|
||||
int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0;
|
||||
struct irq_domain *shirq_domain;
|
||||
void __iomem *base;
|
||||
|
||||
base = of_iomap(np, 0);
|
||||
@ -262,15 +232,15 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
|
||||
}
|
||||
|
||||
for (i = 0; i < block_nr; i++)
|
||||
irq_nr += shirq_blocks[i]->irq_nr;
|
||||
nr_irqs += shirq_blocks[i]->nr_irqs;
|
||||
|
||||
irq_base = irq_alloc_descs(-1, 0, irq_nr, 0);
|
||||
if (IS_ERR_VALUE(irq_base)) {
|
||||
virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
|
||||
if (IS_ERR_VALUE(virq_base)) {
|
||||
pr_err("%s: irq desc alloc failed\n", __func__);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0,
|
||||
shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0,
|
||||
&irq_domain_simple_ops, NULL);
|
||||
if (WARN_ON(!shirq_domain)) {
|
||||
pr_warn("%s: irq domain init failed\n", __func__);
|
||||
@ -279,41 +249,41 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
|
||||
|
||||
for (i = 0; i < block_nr; i++) {
|
||||
shirq_blocks[i]->base = base;
|
||||
shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain,
|
||||
shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain,
|
||||
hwirq);
|
||||
shirq_blocks[i]->irq = irq_of_parse_and_map(np, i);
|
||||
|
||||
spear_shirq_register(shirq_blocks[i]);
|
||||
hwirq += shirq_blocks[i]->irq_nr;
|
||||
parent_irq = irq_of_parse_and_map(np, i);
|
||||
spear_shirq_register(shirq_blocks[i], parent_irq);
|
||||
hwirq += shirq_blocks[i]->nr_irqs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_desc:
|
||||
irq_free_descs(irq_base, irq_nr);
|
||||
irq_free_descs(virq_base, nr_irqs);
|
||||
err_unmap:
|
||||
iounmap(base);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
int __init spear300_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent)
|
||||
static int __init spear300_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return shirq_init(spear300_shirq_blocks,
|
||||
ARRAY_SIZE(spear300_shirq_blocks), np);
|
||||
}
|
||||
IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init);
|
||||
|
||||
int __init spear310_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent)
|
||||
static int __init spear310_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return shirq_init(spear310_shirq_blocks,
|
||||
ARRAY_SIZE(spear310_shirq_blocks), np);
|
||||
}
|
||||
IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init);
|
||||
|
||||
int __init spear320_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent)
|
||||
static int __init spear320_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent)
|
||||
{
|
||||
return shirq_init(spear320_shirq_blocks,
|
||||
ARRAY_SIZE(spear320_shirq_blocks), np);
|
||||
|
@ -771,6 +771,8 @@ void irq_gc_eoi(struct irq_data *d);
|
||||
int irq_gc_set_wake(struct irq_data *d, unsigned int on);
|
||||
|
||||
/* Setup functions for irq_chip_generic */
|
||||
int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
|
||||
irq_hw_number_t hw_irq);
|
||||
struct irq_chip_generic *
|
||||
irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base,
|
||||
void __iomem *reg_base, irq_flow_handler_t handler);
|
||||
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* SPEAr platform shared irq layer header file
|
||||
*
|
||||
* Copyright (C) 2009-2012 ST Microelectronics
|
||||
* Viresh Kumar <viresh.linux@gmail.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __SPEAR_SHIRQ_H
|
||||
#define __SPEAR_SHIRQ_H
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* struct shirq_regs: shared irq register configuration
|
||||
*
|
||||
* enb_reg: enable register offset
|
||||
* reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt
|
||||
* status_reg: status register offset
|
||||
* status_reg_mask: status register valid mask
|
||||
* clear_reg: clear register offset
|
||||
* reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt
|
||||
*/
|
||||
struct shirq_regs {
|
||||
u32 enb_reg;
|
||||
u32 reset_to_enb;
|
||||
u32 status_reg;
|
||||
u32 clear_reg;
|
||||
u32 reset_to_clear;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct spear_shirq: shared irq structure
|
||||
*
|
||||
* irq: hardware irq number
|
||||
* irq_base: base irq in linux domain
|
||||
* irq_nr: no. of shared interrupts in a particular block
|
||||
* irq_bit_off: starting bit offset in the status register
|
||||
* invalid_irq: irq group is currently disabled
|
||||
* base: base address of shared irq register
|
||||
* regs: register configuration for shared irq block
|
||||
*/
|
||||
struct spear_shirq {
|
||||
u32 irq;
|
||||
u32 irq_base;
|
||||
u32 irq_nr;
|
||||
u32 irq_bit_off;
|
||||
int invalid_irq;
|
||||
void __iomem *base;
|
||||
struct shirq_regs regs;
|
||||
};
|
||||
|
||||
int __init spear300_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent);
|
||||
int __init spear310_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent);
|
||||
int __init spear320_shirq_of_init(struct device_node *np,
|
||||
struct device_node *parent);
|
||||
|
||||
#endif /* __SPEAR_SHIRQ_H */
|
@ -172,6 +172,8 @@ extern int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
|
||||
extern void irq_domain_associate_many(struct irq_domain *domain,
|
||||
unsigned int irq_base,
|
||||
irq_hw_number_t hwirq_base, int count);
|
||||
extern void irq_domain_disassociate(struct irq_domain *domain,
|
||||
unsigned int irq);
|
||||
|
||||
extern unsigned int irq_create_mapping(struct irq_domain *host,
|
||||
irq_hw_number_t hwirq);
|
||||
|
@ -341,8 +341,8 @@ static struct lock_class_key irq_nested_lock_class;
|
||||
/*
|
||||
* irq_map_generic_chip - Map a generic chip for an irq domain
|
||||
*/
|
||||
static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
|
||||
irq_hw_number_t hw_irq)
|
||||
int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
|
||||
irq_hw_number_t hw_irq)
|
||||
{
|
||||
struct irq_data *data = irq_get_irq_data(virq);
|
||||
struct irq_domain_chip_generic *dgc = d->gc;
|
||||
@ -394,6 +394,7 @@ static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
|
||||
irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_map_generic_chip);
|
||||
|
||||
struct irq_domain_ops irq_generic_chip_ops = {
|
||||
.map = irq_map_generic_chip,
|
||||
|
@ -231,7 +231,7 @@ void irq_set_default_host(struct irq_domain *domain)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_set_default_host);
|
||||
|
||||
static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
|
||||
void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
|
||||
{
|
||||
struct irq_data *irq_data = irq_get_irq_data(irq);
|
||||
irq_hw_number_t hwirq;
|
||||
|
Loading…
Reference in New Issue
Block a user