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