Looks like people wanted these merged via the omap tree as it's the only user for the GIC crossbar. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.15 (GNU/Linux) iQIcBAABAgAGBQJTE7tJAAoJEBvUPslcq6VzdnQP/i+SLcdTcG6osw8mSoiodK3n BC2/ByQBzI5Q2u3CrISqayPX7lpCP4XWABJ9eEYOC9S5CVda7SjW3nobH764HBre 7y5fRg2OV5kRZZbvS66akcuMys2iwS3ExTZfn6W1ZKgIckqd0t2Q/7ds3mrgVFwv NzI5qEgHjHyNW2dNaVqW+7RblXbyRi8A1VGZofVduBbS2bxq7GPUWNM6CaFYW7aK 8ioYo6sMATUztvqCI/JbNnIWUZV/pfgZXeBYuO5nWgxY/EVd+m2CBMaBKD2bP+Z7 gdzRGEpVqKMZzeo8E10vJML0cLVq53PfBnobEjXFFXgR2Lt63KOsgZov4iHmIIrH FAccTryFfcsD30yunygPLjyYYsOcQEgMGK4aSRiGfmKJS5fxKgIaeBcr8wL9x3ac k3oThe9c19O2jt+sLN0ZVrG7y59th3t4a+mZ9AMFIEjrFm7ExDZ+NOhyLfx7LKsM dKO+FD0sXsRgCdFZXgC/nmSgE9t3pqKotTrPthZY3rivZan0mspdIJzkaU7TEqSw EqThl55cqpexlUfB7YwxsfmJ7y1O2Bxk3ShGhxZ+Wwfhgm8QDeH8VEaACfmkSukq NaNAYdi2yEV8HydXgsd5XhBazGN2ju3fT+/gqFjOKqT8zJrJI7QkDiNH1QcOTTAb XbKBumhC3ClwyFNlfhvx =MLEE -----END PGP SIGNATURE----- Merge tag 'omap-for-v3.15/crossbar-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/drivers Merge OMAP crossbar support from Tony Lindgren: Add support for GIC crossbar that routes interrupts on newer omaps. Looks like people wanted these merged via the omap tree as it's the only user for the GIC crossbar. * tag 'omap-for-v3.15/crossbar-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap: ARM: DRA: Enable Crossbar IP support for DRA7XX ARM: OMAP4+: Correct Wakeup-gen code to use physical irq number DRIVERS: IRQCHIP: CROSSBAR: Add support for Crossbar IP DRIVERS: IRQCHIP: IRQ-GIC: Add support for routable irqs Signed-off-by: Olof Johansson <olof@lixom.net>tirimbino
commit
63261d76c8
@ -0,0 +1,27 @@ |
|||||||
|
Some socs have a large number of interrupts requests to service |
||||||
|
the needs of its many peripherals and subsystems. All of the |
||||||
|
interrupt lines from the subsystems are not needed at the same |
||||||
|
time, so they have to be muxed to the irq-controller appropriately. |
||||||
|
In such places a interrupt controllers are preceded by an CROSSBAR |
||||||
|
that provides flexibility in muxing the device requests to the controller |
||||||
|
inputs. |
||||||
|
|
||||||
|
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,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 |
||||||
|
crossbar. These interrupt lines are reserved in the soc, |
||||||
|
so crossbar bar driver should not consider them as free |
||||||
|
lines. |
||||||
|
|
||||||
|
Examples: |
||||||
|
crossbar_mpu: @4a020000 { |
||||||
|
compatible = "ti,irq-crossbar"; |
||||||
|
reg = <0x4a002a48 0x130>; |
||||||
|
ti,max-irqs = <160>; |
||||||
|
ti,reg-size = <2>; |
||||||
|
ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; |
||||||
|
}; |
@ -0,0 +1,208 @@ |
|||||||
|
/*
|
||||||
|
* drivers/irqchip/irq-crossbar.c |
||||||
|
* |
||||||
|
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
|
||||||
|
* Author: Sricharan R <r.sricharan@ti.com> |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License version 2 as |
||||||
|
* published by the Free Software Foundation. |
||||||
|
* |
||||||
|
*/ |
||||||
|
#include <linux/err.h> |
||||||
|
#include <linux/io.h> |
||||||
|
#include <linux/of_address.h> |
||||||
|
#include <linux/of_irq.h> |
||||||
|
#include <linux/slab.h> |
||||||
|
#include <linux/irqchip/arm-gic.h> |
||||||
|
|
||||||
|
#define IRQ_FREE -1 |
||||||
|
#define GIC_IRQ_START 32 |
||||||
|
|
||||||
|
/*
|
||||||
|
* @int_max: maximum number of supported interrupts |
||||||
|
* @irq_map: array of interrupts to crossbar number mapping |
||||||
|
* @crossbar_base: crossbar base address |
||||||
|
* @register_offsets: offsets for each irq number |
||||||
|
*/ |
||||||
|
struct crossbar_device { |
||||||
|
uint int_max; |
||||||
|
uint *irq_map; |
||||||
|
void __iomem *crossbar_base; |
||||||
|
int *register_offsets; |
||||||
|
void (*write) (int, int); |
||||||
|
}; |
||||||
|
|
||||||
|
static struct crossbar_device *cb; |
||||||
|
|
||||||
|
static inline void crossbar_writel(int irq_no, int cb_no) |
||||||
|
{ |
||||||
|
writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); |
||||||
|
} |
||||||
|
|
||||||
|
static inline void crossbar_writew(int irq_no, int cb_no) |
||||||
|
{ |
||||||
|
writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); |
||||||
|
} |
||||||
|
|
||||||
|
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 allocate_free_irq(int cb_no) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
|
||||||
|
for (i = 0; i < cb->int_max; i++) { |
||||||
|
if (cb->irq_map[i] == IRQ_FREE) { |
||||||
|
cb->irq_map[i] = cb_no; |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
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]); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; |
||||||
|
} |
||||||
|
|
||||||
|
static int crossbar_domain_xlate(struct irq_domain *d, |
||||||
|
struct device_node *controller, |
||||||
|
const u32 *intspec, unsigned int intsize, |
||||||
|
unsigned long *out_hwirq, |
||||||
|
unsigned int *out_type) |
||||||
|
{ |
||||||
|
unsigned long ret; |
||||||
|
|
||||||
|
ret = allocate_free_irq(intspec[1]); |
||||||
|
|
||||||
|
if (IS_ERR_VALUE(ret)) |
||||||
|
return ret; |
||||||
|
|
||||||
|
*out_hwirq = ret + GIC_IRQ_START; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
const struct irq_domain_ops routable_irq_domain_ops = { |
||||||
|
.map = crossbar_domain_map, |
||||||
|
.unmap = crossbar_domain_unmap, |
||||||
|
.xlate = crossbar_domain_xlate |
||||||
|
}; |
||||||
|
|
||||||
|
static int __init crossbar_of_init(struct device_node *node) |
||||||
|
{ |
||||||
|
int i, size, max, reserved = 0, entry; |
||||||
|
const __be32 *irqsr; |
||||||
|
|
||||||
|
cb = kzalloc(sizeof(struct cb_device *), GFP_KERNEL); |
||||||
|
|
||||||
|
if (!cb) |
||||||
|
return -ENOMEM; |
||||||
|
|
||||||
|
cb->crossbar_base = of_iomap(node, 0); |
||||||
|
if (!cb->crossbar_base) |
||||||
|
goto err1; |
||||||
|
|
||||||
|
of_property_read_u32(node, "ti,max-irqs", &max); |
||||||
|
cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); |
||||||
|
if (!cb->irq_map) |
||||||
|
goto err2; |
||||||
|
|
||||||
|
cb->int_max = max; |
||||||
|
|
||||||
|
for (i = 0; i < max; i++) |
||||||
|
cb->irq_map[i] = IRQ_FREE; |
||||||
|
|
||||||
|
/* Get and mark reserved irqs */ |
||||||
|
irqsr = of_get_property(node, "ti,irqs-reserved", &size); |
||||||
|
if (irqsr) { |
||||||
|
size /= sizeof(__be32); |
||||||
|
|
||||||
|
for (i = 0; i < size; i++) { |
||||||
|
of_property_read_u32_index(node, |
||||||
|
"ti,irqs-reserved", |
||||||
|
i, &entry); |
||||||
|
if (entry > max) { |
||||||
|
pr_err("Invalid reserved entry\n"); |
||||||
|
goto err3; |
||||||
|
} |
||||||
|
cb->irq_map[entry] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); |
||||||
|
if (!cb->register_offsets) |
||||||
|
goto err3; |
||||||
|
|
||||||
|
of_property_read_u32(node, "ti,reg-size", &size); |
||||||
|
|
||||||
|
switch (size) { |
||||||
|
case 1: |
||||||
|
cb->write = crossbar_writeb; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
cb->write = crossbar_writew; |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
cb->write = crossbar_writel; |
||||||
|
break; |
||||||
|
default: |
||||||
|
pr_err("Invalid reg-size property\n"); |
||||||
|
goto err4; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Register offsets are not linear because of the |
||||||
|
* reserved irqs. so find and store the offsets once. |
||||||
|
*/ |
||||||
|
for (i = 0; i < max; i++) { |
||||||
|
if (!cb->irq_map[i]) |
||||||
|
continue; |
||||||
|
|
||||||
|
cb->register_offsets[i] = reserved; |
||||||
|
reserved += size; |
||||||
|
} |
||||||
|
|
||||||
|
register_routable_domain_ops(&routable_irq_domain_ops); |
||||||
|
return 0; |
||||||
|
|
||||||
|
err4: |
||||||
|
kfree(cb->register_offsets); |
||||||
|
err3: |
||||||
|
kfree(cb->irq_map); |
||||||
|
err2: |
||||||
|
iounmap(cb->crossbar_base); |
||||||
|
err1: |
||||||
|
kfree(cb); |
||||||
|
return -ENOMEM; |
||||||
|
} |
||||||
|
|
||||||
|
static const struct of_device_id crossbar_match[] __initconst = { |
||||||
|
{ .compatible = "ti,irq-crossbar" }, |
||||||
|
{} |
||||||
|
}; |
||||||
|
|
||||||
|
int __init irqcrossbar_init(void) |
||||||
|
{ |
||||||
|
struct device_node *np; |
||||||
|
np = of_find_matching_node(NULL, crossbar_match); |
||||||
|
if (!np) |
||||||
|
return -ENODEV; |
||||||
|
|
||||||
|
crossbar_of_init(np); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
/*
|
||||||
|
* drivers/irqchip/irq-crossbar.h |
||||||
|
* |
||||||
|
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
|
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License version 2 as |
||||||
|
* published by the Free Software Foundation. |
||||||
|
* |
||||||
|
*/ |
||||||
|
int irqcrossbar_init(void); |
Loading…
Reference in new issue