summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorJason Cooper <jason@lakedaemon.net>2015-04-11 00:57:56 +0200
committerJason Cooper <jason@lakedaemon.net>2015-04-11 00:57:56 +0200
commit07c523f1493b6860662e74a3d7e1ae0e7d67d416 (patch)
tree8d8edfc4f8ba1c985e1b8ca032beadf04323cc01 /drivers/irqchip
parentMerge branch 'irqchip/stacked-tegra' into irqchip/core (diff)
parentARM: omap: convert wakeupgen to stacked domains (diff)
downloadlinux-07c523f1493b6860662e74a3d7e1ae0e7d67d416.tar.xz
linux-07c523f1493b6860662e74a3d7e1ae0e7d67d416.zip
Merge branch 'irqchip/stacked-omap' into irqchip/core
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/irq-crossbar.c210
-rw-r--r--drivers/irqchip/irq-gic.c59
2 files changed, 128 insertions, 141 deletions
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
index bbbaf5de65d2..692fe2bc8197 100644
--- a/drivers/irqchip/irq-crossbar.c
+++ b/drivers/irqchip/irq-crossbar.c
@@ -11,11 +11,12 @@
*/
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
-#include <linux/irqchip/arm-gic.h>
-#include <linux/irqchip/irq-crossbar.h>
+
+#include "irqchip.h"
#define IRQ_FREE -1
#define IRQ_RESERVED -2
@@ -24,6 +25,7 @@
/**
* struct crossbar_device - crossbar device description
+ * @lock: spinlock serializing access to @irq_map
* @int_max: maximum number of supported interrupts
* @safe_map: safe default value to initialize the crossbar
* @max_crossbar_sources: Maximum number of crossbar sources
@@ -33,6 +35,7 @@
* @write: register write function pointer
*/
struct crossbar_device {
+ raw_spinlock_t lock;
uint int_max;
uint safe_map;
uint max_crossbar_sources;
@@ -44,72 +47,101 @@ struct crossbar_device {
static struct crossbar_device *cb;
-static inline void crossbar_writel(int irq_no, int cb_no)
+static 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)
+static 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)
+static void crossbar_writeb(int irq_no, int cb_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 struct irq_chip crossbar_chip = {
+ .name = "CBAR",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_wake = irq_chip_set_wake_parent,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+#endif
+};
-static inline int allocate_free_irq(int cb_no)
+static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
+ irq_hw_number_t hwirq)
{
+ struct of_phandle_args args;
int i;
+ int err;
+ raw_spin_lock(&cb->lock);
for (i = cb->int_max - 1; i >= 0; i--) {
if (cb->irq_map[i] == IRQ_FREE) {
- cb->irq_map[i] = cb_no;
- return i;
+ cb->irq_map[i] = hwirq;
+ break;
}
}
+ raw_spin_unlock(&cb->lock);
- return -ENODEV;
-}
+ if (i < 0)
+ return -ENODEV;
-static inline bool needs_crossbar_write(irq_hw_number_t hw)
-{
- int cb_no;
+ args.np = domain->parent->of_node;
+ args.args_count = 3;
+ args.args[0] = 0; /* SPI */
+ args.args[1] = i;
+ args.args[2] = IRQ_TYPE_LEVEL_HIGH;
- 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;
- }
+ err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+ if (err)
+ cb->irq_map[i] = IRQ_FREE;
+ else
+ cb->write(i, hwirq);
- return false;
+ return err;
}
-static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hw)
+static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq,
+ unsigned int nr_irqs, void *data)
{
- if (needs_crossbar_write(hw))
- cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
+ struct of_phandle_args *args = data;
+ irq_hw_number_t hwirq;
+ int i;
+
+ if (args->args_count != 3)
+ return -EINVAL; /* Not GIC compliant */
+ if (args->args[0] != 0)
+ return -EINVAL; /* No PPI should point to this domain */
+
+ hwirq = args->args[1];
+ if ((hwirq + nr_irqs) > cb->max_crossbar_sources)
+ return -EINVAL; /* Can't deal with this */
+
+ for (i = 0; i < nr_irqs; i++) {
+ int err = allocate_gic_irq(d, virq + i, hwirq + i);
+
+ if (err)
+ return err;
+
+ irq_domain_set_hwirq_and_chip(d, virq + i, hwirq + i,
+ &crossbar_chip, NULL);
+ }
return 0;
}
/**
- * crossbar_domain_unmap - unmap a crossbar<->irq connection
- * @d: domain of irq to unmap
- * @irq: virq number
+ * crossbar_domain_free - unmap/free a crossbar<->irq connection
+ * @domain: domain of irq to unmap
+ * @virq: virq number
+ * @nr_irqs: number of irqs to free
*
* 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
@@ -117,14 +149,20 @@ static int crossbar_domain_map(struct irq_domain *d, unsigned int 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_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs)
{
- irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
+ int i;
- if (needs_crossbar_write(hw)) {
- cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
- cb->write(hw - GIC_IRQ_START, cb->safe_map);
+ raw_spin_lock(&cb->lock);
+ for (i = 0; i < nr_irqs; i++) {
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
+
+ irq_domain_reset_irq_data(d);
+ cb->irq_map[d->hwirq] = IRQ_FREE;
+ cb->write(d->hwirq, cb->safe_map);
}
+ raw_spin_unlock(&cb->lock);
}
static int crossbar_domain_xlate(struct irq_domain *d,
@@ -133,44 +171,22 @@ static int crossbar_domain_xlate(struct irq_domain *d,
unsigned long *out_hwirq,
unsigned int *out_type)
{
- 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 = get_prev_map_irq(req_num);
- if (ret >= 0)
- goto found;
-
- ret = allocate_free_irq(req_num);
-
- if (ret < 0)
- return ret;
-
-found:
- *out_hwirq = ret + GIC_IRQ_START;
+ if (d->of_node != controller)
+ return -EINVAL; /* Shouldn't happen, really... */
+ if (intsize != 3)
+ return -EINVAL; /* Not GIC compliant */
+ if (intspec[0] != 0)
+ return -EINVAL; /* No PPI should point to this domain */
+
+ *out_hwirq = intspec[1];
+ *out_type = intspec[2];
return 0;
}
-static const struct irq_domain_ops routable_irq_domain_ops = {
- .map = crossbar_domain_map,
- .unmap = crossbar_domain_unmap,
- .xlate = crossbar_domain_xlate
+static const struct irq_domain_ops crossbar_domain_ops = {
+ .alloc = crossbar_domain_alloc,
+ .free = crossbar_domain_free,
+ .xlate = crossbar_domain_xlate,
};
static int __init crossbar_of_init(struct device_node *node)
@@ -293,7 +309,8 @@ static int __init crossbar_of_init(struct device_node *node)
cb->write(i, cb->safe_map);
}
- register_routable_domain_ops(&routable_irq_domain_ops);
+ raw_spin_lock_init(&cb->lock);
+
return 0;
err_reg_offset:
@@ -309,18 +326,37 @@ err_cb:
return ret;
}
-static const struct of_device_id crossbar_match[] __initconst = {
- { .compatible = "ti,irq-crossbar" },
- {}
-};
-
-int __init irqcrossbar_init(void)
+static int __init irqcrossbar_init(struct device_node *node,
+ struct device_node *parent)
{
- struct device_node *np;
- np = of_find_matching_node(NULL, crossbar_match);
- if (!np)
+ struct irq_domain *parent_domain, *domain;
+ int err;
+
+ if (!parent) {
+ pr_err("%s: no parent, giving up\n", node->full_name);
return -ENODEV;
+ }
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("%s: unable to obtain parent domain\n", node->full_name);
+ return -ENXIO;
+ }
+
+ err = crossbar_of_init(node);
+ if (err)
+ return err;
+
+ domain = irq_domain_add_hierarchy(parent_domain, 0,
+ cb->max_crossbar_sources,
+ node, &crossbar_domain_ops,
+ NULL);
+ if (!domain) {
+ pr_err("%s: failed to allocated domain\n", node->full_name);
+ return -ENOMEM;
+ }
- crossbar_of_init(np);
return 0;
}
+
+IRQCHIP_DECLARE(ti_irqcrossbar, "ti,irq-crossbar", irqcrossbar_init);
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 50e70a8c7ec4..868983c6aa5e 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -798,15 +798,12 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
handle_fasteoi_irq, NULL, NULL);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
-
- gic_routable_irq_domain_ops->map(d, irq, hw);
}
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,
@@ -825,16 +822,8 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
*out_hwirq = intspec[1] + 16;
/* For SPIs, we need to add 16 more to get the GIC irq ID number */
- if (!intspec[0]) {
- ret = gic_routable_irq_domain_ops->xlate(d, controller,
- intspec,
- intsize,
- out_hwirq,
- out_type);
-
- if (IS_ERR_VALUE(ret))
- return ret;
- }
+ if (!intspec[0])
+ *out_hwirq += 16;
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
@@ -891,37 +880,6 @@ static const struct irq_domain_ops gic_irq_domain_ops = {
.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;
-}
-
-static 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 __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset, struct device_node *node)
@@ -929,7 +887,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
int gic_irqs, irq_base, i;
- int nr_routable_irqs;
BUG_ON(gic_nr >= MAX_GIC_NR);
@@ -985,15 +942,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
gic->gic_irqs = gic_irqs;
if (node) { /* DT case */
- const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops;
-
- if (!of_property_read_u32(node, "arm,routable-irqs",
- &nr_routable_irqs)) {
- ops = &gic_irq_domain_ops;
- gic_irqs = nr_routable_irqs;
- }
-
- gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic);
+ gic->domain = irq_domain_add_linear(node, gic_irqs,
+ &gic_irq_domain_hierarchy_ops,
+ gic);
} else { /* Non-DT case */
/*
* For primary GICs, skip over SGIs.