提交 08d69a25 编写于 作者: L Linus Torvalds

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
  ...
...@@ -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);
}
...@@ -35,6 +35,20 @@ config ARM_VIC_NR ...@@ -35,6 +35,20 @@ config ARM_VIC_NR
The maximum number of VICs available in the system, for The maximum number of VICs available in the system, for
power management. 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 config BRCMSTB_L2_IRQ
bool bool
depends on ARM depends on ARM
...@@ -58,6 +72,10 @@ config CLPS711X_IRQCHIP ...@@ -58,6 +72,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
...@@ -19,6 +20,8 @@ obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.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_GIC_V3) += irq-gic-v3.o irq-gic-common.o
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
obj-$(CONFIG_ARM_VIC) += irq-vic.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_IMGPDC_IRQ) += irq-imgpdc.o
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
......
/*
* 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);
}
/*
* 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 */
/*
* 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);
/*
* 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 @@ ...@@ -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 = {
......
...@@ -49,14 +49,6 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs) ...@@ -49,14 +49,6 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
handle_IRQ(irq, 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, static int __init nvic_of_init(struct device_node *node,
struct device_node *parent) struct device_node *parent)
{ {
...@@ -102,7 +94,10 @@ static int __init nvic_of_init(struct device_node *node, ...@@ -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].regs.disable = NVIC_ICER;
gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; 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_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 */ /* disable interrupts */
writel_relaxed(~0, gc->reg_base + NVIC_ICER); writel_relaxed(~0, gc->reg_base + NVIC_ICER);
......
/*
* 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 @@ ...@@ -19,7 +19,6 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/irqchip/spear-shirq.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
...@@ -27,20 +26,73 @@ ...@@ -27,20 +26,73 @@
#include "irqchip.h" #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 */ /* spear300 shared irq registers offsets and masks */
#define SPEAR300_INT_ENB_MASK_REG 0x54 #define SPEAR300_INT_ENB_MASK_REG 0x54
#define SPEAR300_INT_STS_MASK_REG 0x58 #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 = { static struct spear_shirq spear300_shirq_ras1 = {
.irq_nr = 9, .offset = 0,
.irq_bit_off = 0, .nr_irqs = 9,
.regs = { .mask = ((0x1 << 9) - 1) << 0,
.enb_reg = SPEAR300_INT_ENB_MASK_REG, .irq_chip = &shirq_chip,
.status_reg = SPEAR300_INT_STS_MASK_REG, .status_reg = SPEAR300_INT_STS_MASK_REG,
.clear_reg = -1, .mask_reg = SPEAR300_INT_ENB_MASK_REG,
},
}; };
static struct spear_shirq *spear300_shirq_blocks[] = { static struct spear_shirq *spear300_shirq_blocks[] = {
...@@ -51,43 +103,35 @@ 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 #define SPEAR310_INT_STS_MASK_REG 0x04
static struct spear_shirq spear310_shirq_ras1 = { static struct spear_shirq spear310_shirq_ras1 = {
.irq_nr = 8, .offset = 0,
.irq_bit_off = 0, .nr_irqs = 8,
.regs = { .mask = ((0x1 << 8) - 1) << 0,
.enb_reg = -1, .irq_chip = &dummy_irq_chip,
.status_reg = SPEAR310_INT_STS_MASK_REG, .status_reg = SPEAR310_INT_STS_MASK_REG,
.clear_reg = -1,
},
}; };
static struct spear_shirq spear310_shirq_ras2 = { static struct spear_shirq spear310_shirq_ras2 = {
.irq_nr = 5, .offset = 8,
.irq_bit_off = 8, .nr_irqs = 5,
.regs = { .mask = ((0x1 << 5) - 1) << 8,
.enb_reg = -1, .irq_chip = &dummy_irq_chip,
.status_reg = SPEAR310_INT_STS_MASK_REG, .status_reg = SPEAR310_INT_STS_MASK_REG,
.clear_reg = -1,
},
}; };
static struct spear_shirq spear310_shirq_ras3 = { static struct spear_shirq spear310_shirq_ras3 = {
.irq_nr = 1, .offset = 13,
.irq_bit_off = 13, .nr_irqs = 1,
.regs = { .mask = ((0x1 << 1) - 1) << 13,
.enb_reg = -1, .irq_chip = &dummy_irq_chip,
.status_reg = SPEAR310_INT_STS_MASK_REG, .status_reg = SPEAR310_INT_STS_MASK_REG,
.clear_reg = -1,
},
}; };
static struct spear_shirq spear310_shirq_intrcomm_ras = { static struct spear_shirq spear310_shirq_intrcomm_ras = {
.irq_nr = 3, .offset = 14,
.irq_bit_off = 14, .nr_irqs = 3,
.regs = { .mask = ((0x1 << 3) - 1) << 14,
.enb_reg = -1, .irq_chip = &dummy_irq_chip,
.status_reg = SPEAR310_INT_STS_MASK_REG, .status_reg = SPEAR310_INT_STS_MASK_REG,
.clear_reg = -1,
},
}; };
static struct spear_shirq *spear310_shirq_blocks[] = { static struct spear_shirq *spear310_shirq_blocks[] = {
...@@ -102,50 +146,34 @@ 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_CLR_MASK_REG 0x04
#define SPEAR320_INT_ENB_MASK_REG 0x08 #define SPEAR320_INT_ENB_MASK_REG 0x08
static struct spear_shirq spear320_shirq_ras1 = { static struct spear_shirq spear320_shirq_ras3 = {
.irq_nr = 3, .offset = 0,
.irq_bit_off = 7, .nr_irqs = 7,
.regs = { .mask = ((0x1 << 7) - 1) << 0,
.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_ras2 = { static struct spear_shirq spear320_shirq_ras1 = {
.irq_nr = 1, .offset = 7,
.irq_bit_off = 10, .nr_irqs = 3,
.regs = { .mask = ((0x1 << 3) - 1) << 7,
.enb_reg = -1, .irq_chip = &dummy_irq_chip,
.status_reg = SPEAR320_INT_STS_MASK_REG, .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 = { static struct spear_shirq spear320_shirq_ras2 = {
.irq_nr = 7, .offset = 10,
.irq_bit_off = 0, .nr_irqs = 1,
.invalid_irq = 1, .mask = ((0x1 << 1) - 1) << 10,
.regs = { .irq_chip = &dummy_irq_chip,
.enb_reg = SPEAR320_INT_ENB_MASK_REG, .status_reg = SPEAR320_INT_STS_MASK_REG,
.reset_to_enb = 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_intrcomm_ras = { static struct spear_shirq spear320_shirq_intrcomm_ras = {
.irq_nr = 11, .offset = 11,
.irq_bit_off = 11, .nr_irqs = 11,
.regs = { .mask = ((0x1 << 11) - 1) << 11,
.enb_reg = -1, .irq_chip = &dummy_irq_chip,
.status_reg = SPEAR320_INT_STS_MASK_REG, .status_reg = SPEAR320_INT_STS_MASK_REG,
.clear_reg = SPEAR320_INT_CLR_MASK_REG,
.reset_to_clear = 1,
},
}; };
static struct spear_shirq *spear320_shirq_blocks[] = { static struct spear_shirq *spear320_shirq_blocks[] = {
...@@ -155,104 +183,46 @@ static struct spear_shirq *spear320_shirq_blocks[] = { ...@@ -155,104 +183,46 @@ static struct spear_shirq *spear320_shirq_blocks[] = {
&spear320_shirq_intrcomm_ras, &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) 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); struct spear_shirq *shirq = irq_get_handler_data(irq);
u32 pend;
chip = irq_get_chip(irq); pend = readl(shirq->base + shirq->status_reg) & shirq->mask;
chip->irq_ack(&desc->irq_data); pend >>= shirq->offset;
mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off;
while ((val = readl(shirq->base + shirq->regs.status_reg) &
mask)) {
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); while (pend) {
int irq = __ffs(pend);
/* clear interrupt */ pend &= ~(0x1 << irq);
if (shirq->regs.clear_reg == -1) generic_handle_irq(shirq->virq_base + irq);
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);
}
} }
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; int i;
if (shirq->invalid_irq) if (!shirq->irq_chip)
return; return;
irq_set_chained_handler(shirq->irq, shirq_handler); irq_set_chained_handler(parent_irq, shirq_handler);
for (i = 0; i < shirq->irq_nr; i++) { irq_set_handler_data(parent_irq, shirq);
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_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, static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
struct device_node *np) struct device_node *np)
{ {
int i, irq_base, hwirq = 0, irq_nr = 0; int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0;
static struct irq_domain *shirq_domain; struct irq_domain *shirq_domain;
void __iomem *base; void __iomem *base;
base = of_iomap(np, 0); base = of_iomap(np, 0);
...@@ -262,15 +232,15 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, ...@@ -262,15 +232,15 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
} }
for (i = 0; i < block_nr; i++) 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); virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
if (IS_ERR_VALUE(irq_base)) { if (IS_ERR_VALUE(virq_base)) {
pr_err("%s: irq desc alloc failed\n", __func__); pr_err("%s: irq desc alloc failed\n", __func__);
goto err_unmap; 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); &irq_domain_simple_ops, NULL);
if (WARN_ON(!shirq_domain)) { if (WARN_ON(!shirq_domain)) {
pr_warn("%s: irq domain init failed\n", __func__); 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, ...@@ -279,41 +249,41 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
for (i = 0; i < block_nr; i++) { for (i = 0; i < block_nr; i++) {
shirq_blocks[i]->base = base; 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); hwirq);
shirq_blocks[i]->irq = irq_of_parse_and_map(np, i);
spear_shirq_register(shirq_blocks[i]); parent_irq = irq_of_parse_and_map(np, i);
hwirq += shirq_blocks[i]->irq_nr; spear_shirq_register(shirq_blocks[i], parent_irq);
hwirq += shirq_blocks[i]->nr_irqs;
} }
return 0; return 0;
err_free_desc: err_free_desc:
irq_free_descs(irq_base, irq_nr); irq_free_descs(virq_base, nr_irqs);
err_unmap: err_unmap:
iounmap(base); iounmap(base);
return -ENXIO; return -ENXIO;
} }
int __init spear300_shirq_of_init(struct device_node *np, static int __init spear300_shirq_of_init(struct device_node *np,
struct device_node *parent) struct device_node *parent)
{ {
return shirq_init(spear300_shirq_blocks, return shirq_init(spear300_shirq_blocks,
ARRAY_SIZE(spear300_shirq_blocks), np); ARRAY_SIZE(spear300_shirq_blocks), np);
} }
IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init); IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init);
int __init spear310_shirq_of_init(struct device_node *np, static int __init spear310_shirq_of_init(struct device_node *np,
struct device_node *parent) struct device_node *parent)
{ {
return shirq_init(spear310_shirq_blocks, return shirq_init(spear310_shirq_blocks,
ARRAY_SIZE(spear310_shirq_blocks), np); ARRAY_SIZE(spear310_shirq_blocks), np);
} }
IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init); IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init);
int __init spear320_shirq_of_init(struct device_node *np, static int __init spear320_shirq_of_init(struct device_node *np,
struct device_node *parent) struct device_node *parent)
{ {
return shirq_init(spear320_shirq_blocks, return shirq_init(spear320_shirq_blocks,
ARRAY_SIZE(spear320_shirq_blocks), np); ARRAY_SIZE(spear320_shirq_blocks), np);
......
...@@ -771,6 +771,8 @@ void irq_gc_eoi(struct irq_data *d); ...@@ -771,6 +771,8 @@ void irq_gc_eoi(struct irq_data *d);
int irq_gc_set_wake(struct irq_data *d, unsigned int on); int irq_gc_set_wake(struct irq_data *d, unsigned int on);
/* Setup functions for irq_chip_generic */ /* 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 * struct irq_chip_generic *
irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base, irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base,
void __iomem *reg_base, irq_flow_handler_t handler); void __iomem *reg_base, irq_flow_handler_t handler);
......
/*
* 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, ...@@ -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, extern void irq_domain_associate_many(struct irq_domain *domain,
unsigned int irq_base, unsigned int irq_base,
irq_hw_number_t hwirq_base, int count); 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, extern unsigned int irq_create_mapping(struct irq_domain *host,
irq_hw_number_t hwirq); irq_hw_number_t hwirq);
......
...@@ -341,8 +341,8 @@ static struct lock_class_key irq_nested_lock_class; ...@@ -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 * 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, int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw_irq) irq_hw_number_t hw_irq)
{ {
struct irq_data *data = irq_get_irq_data(virq); struct irq_data *data = irq_get_irq_data(virq);
struct irq_domain_chip_generic *dgc = d->gc; 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, ...@@ -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); irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(irq_map_generic_chip);
struct irq_domain_ops irq_generic_chip_ops = { struct irq_domain_ops irq_generic_chip_ops = {
.map = irq_map_generic_chip, .map = irq_map_generic_chip,
......
...@@ -231,7 +231,7 @@ void irq_set_default_host(struct irq_domain *domain) ...@@ -231,7 +231,7 @@ void irq_set_default_host(struct irq_domain *domain)
} }
EXPORT_SYMBOL_GPL(irq_set_default_host); 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); struct irq_data *irq_data = irq_get_irq_data(irq);
irq_hw_number_t hwirq; irq_hw_number_t hwirq;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册