Commit 1ce7b039b5029ab698f9d64c0ad603794bc31ae7

Authored by Paul Mundt
1 parent 58ee987e2f

sh: intc: dynamic IRQ support.

This adds support for dynamic IRQ allocation/deallocation for all parts
using the SH-style vectored IRQs. While this is not inherently
INTC-specific, the INTC code is the main tie-in for vectored IRQ
registration, and is the only place that a full view of the utilized
vector map is possible.

The implementation is fairly straightforward, implementing a flat IRQ map
where each registered vector is reserved, allowing us to scan for holes
and dynamically wire up IRQs lazily later on in the boot stage. This
piggybacks on top of sparseirq in order to make the best use of the
available vector space.

Dynamic IRQs can be used for any number of things, ranging from MSI in
the SH-X3 PCIe case down to demux vectors for board FPGAs and system
controllers that presently allocate an arbitrary range. In the latter
case, this also allows those platforms to use sparseirq without blowing
up, which brings us one step closer to enabling sparseirq as the default
for all platform and CPU combinations.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>

Showing 1 changed file with 83 additions and 1 deletions Side-by-side Diff

... ... @@ -2,6 +2,7 @@
2 2 * Shared interrupt handling code for IPR and INTC2 types of IRQs.
3 3 *
4 4 * Copyright (C) 2007, 2008 Magnus Damm
  5 + * Copyright (C) 2009 Paul Mundt
5 6 *
6 7 * Based on intc2.c and ipr.c
7 8 *
... ... @@ -24,6 +25,7 @@
24 25 #include <linux/sysdev.h>
25 26 #include <linux/list.h>
26 27 #include <linux/topology.h>
  28 +#include <linux/bitmap.h>
27 29  
28 30 #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
29 31 ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
... ... @@ -59,6 +61,20 @@
59 61  
60 62 static LIST_HEAD(intc_list);
61 63  
  64 +/*
  65 + * The intc_irq_map provides a global map of bound IRQ vectors for a
  66 + * given platform. Allocation of IRQs are either static through the CPU
  67 + * vector map, or dynamic in the case of board mux vectors or MSI.
  68 + *
  69 + * As this is a central point for all IRQ controllers on the system,
  70 + * each of the available sources are mapped out here. This combined with
  71 + * sparseirq makes it quite trivial to keep the vector map tightly packed
  72 + * when dynamically creating IRQs, as well as tying in to otherwise
  73 + * unused irq_desc positions in the sparse array.
  74 + */
  75 +static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
  76 +static DEFINE_SPINLOCK(vector_lock);
  77 +
62 78 #ifdef CONFIG_SMP
63 79 #define IS_SMP(x) x.smp
64 80 #define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c))
... ... @@ -566,6 +582,11 @@
566 582 struct intc_handle_int *hp;
567 583 unsigned int data[2], primary;
568 584  
  585 + /*
  586 + * Register the IRQ position with the global IRQ map
  587 + */
  588 + set_bit(irq, intc_irq_map);
  589 +
569 590 /* Prefer single interrupt source bitmap over other combinations:
570 591 * 1. bitmap, single interrupt source
571 592 * 2. priority, single interrupt source
572 593  
... ... @@ -844,6 +865,67 @@
844 865  
845 866 return error;
846 867 }
847   -
848 868 device_initcall(register_intc_sysdevs);
  869 +
  870 +/*
  871 + * Dynamic IRQ allocation and deallocation
  872 + */
  873 +static unsigned int create_irq_on_node(unsigned int irq_want, int node)
  874 +{
  875 + unsigned int irq = 0, new;
  876 + unsigned long flags;
  877 + struct irq_desc *desc;
  878 +
  879 + spin_lock_irqsave(&vector_lock, flags);
  880 +
  881 + /*
  882 + * First try the wanted IRQ, then scan.
  883 + */
  884 + if (test_and_set_bit(irq_want, intc_irq_map)) {
  885 + new = find_first_zero_bit(intc_irq_map, nr_irqs);
  886 + if (unlikely(new == nr_irqs))
  887 + goto out_unlock;
  888 +
  889 + desc = irq_to_desc_alloc_node(new, node);
  890 + if (unlikely(!desc)) {
  891 + pr_info("can't get irq_desc for %d\n", new);
  892 + goto out_unlock;
  893 + }
  894 +
  895 + desc = move_irq_desc(desc, node);
  896 + __set_bit(new, intc_irq_map);
  897 + irq = new;
  898 + }
  899 +
  900 +out_unlock:
  901 + spin_unlock_irqrestore(&vector_lock, flags);
  902 +
  903 + if (irq > 0)
  904 + dynamic_irq_init(irq);
  905 +
  906 + return irq;
  907 +}
  908 +
  909 +int create_irq(void)
  910 +{
  911 + int nid = cpu_to_node(smp_processor_id());
  912 + int irq;
  913 +
  914 + irq = create_irq_on_node(NR_IRQS_LEGACY, nid);
  915 + if (irq == 0)
  916 + irq = -1;
  917 +
  918 + return irq;
  919 +}
  920 +
  921 +void destroy_irq(unsigned int irq)
  922 +{
  923 + unsigned long flags;
  924 +
  925 + dynamic_irq_cleanup(irq);
  926 +
  927 + spin_lock_irqsave(&vector_lock, flags);
  928 + __clear_bit(irq, intc_irq_map);
  929 + spin_unlock_irqrestore(&vector_lock, flags);
  930 +}