ARM: EXYNOS: Add device tree support for interrupt combiner
Add device tree based instantiation of the interrupt combiner controller. Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org> Acked-by: Rob Herring <rob.herring@calxeda.com> Acked-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
This commit is contained in:
parent
1e60bc0b5f
commit
e873a47c4e
|
@ -0,0 +1,52 @@
|
|||
* Samsung Exynos Interrupt Combiner Controller
|
||||
|
||||
Samsung's Exynos4 architecture includes a interrupt combiner controller which
|
||||
can combine interrupt sources as a group and provide a single interrupt request
|
||||
for the group. The interrupt request from each group are connected to a parent
|
||||
interrupt controller, such as GIC in case of Exynos4210.
|
||||
|
||||
The interrupt combiner controller consists of multiple combiners. Upto eight
|
||||
interrupt sources can be connected to a combiner. The combiner outputs one
|
||||
combined interrupt for its eight interrupt sources. The combined interrupt
|
||||
is usually connected to a parent interrupt controller.
|
||||
|
||||
A single node in the device tree is used to describe the interrupt combiner
|
||||
controller module (which includes multiple combiners). A combiner in the
|
||||
interrupt controller module shares config/control registers with other
|
||||
combiners. For example, a 32-bit interrupt enable/disable config register
|
||||
can accommodate upto 4 interrupt combiners (with each combiner supporting
|
||||
upto 8 interrupt sources).
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "samsung,exynos4210-combiner".
|
||||
- interrupt-controller: Identifies the node as an interrupt controller.
|
||||
- #interrupt-cells: should be <2>. The meaning of the cells are
|
||||
* First Cell: Combiner Group Number.
|
||||
* Second Cell: Interrupt number within the group.
|
||||
- reg: Base address and size of interrupt combiner registers.
|
||||
- interrupts: The list of interrupts generated by the combiners which are then
|
||||
connected to a parent interrupt controller. The format of the interrupt
|
||||
specifier depends in the interrupt parent controller.
|
||||
|
||||
Optional properties:
|
||||
- samsung,combiner-nr: The number of interrupt combiners supported. If this
|
||||
property is not specified, the default number of combiners is assumed
|
||||
to be 16.
|
||||
- interrupt-parent: pHandle of the parent interrupt controller, if not
|
||||
inherited from the parent node.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
The following is a an example from the Exynos4210 SoC dtsi file.
|
||||
|
||||
combiner:interrupt-controller@10440000 {
|
||||
compatible = "samsung,exynos4210-combiner";
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
reg = <0x10440000 0x1000>;
|
||||
interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>,
|
||||
<0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>,
|
||||
<0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>,
|
||||
<0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>;
|
||||
};
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/exception.h>
|
||||
|
@ -490,6 +491,35 @@ static void __init combiner_init_one(unsigned int combiner_nr,
|
|||
base + COMBINER_ENABLE_CLEAR);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int combiner_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)
|
||||
{
|
||||
if (d->of_node != controller)
|
||||
return -EINVAL;
|
||||
|
||||
if (intsize < 2)
|
||||
return -EINVAL;
|
||||
|
||||
*out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1];
|
||||
*out_type = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int combiner_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)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
|
@ -501,16 +531,26 @@ static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
|||
}
|
||||
|
||||
static struct irq_domain_ops combiner_irq_domain_ops = {
|
||||
.xlate = combiner_irq_domain_xlate,
|
||||
.map = combiner_irq_domain_map,
|
||||
};
|
||||
|
||||
void __init combiner_init(void __iomem *combiner_base, struct device_node *np)
|
||||
{
|
||||
int i, irq_base;
|
||||
int i, irq, irq_base;
|
||||
unsigned int max_nr, nr_irq;
|
||||
|
||||
max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR :
|
||||
EXYNOS4_MAX_COMBINER_NR;
|
||||
if (np) {
|
||||
if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
|
||||
pr_warning("%s: number of combiners not specified, "
|
||||
"setting default as %d.\n",
|
||||
__func__, EXYNOS4_MAX_COMBINER_NR);
|
||||
max_nr = EXYNOS4_MAX_COMBINER_NR;
|
||||
}
|
||||
} else {
|
||||
max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR :
|
||||
EXYNOS4_MAX_COMBINER_NR;
|
||||
}
|
||||
nr_irq = max_nr * MAX_IRQ_IN_COMBINER;
|
||||
|
||||
irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0);
|
||||
|
@ -528,13 +568,31 @@ void __init combiner_init(void __iomem *combiner_base, struct device_node *np)
|
|||
|
||||
for (i = 0; i < max_nr; i++) {
|
||||
combiner_init_one(i, combiner_base + (i >> 2) * 0x10);
|
||||
combiner_cascade_irq(i, IRQ_SPI(i));
|
||||
irq = np ? irq_of_parse_and_map(np, i) : IRQ_SPI(i);
|
||||
combiner_cascade_irq(i, irq);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
int __init combiner_of_init(struct device_node *np, struct device_node *parent)
|
||||
{
|
||||
void __iomem *combiner_base;
|
||||
|
||||
combiner_base = of_iomap(np, 0);
|
||||
if (!combiner_base) {
|
||||
pr_err("%s: failed to map combiner registers\n", __func__);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
combiner_init(combiner_base, np);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id exynos4_dt_irq_match[] = {
|
||||
{ .compatible = "arm,cortex-a9-gic", .data = gic_of_init, },
|
||||
{ .compatible = "samsung,exynos4210-combiner",
|
||||
.data = combiner_of_init, },
|
||||
{},
|
||||
};
|
||||
#endif
|
||||
|
@ -552,7 +610,8 @@ void __init exynos4_init_irq(void)
|
|||
of_irq_init(exynos4_dt_irq_match);
|
||||
#endif
|
||||
|
||||
combiner_init(S5P_VA_COMBINER_BASE, NULL);
|
||||
if (!of_have_populated_dt())
|
||||
combiner_init(S5P_VA_COMBINER_BASE, NULL);
|
||||
|
||||
/*
|
||||
* The parameters of s5p_init_irq() are for VIC init.
|
||||
|
@ -567,8 +626,6 @@ void __init exynos5_init_irq(void)
|
|||
#ifdef CONFIG_OF
|
||||
of_irq_init(exynos4_dt_irq_match);
|
||||
#endif
|
||||
combiner_init(S5P_VA_COMBINER_BASE, NULL);
|
||||
|
||||
/*
|
||||
* The parameters of s5p_init_irq() are for VIC init.
|
||||
* Theses parameters should be NULL and 0 because EXYNOS4
|
||||
|
|
Loading…
Reference in New Issue