Commit 006e983b authored by Sricharan R's avatar Sricharan R

DRIVERS: IRQCHIP: IRQ-GIC: Add support for routable irqs

In some socs the gic can be preceded by a crossbar IP which
routes the peripheral interrupts to the gic inputs. The peripheral
interrupts are associated with a fixed crossbar input line and the
crossbar routes that to one of the free gic input line.

The DT entries for peripherals provides the fixed crossbar input line
as its interrupt number and the mapping code should associate this with
a free gic input line. This patch adds the support inside the gic irqchip
to handle such routable irqs. The routable irqs are registered in a linear
domain. The registered routable domain's callback should be implemented
to get a free irq and to configure the IP to route it.

Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Rajendra Nayak <rnayak@ti.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: default avatarSricharan R <r.sricharan@ti.com>
Reviewed-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: default avatarSantosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 38dbfb59
...@@ -50,6 +50,11 @@ Optional ...@@ -50,6 +50,11 @@ Optional
regions, used when the GIC doesn't have banked registers. The offset is regions, used when the GIC doesn't have banked registers. The offset is
cpu-offset * cpu-nr. cpu-offset * cpu-nr.
- arm,routable-irqs : Total number of gic irq inputs which are not directly
connected from the peripherals, but are routed dynamically
by a crossbar/multiplexer preceding the GIC. The GIC irq
input line is assigned dynamically when the corresponding
peripheral's crossbar line is mapped.
Example: Example:
intc: interrupt-controller@fff11000 { intc: interrupt-controller@fff11000 {
...@@ -57,6 +62,7 @@ Example: ...@@ -57,6 +62,7 @@ Example:
#interrupt-cells = <3>; #interrupt-cells = <3>;
#address-cells = <1>; #address-cells = <1>;
interrupt-controller; interrupt-controller;
arm,routable-irqs = <160>;
reg = <0xfff11000 0x1000>, reg = <0xfff11000 0x1000>,
<0xfff10100 0x100>; <0xfff10100 0x100>;
}; };
......
...@@ -824,16 +824,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, ...@@ -824,16 +824,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_set_chip_and_handler(irq, &gic_chip, irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq); handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
gic_routable_irq_domain_ops->map(d, irq, hw);
} }
irq_set_chip_data(irq, d->host_data); irq_set_chip_data(irq, d->host_data);
return 0; return 0;
} }
static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
{
gic_routable_irq_domain_ops->unmap(d, irq);
}
static int gic_irq_domain_xlate(struct irq_domain *d, static int gic_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller, struct device_node *controller,
const u32 *intspec, unsigned int intsize, const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type) unsigned long *out_hwirq, unsigned int *out_type)
{ {
unsigned long ret = 0;
if (d->of_node != controller) if (d->of_node != controller)
return -EINVAL; return -EINVAL;
if (intsize < 3) if (intsize < 3)
...@@ -843,11 +852,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d, ...@@ -843,11 +852,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
*out_hwirq = intspec[1] + 16; *out_hwirq = intspec[1] + 16;
/* For SPIs, we need to add 16 more to get the GIC irq ID number */ /* For SPIs, we need to add 16 more to get the GIC irq ID number */
if (!intspec[0]) if (!intspec[0]) {
*out_hwirq += 16; ret = gic_routable_irq_domain_ops->xlate(d, controller,
intspec,
intsize,
out_hwirq,
out_type);
if (IS_ERR_VALUE(ret))
return ret;
}
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
return 0;
return ret;
} }
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
...@@ -871,9 +889,41 @@ static struct notifier_block gic_cpu_notifier = { ...@@ -871,9 +889,41 @@ static struct notifier_block gic_cpu_notifier = {
const struct irq_domain_ops gic_irq_domain_ops = { const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map, .map = gic_irq_domain_map,
.unmap = gic_irq_domain_unmap,
.xlate = gic_irq_domain_xlate, .xlate = gic_irq_domain_xlate,
}; };
/* Default functions for routable irq domain */
static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
return 0;
}
static void gic_routable_irq_domain_unmap(struct irq_domain *d,
unsigned int irq)
{
}
static int gic_routable_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq,
unsigned int *out_type)
{
*out_hwirq += 16;
return 0;
}
const struct irq_domain_ops gic_default_routable_irq_domain_ops = {
.map = gic_routable_irq_domain_map,
.unmap = gic_routable_irq_domain_unmap,
.xlate = gic_routable_irq_domain_xlate,
};
const struct irq_domain_ops *gic_routable_irq_domain_ops =
&gic_default_routable_irq_domain_ops;
void __init gic_init_bases(unsigned int gic_nr, int irq_start, void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base, void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset, struct device_node *node) u32 percpu_offset, struct device_node *node)
...@@ -881,6 +931,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, ...@@ -881,6 +931,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
irq_hw_number_t hwirq_base; irq_hw_number_t hwirq_base;
struct gic_chip_data *gic; struct gic_chip_data *gic;
int gic_irqs, irq_base, i; int gic_irqs, irq_base, i;
int nr_routable_irqs;
BUG_ON(gic_nr >= MAX_GIC_NR); BUG_ON(gic_nr >= MAX_GIC_NR);
...@@ -946,14 +997,25 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, ...@@ -946,14 +997,25 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
gic->gic_irqs = gic_irqs; gic->gic_irqs = gic_irqs;
gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
if (IS_ERR_VALUE(irq_base)) { if (of_property_read_u32(node, "arm,routable-irqs",
WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", &nr_routable_irqs)) {
irq_start); irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
irq_base = irq_start; numa_node_id());
if (IS_ERR_VALUE(irq_base)) {
WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
irq_start);
irq_base = irq_start;
}
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
} else {
gic->domain = irq_domain_add_linear(node, nr_routable_irqs,
&gic_irq_domain_ops,
gic);
} }
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
if (WARN_ON(!gic->domain)) if (WARN_ON(!gic->domain))
return; return;
......
...@@ -93,6 +93,11 @@ int gic_get_cpu_id(unsigned int cpu); ...@@ -93,6 +93,11 @@ int gic_get_cpu_id(unsigned int cpu);
void gic_migrate_target(unsigned int new_cpu_id); void gic_migrate_target(unsigned int new_cpu_id);
unsigned long gic_get_sgir_physaddr(void); unsigned long gic_get_sgir_physaddr(void);
extern const struct irq_domain_ops *gic_routable_irq_domain_ops;
static inline void __init register_routable_domain_ops
(const struct irq_domain_ops *ops)
{
gic_routable_irq_domain_ops = ops;
}
#endif /* __ASSEMBLY */ #endif /* __ASSEMBLY */
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment