[RFC] irq: Migrate powerpc virq subsystem into generic code
Lorenzo Pieralisi
Lorenzo.Pieralisi at arm.com
Fri Oct 1 22:44:10 EST 2010
Hi Grant, Ben, all
> -----Original Message-----
> From: devicetree-discuss-
> bounces+lorenzo.pieralisi=arm.com at lists.ozlabs.org [mailto:devicetree-
> discuss-bounces+lorenzo.pieralisi=arm.com at lists.ozlabs.org] On Behalf
> Of Grant Likely
> Sent: 22 September 2010 21:33
> To: benh at kernel.crashing.org; devicetree-discuss at lists.ozlabs.org;
> linuxppc-dev at lists.ozlabs.org
> Subject: [RFC] irq: Migrate powerpc virq subsystem into generic code
>
> Being able to dynamically manage linux irq ranges is useful. Migrate
> the powerpc virq code into common code so that other architectures can
> use it.
>
> This patch also removes the unused irq_early_init() references.
>
> Signed-off-by: Grant Likely <grant.likely at secretlab.ca>
> ---
> Only compile tested; but I wanted to get this out for comments. I
> think this is the right set of routines to generalize for virq on
> other architectures.
>
> g.
I have a question on the PowerPC IRQ layer and how to use it
for device drivers and device tree in general.
A device such as eth smsc911x (example) requires the platform_data
pointer to specify irq sense/level to programme the chip accordingly.
As agreed with Grant these pieces of information should be retrieved
from the device tree, from the interrupt-specifier.
Now: the device driver should be interrupt-controller agnostic, so
I cannot just retrieve the interrupts property at probe and decode it
in order to get the interrupt sense/level bits (the driver has no clue
about the interrupt-controller irq flags encoding).
I need to associate a irq_host to the interrupt controller node, with
a proper xlate function to correctly decode the interrupt-specifier
and set the irq type accordingly (irq_create_of_mapping(),
for now 1:1 on ARM, so useless from this standpoint).
At platform device init time, of_irq_to_resource() is called to parse
and map irqs; if we code the irq_host correctly for the ARM GIC for
instance the xlate function gets called and irq type set accordingly
(and maybe the function could set platform_device IRQ resource
flags as well ?)
At driver dt probe, from the hwirq number defined in "interrupts"
the driver retrieves the virq, hence sense/level flags and use them,
or just use the platform_device IRQ resource flags if set properly
by the OF layer.
Correct ?
If yes I will have a stab at it on a ARM platform with complex
IRQ routing.
Thank you very much.
Cheers,
Lorenzo
>
> arch/microblaze/kernel/setup.c | 2
> arch/powerpc/Kconfig | 3
> arch/powerpc/include/asm/irq.h | 270 ----------------
> arch/powerpc/kernel/irq.c | 659 --------------------------------
> ------
> include/linux/virq.h | 302 ++++++++++++++++++
> kernel/irq/Makefile | 1
> kernel/irq/virq.c | 687
> ++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 995 insertions(+), 929 deletions(-)
> create mode 100644 include/linux/virq.h
> create mode 100644 kernel/irq/virq.c
>
> diff --git a/arch/microblaze/kernel/setup.c
> b/arch/microblaze/kernel/setup.c
> index f5f7688..39cf20d 100644
> --- a/arch/microblaze/kernel/setup.c
> +++ b/arch/microblaze/kernel/setup.c
> @@ -51,8 +51,6 @@ void __init setup_arch(char **cmdline_p)
>
> unflatten_device_tree();
>
> - /* NOTE I think that this function is not necessary to call */
> - /* irq_early_init(); */
> setup_cpuinfo();
>
> microblaze_cache_init();
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 631e5a0..cc06e59 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -146,6 +146,9 @@ config EARLY_PRINTK
> bool
> default y
>
> +config VIRQ
> + def_bool y
> +
> config COMPAT
> bool
> default y if PPC64
> diff --git a/arch/powerpc/include/asm/irq.h
> b/arch/powerpc/include/asm/irq.h
> index 67ab5fb..6dea0cb 100644
> --- a/arch/powerpc/include/asm/irq.h
> +++ b/arch/powerpc/include/asm/irq.h
> @@ -17,10 +17,6 @@
> #include <asm/atomic.h>
>
>
> -/* Define a way to iterate across irqs. */
> -#define for_each_irq(i) \
> - for ((i) = 0; (i) < NR_IRQS; ++(i))
> -
> extern atomic_t ppc_n_lost_interrupts;
>
> /* This number is used when no interrupt has been assigned */
> @@ -41,270 +37,6 @@ extern atomic_t ppc_n_lost_interrupts;
> /* Same thing, used by the generic IRQ code */
> #define NR_IRQS_LEGACY NUM_ISA_INTERRUPTS
>
> -/* This type is the placeholder for a hardware interrupt number. It
> has to
> - * be big enough to enclose whatever representation is used by a given
> - * platform.
> - */
> -typedef unsigned long irq_hw_number_t;
> -
> -/* Interrupt controller "host" data structure. This could be defined
> as a
> - * irq domain controller. That is, it handles the mapping between
> hardware
> - * and virtual interrupt numbers for a given interrupt domain. The
> host
> - * structure is generally created by the PIC code for a given PIC
> instance
> - * (though a host can cover more than one PIC if they have a flat
> number
> - * model). It's the host callbacks that are responsible for setting
> the
> - * irq_chip on a given irq_desc after it's been mapped.
> - *
> - * The host code and data structures are fairly agnostic to the fact
> that
> - * we use an open firmware device-tree. We do have references to
> struct
> - * device_node in two places: in irq_find_host() to find the host
> matching
> - * a given interrupt controller node, and of course as an argument to
> its
> - * counterpart host->ops->match() callback. However, those are treated
> as
> - * generic pointers by the core and the fact that it's actually a
> device-node
> - * pointer is purely a convention between callers and implementation.
> This
> - * code could thus be used on other architectures by replacing those
> two
> - * by some sort of arch-specific void * "token" used to identify
> interrupt
> - * controllers.
> - */
> -struct irq_host;
> -struct radix_tree_root;
> -
> -/* Functions below are provided by the host and called whenever a new
> mapping
> - * is created or an old mapping is disposed. The host can then proceed
> to
> - * whatever internal data structures management is required. It also
> needs
> - * to setup the irq_desc when returning from map().
> - */
> -struct irq_host_ops {
> - /* Match an interrupt controller device node to a host, returns
> - * 1 on a match
> - */
> - int (*match)(struct irq_host *h, struct device_node *node);
> -
> - /* Create or update a mapping between a virtual irq number and a
> hw
> - * irq number. This is called only once for a given mapping.
> - */
> - int (*map)(struct irq_host *h, unsigned int virq, irq_hw_number_t
> hw);
> -
> - /* Dispose of such a mapping */
> - void (*unmap)(struct irq_host *h, unsigned int virq);
> -
> - /* Update of such a mapping */
> - void (*remap)(struct irq_host *h, unsigned int virq,
> irq_hw_number_t hw);
> -
> - /* Translate device-tree interrupt specifier from raw format
> coming
> - * from the firmware to a irq_hw_number_t (interrupt line number)
> and
> - * type (sense) that can be passed to set_irq_type(). In the
> absence
> - * of this callback, irq_create_of_mapping() and
> irq_of_parse_and_map()
> - * will return the hw number in the first cell and IRQ_TYPE_NONE
> for
> - * the type (which amount to keeping whatever default value the
> - * interrupt controller has for that line)
> - */
> - int (*xlate)(struct irq_host *h, struct device_node *ctrler,
> - const u32 *intspec, unsigned int intsize,
> - irq_hw_number_t *out_hwirq, unsigned int *out_type);
> -};
> -
> -struct irq_host {
> - struct list_head link;
> -
> - /* type of reverse mapping technique */
> - unsigned int revmap_type;
> -#define IRQ_HOST_MAP_LEGACY 0 /* legacy 8259, gets irqs 1..15 */
> -#define IRQ_HOST_MAP_NOMAP 1 /* no fast reverse mapping */
> -#define IRQ_HOST_MAP_LINEAR 2 /* linear map of interrupts */
> -#define IRQ_HOST_MAP_TREE 3 /* radix tree */
> - union {
> - struct {
> - unsigned int size;
> - unsigned int *revmap;
> - } linear;
> - struct radix_tree_root tree;
> - } revmap_data;
> - struct irq_host_ops *ops;
> - void *host_data;
> - irq_hw_number_t inval_irq;
> -
> - /* Optional device node pointer */
> - struct device_node *of_node;
> -};
> -
> -/* The main irq map itself is an array of NR_IRQ entries containing
> the
> - * associate host and irq number. An entry with a host of NULL is
> free.
> - * An entry can be allocated if it's free, the allocator always then
> sets
> - * hwirq first to the host's invalid irq number and then fills ops.
> - */
> -struct irq_map_entry {
> - irq_hw_number_t hwirq;
> - struct irq_host *host;
> -};
> -
> -extern struct irq_map_entry irq_map[NR_IRQS];
> -
> -extern irq_hw_number_t virq_to_hw(unsigned int virq);
> -
> -/**
> - * irq_alloc_host - Allocate a new irq_host data structure
> - * @of_node: optional device-tree node of the interrupt controller
> - * @revmap_type: type of reverse mapping to use
> - * @revmap_arg: for IRQ_HOST_MAP_LINEAR linear only: size of the map
> - * @ops: map/unmap host callbacks
> - * @inval_irq: provide a hw number in that host space that is always
> invalid
> - *
> - * Allocates and initialize and irq_host structure. Note that in the
> case of
> - * IRQ_HOST_MAP_LEGACY, the map() callback will be called before this
> returns
> - * for all legacy interrupts except 0 (which is always the invalid irq
> for
> - * a legacy controller). For a IRQ_HOST_MAP_LINEAR, the map is
> allocated by
> - * this call as well. For a IRQ_HOST_MAP_TREE, the radix tree will be
> allocated
> - * later during boot automatically (the reverse mapping will use the
> slow path
> - * until that happens).
> - */
> -extern struct irq_host *irq_alloc_host(struct device_node *of_node,
> - unsigned int revmap_type,
> - unsigned int revmap_arg,
> - struct irq_host_ops *ops,
> - irq_hw_number_t inval_irq);
> -
> -
> -/**
> - * irq_find_host - Locates a host for a given device node
> - * @node: device-tree node of the interrupt controller
> - */
> -extern struct irq_host *irq_find_host(struct device_node *node);
> -
> -
> -/**
> - * irq_set_default_host - Set a "default" host
> - * @host: default host pointer
> - *
> - * For convenience, it's possible to set a "default" host that will be
> used
> - * whenever NULL is passed to irq_create_mapping(). It makes life
> easier for
> - * platforms that want to manipulate a few hard coded interrupt
> numbers that
> - * aren't properly represented in the device-tree.
> - */
> -extern void irq_set_default_host(struct irq_host *host);
> -
> -
> -/**
> - * irq_set_virq_count - Set the maximum number of virt irqs
> - * @count: number of linux virtual irqs, capped with NR_IRQS
> - *
> - * This is mainly for use by platforms like iSeries who want to
> program
> - * the virtual irq number in the controller to avoid the reverse
> mapping
> - */
> -extern void irq_set_virq_count(unsigned int count);
> -
> -
> -/**
> - * irq_create_mapping - Map a hardware interrupt into linux virq space
> - * @host: host owning this hardware interrupt or NULL for default host
> - * @hwirq: hardware irq number in that host space
> - *
> - * Only one mapping per hardware interrupt is permitted. Returns a
> linux
> - * virq number.
> - * If the sense/trigger is to be specified, set_irq_type() should be
> called
> - * on the number returned from that call.
> - */
> -extern unsigned int irq_create_mapping(struct irq_host *host,
> - irq_hw_number_t hwirq);
> -
> -
> -/**
> - * irq_dispose_mapping - Unmap an interrupt
> - * @virq: linux virq number of the interrupt to unmap
> - */
> -extern void irq_dispose_mapping(unsigned int virq);
> -
> -/**
> - * irq_find_mapping - Find a linux virq from an hw irq number.
> - * @host: host owning this hardware interrupt
> - * @hwirq: hardware irq number in that host space
> - *
> - * This is a slow path, for use by generic code. It's expected that an
> - * irq controller implementation directly calls the appropriate low
> level
> - * mapping function.
> - */
> -extern unsigned int irq_find_mapping(struct irq_host *host,
> - irq_hw_number_t hwirq);
> -
> -/**
> - * irq_create_direct_mapping - Allocate a virq for direct mapping
> - * @host: host to allocate the virq for or NULL for default host
> - *
> - * This routine is used for irq controllers which can choose the
> hardware
> - * interrupt numbers they generate. In such a case it's simplest to
> use
> - * the linux virq as the hardware interrupt number.
> - */
> -extern unsigned int irq_create_direct_mapping(struct irq_host *host);
> -
> -/**
> - * irq_radix_revmap_insert - Insert a hw irq to linux virq number
> mapping.
> - * @host: host owning this hardware interrupt
> - * @virq: linux irq number
> - * @hwirq: hardware irq number in that host space
> - *
> - * This is for use by irq controllers that use a radix tree reverse
> - * mapping for fast lookup.
> - */
> -extern void irq_radix_revmap_insert(struct irq_host *host, unsigned
> int virq,
> - irq_hw_number_t hwirq);
> -
> -/**
> - * irq_radix_revmap_lookup - Find a linux virq from a hw irq number.
> - * @host: host owning this hardware interrupt
> - * @hwirq: hardware irq number in that host space
> - *
> - * This is a fast path, for use by irq controller code that uses radix
> tree
> - * revmaps
> - */
> -extern unsigned int irq_radix_revmap_lookup(struct irq_host *host,
> - irq_hw_number_t hwirq);
> -
> -/**
> - * irq_linear_revmap - Find a linux virq from a hw irq number.
> - * @host: host owning this hardware interrupt
> - * @hwirq: hardware irq number in that host space
> - *
> - * This is a fast path, for use by irq controller code that uses
> linear
> - * revmaps. It does fallback to the slow path if the revmap doesn't
> exist
> - * yet and will create the revmap entry with appropriate locking
> - */
> -
> -extern unsigned int irq_linear_revmap(struct irq_host *host,
> - irq_hw_number_t hwirq);
> -
> -
> -
> -/**
> - * irq_alloc_virt - Allocate virtual irq numbers
> - * @host: host owning these new virtual irqs
> - * @count: number of consecutive numbers to allocate
> - * @hint: pass a hint number, the allocator will try to use a 1:1
> mapping
> - *
> - * This is a low level function that is used internally by
> irq_create_mapping()
> - * and that can be used by some irq controllers implementations for
> things
> - * like allocating ranges of numbers for MSIs. The revmaps are left
> untouched.
> - */
> -extern unsigned int irq_alloc_virt(struct irq_host *host,
> - unsigned int count,
> - unsigned int hint);
> -
> -/**
> - * irq_free_virt - Free virtual irq numbers
> - * @virq: virtual irq number of the first interrupt to free
> - * @count: number of interrupts to free
> - *
> - * This function is the opposite of irq_alloc_virt. It will not clear
> reverse
> - * maps, this should be done previously by unmap'ing the interrupt. In
> fact,
> - * all interrupts covered by the range being freed should have been
> unmapped
> - * prior to calling this.
> - */
> -extern void irq_free_virt(unsigned int virq, unsigned int count);
> -
> -/**
> - * irq_early_init - Init irq remapping subsystem
> - */
> -extern void irq_early_init(void);
> -
> static __inline__ int irq_canonicalize(int irq)
> {
> return irq;
> @@ -342,5 +74,7 @@ extern int call_handle_irq(int irq, void *p1,
> struct thread_info *tp, void *func);
> extern void do_IRQ(struct pt_regs *regs);
>
> +#include <linux/virq.h>
> +
> #endif /* _ASM_IRQ_H */
> #endif /* __KERNEL__ */
> diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
> index 4a65386..86d8e42 100644
> --- a/arch/powerpc/kernel/irq.c
> +++ b/arch/powerpc/kernel/irq.c
> @@ -523,553 +523,6 @@ void do_softirq(void)
> }
>
>
> -/*
> - * IRQ controller and virtual interrupts
> - */
> -
> -static LIST_HEAD(irq_hosts);
> -static DEFINE_RAW_SPINLOCK(irq_big_lock);
> -static unsigned int revmap_trees_allocated;
> -static DEFINE_MUTEX(revmap_trees_mutex);
> -struct irq_map_entry irq_map[NR_IRQS];
> -static unsigned int irq_virq_count = NR_IRQS;
> -static struct irq_host *irq_default_host;
> -
> -irq_hw_number_t virq_to_hw(unsigned int virq)
> -{
> - return irq_map[virq].hwirq;
> -}
> -EXPORT_SYMBOL_GPL(virq_to_hw);
> -
> -static int default_irq_host_match(struct irq_host *h, struct
> device_node *np)
> -{
> - return h->of_node != NULL && h->of_node == np;
> -}
> -
> -struct irq_host *irq_alloc_host(struct device_node *of_node,
> - unsigned int revmap_type,
> - unsigned int revmap_arg,
> - struct irq_host_ops *ops,
> - irq_hw_number_t inval_irq)
> -{
> - struct irq_host *host;
> - unsigned int size = sizeof(struct irq_host);
> - unsigned int i;
> - unsigned int *rmap;
> - unsigned long flags;
> -
> - /* Allocate structure and revmap table if using linear mapping */
> - if (revmap_type == IRQ_HOST_MAP_LINEAR)
> - size += revmap_arg * sizeof(unsigned int);
> - host = zalloc_maybe_bootmem(size, GFP_KERNEL);
> - if (host == NULL)
> - return NULL;
> -
> - /* Fill structure */
> - host->revmap_type = revmap_type;
> - host->inval_irq = inval_irq;
> - host->ops = ops;
> - host->of_node = of_node_get(of_node);
> -
> - if (host->ops->match == NULL)
> - host->ops->match = default_irq_host_match;
> -
> - raw_spin_lock_irqsave(&irq_big_lock, flags);
> -
> - /* If it's a legacy controller, check for duplicates and
> - * mark it as allocated (we use irq 0 host pointer for that
> - */
> - if (revmap_type == IRQ_HOST_MAP_LEGACY) {
> - if (irq_map[0].host != NULL) {
> - raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> - /* If we are early boot, we can't free the
structure,
> - * too bad...
> - * this will be fixed once slab is made available
> early
> - * instead of the current cruft
> - */
> - if (mem_init_done)
> - kfree(host);
> - return NULL;
> - }
> - irq_map[0].host = host;
> - }
> -
> - list_add(&host->link, &irq_hosts);
> - raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -
> - /* Additional setups per revmap type */
> - switch(revmap_type) {
> - case IRQ_HOST_MAP_LEGACY:
> - /* 0 is always the invalid number for legacy */
> - host->inval_irq = 0;
> - /* setup us as the host for all legacy interrupts */
> - for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
> - irq_map[i].hwirq = i;
> - smp_wmb();
> - irq_map[i].host = host;
> - smp_wmb();
> -
> - /* Clear norequest flags */
> - irq_to_desc(i)->status &= ~IRQ_NOREQUEST;
> -
> - /* Legacy flags are left to default at this point,
> - * one can then use irq_create_mapping() to
> - * explicitly change them
> - */
> - ops->map(host, i, i);
> - }
> - break;
> - case IRQ_HOST_MAP_LINEAR:
> - rmap = (unsigned int *)(host + 1);
> - for (i = 0; i < revmap_arg; i++)
> - rmap[i] = NO_IRQ;
> - host->revmap_data.linear.size = revmap_arg;
> - smp_wmb();
> - host->revmap_data.linear.revmap = rmap;
> - break;
> - default:
> - break;
> - }
> -
> - pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type,
> host);
> -
> - return host;
> -}
> -
> -struct irq_host *irq_find_host(struct device_node *node)
> -{
> - struct irq_host *h, *found = NULL;
> - unsigned long flags;
> -
> - /* We might want to match the legacy controller last since
> - * it might potentially be set to match all interrupts in
> - * the absence of a device node. This isn't a problem so far
> - * yet though...
> - */
> - raw_spin_lock_irqsave(&irq_big_lock, flags);
> - list_for_each_entry(h, &irq_hosts, link)
> - if (h->ops->match(h, node)) {
> - found = h;
> - break;
> - }
> - raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> - return found;
> -}
> -EXPORT_SYMBOL_GPL(irq_find_host);
> -
> -void irq_set_default_host(struct irq_host *host)
> -{
> - pr_debug("irq: Default host set to @0x%p\n", host);
> -
> - irq_default_host = host;
> -}
> -
> -void irq_set_virq_count(unsigned int count)
> -{
> - pr_debug("irq: Trying to set virq count to %d\n", count);
> -
> - BUG_ON(count < NUM_ISA_INTERRUPTS);
> - if (count < NR_IRQS)
> - irq_virq_count = count;
> -}
> -
> -static int irq_setup_virq(struct irq_host *host, unsigned int virq,
> - irq_hw_number_t hwirq)
> -{
> - struct irq_desc *desc;
> -
> - desc = irq_to_desc_alloc_node(virq, 0);
> - if (!desc) {
> - pr_debug("irq: -> allocating desc failed\n");
> - goto error;
> - }
> -
> - /* Clear IRQ_NOREQUEST flag */
> - desc->status &= ~IRQ_NOREQUEST;
> -
> - /* map it */
> - smp_wmb();
> - irq_map[virq].hwirq = hwirq;
> - smp_mb();
> -
> - if (host->ops->map(host, virq, hwirq)) {
> - pr_debug("irq: -> mapping failed, freeing\n");
> - goto error;
> - }
> -
> - return 0;
> -
> -error:
> - irq_free_virt(virq, 1);
> - return -1;
> -}
> -
> -unsigned int irq_create_direct_mapping(struct irq_host *host)
> -{
> - unsigned int virq;
> -
> - if (host == NULL)
> - host = irq_default_host;
> -
> - BUG_ON(host == NULL);
> - WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP);
> -
> - virq = irq_alloc_virt(host, 1, 0);
> - if (virq == NO_IRQ) {
> - pr_debug("irq: create_direct virq allocation failed\n");
> - return NO_IRQ;
> - }
> -
> - pr_debug("irq: create_direct obtained virq %d\n", virq);
> -
> - if (irq_setup_virq(host, virq, virq))
> - return NO_IRQ;
> -
> - return virq;
> -}
> -
> -unsigned int irq_create_mapping(struct irq_host *host,
> - irq_hw_number_t hwirq)
> -{
> - unsigned int virq, hint;
> -
> - pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq);
> -
> - /* Look for default host if nececssary */
> - if (host == NULL)
> - host = irq_default_host;
> - if (host == NULL) {
> - printk(KERN_WARNING "irq_create_mapping called for"
> - " NULL host, hwirq=%lx\n", hwirq);
> - WARN_ON(1);
> - return NO_IRQ;
> - }
> - pr_debug("irq: -> using host @%p\n", host);
> -
> - /* Check if mapping already exist, if it does, call
> - * host->ops->map() to update the flags
> - */
> - virq = irq_find_mapping(host, hwirq);
> - if (virq != NO_IRQ) {
> - if (host->ops->remap)
> - host->ops->remap(host, virq, hwirq);
> - pr_debug("irq: -> existing mapping on virq %d\n", virq);
> - return virq;
> - }
> -
> - /* Get a virtual interrupt number */
> - if (host->revmap_type == IRQ_HOST_MAP_LEGACY) {
> - /* Handle legacy */
> - virq = (unsigned int)hwirq;
> - if (virq == 0 || virq >= NUM_ISA_INTERRUPTS)
> - return NO_IRQ;
> - return virq;
> - } else {
> - /* Allocate a virtual interrupt number */
> - hint = hwirq % irq_virq_count;
> - virq = irq_alloc_virt(host, 1, hint);
> - if (virq == NO_IRQ) {
> - pr_debug("irq: -> virq allocation failed\n");
> - return NO_IRQ;
> - }
> - }
> -
> - if (irq_setup_virq(host, virq, hwirq))
> - return NO_IRQ;
> -
> - printk(KERN_DEBUG "irq: irq %lu on host %s mapped to virtual irq
> %u\n",
> - hwirq, host->of_node ? host->of_node->full_name : "null",
> virq);
> -
> - return virq;
> -}
> -EXPORT_SYMBOL_GPL(irq_create_mapping);
> -
> -unsigned int irq_create_of_mapping(struct device_node *controller,
> - const u32 *intspec, unsigned int intsize)
> -{
> - struct irq_host *host;
> - irq_hw_number_t hwirq;
> - unsigned int type = IRQ_TYPE_NONE;
> - unsigned int virq;
> -
> - if (controller == NULL)
> - host = irq_default_host;
> - else
> - host = irq_find_host(controller);
> - if (host == NULL) {
> - printk(KERN_WARNING "irq: no irq host found for %s !\n",
> - controller->full_name);
> - return NO_IRQ;
> - }
> -
> - /* If host has no translation, then we assume interrupt line */
> - if (host->ops->xlate == NULL)
> - hwirq = intspec[0];
> - else {
> - if (host->ops->xlate(host, controller, intspec, intsize,
> - &hwirq, &type))
> - return NO_IRQ;
> - }
> -
> - /* Create mapping */
> - virq = irq_create_mapping(host, hwirq);
> - if (virq == NO_IRQ)
> - return virq;
> -
> - /* Set type if specified and different than the current one */
> - if (type != IRQ_TYPE_NONE &&
> - type != (irq_to_desc(virq)->status & IRQF_TRIGGER_MASK))
> - set_irq_type(virq, type);
> - return virq;
> -}
> -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> -
> -void irq_dispose_mapping(unsigned int virq)
> -{
> - struct irq_host *host;
> - irq_hw_number_t hwirq;
> -
> - if (virq == NO_IRQ)
> - return;
> -
> - host = irq_map[virq].host;
> - WARN_ON (host == NULL);
> - if (host == NULL)
> - return;
> -
> - /* Never unmap legacy interrupts */
> - if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
> - return;
> -
> - /* remove chip and handler */
> - set_irq_chip_and_handler(virq, NULL, NULL);
> -
> - /* Make sure it's completed */
> - synchronize_irq(virq);
> -
> - /* Tell the PIC about it */
> - if (host->ops->unmap)
> - host->ops->unmap(host, virq);
> - smp_mb();
> -
> - /* Clear reverse map */
> - hwirq = irq_map[virq].hwirq;
> - switch(host->revmap_type) {
> - case IRQ_HOST_MAP_LINEAR:
> - if (hwirq < host->revmap_data.linear.size)
> - host->revmap_data.linear.revmap[hwirq] = NO_IRQ;
> - break;
> - case IRQ_HOST_MAP_TREE:
> - /*
> - * Check if radix tree allocated yet, if not then nothing
> to
> - * remove.
> - */
> - smp_rmb();
> - if (revmap_trees_allocated < 1)
> - break;
> - mutex_lock(&revmap_trees_mutex);
> - radix_tree_delete(&host->revmap_data.tree, hwirq);
> - mutex_unlock(&revmap_trees_mutex);
> - break;
> - }
> -
> - /* Destroy map */
> - smp_mb();
> - irq_map[virq].hwirq = host->inval_irq;
> -
> - /* Set some flags */
> - irq_to_desc(virq)->status |= IRQ_NOREQUEST;
> -
> - /* Free it */
> - irq_free_virt(virq, 1);
> -}
> -EXPORT_SYMBOL_GPL(irq_dispose_mapping);
> -
> -unsigned int irq_find_mapping(struct irq_host *host,
> - irq_hw_number_t hwirq)
> -{
> - unsigned int i;
> - unsigned int hint = hwirq % irq_virq_count;
> -
> - /* Look for default host if nececssary */
> - if (host == NULL)
> - host = irq_default_host;
> - if (host == NULL)
> - return NO_IRQ;
> -
> - /* legacy -> bail early */
> - if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
> - return hwirq;
> -
> - /* Slow path does a linear search of the map */
> - if (hint < NUM_ISA_INTERRUPTS)
> - hint = NUM_ISA_INTERRUPTS;
> - i = hint;
> - do {
> - if (irq_map[i].host == host &&
> - irq_map[i].hwirq == hwirq)
> - return i;
> - i++;
> - if (i >= irq_virq_count)
> - i = NUM_ISA_INTERRUPTS;
> - } while(i != hint);
> - return NO_IRQ;
> -}
> -EXPORT_SYMBOL_GPL(irq_find_mapping);
> -
> -
> -unsigned int irq_radix_revmap_lookup(struct irq_host *host,
> - irq_hw_number_t hwirq)
> -{
> - struct irq_map_entry *ptr;
> - unsigned int virq;
> -
> - WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
> -
> - /*
> - * Check if the radix tree exists and has bee initialized.
> - * If not, we fallback to slow mode
> - */
> - if (revmap_trees_allocated < 2)
> - return irq_find_mapping(host, hwirq);
> -
> - /* Now try to resolve */
> - /*
> - * No rcu_read_lock(ing) needed, the ptr returned can't go under
> us
> - * as it's referencing an entry in the static irq_map table.
> - */
> - ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
> -
> - /*
> - * If found in radix tree, then fine.
> - * Else fallback to linear lookup - this should not happen in
> practice
> - * as it means that we failed to insert the node in the radix
> tree.
> - */
> - if (ptr)
> - virq = ptr - irq_map;
> - else
> - virq = irq_find_mapping(host, hwirq);
> -
> - return virq;
> -}
> -
> -void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
> - irq_hw_number_t hwirq)
> -{
> -
> - WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
> -
> - /*
> - * Check if the radix tree exists yet.
> - * If not, then the irq will be inserted into the tree when it
> gets
> - * initialized.
> - */
> - smp_rmb();
> - if (revmap_trees_allocated < 1)
> - return;
> -
> - if (virq != NO_IRQ) {
> - mutex_lock(&revmap_trees_mutex);
> - radix_tree_insert(&host->revmap_data.tree, hwirq,
> - &irq_map[virq]);
> - mutex_unlock(&revmap_trees_mutex);
> - }
> -}
> -
> -unsigned int irq_linear_revmap(struct irq_host *host,
> - irq_hw_number_t hwirq)
> -{
> - unsigned int *revmap;
> -
> - WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR);
> -
> - /* Check revmap bounds */
> - if (unlikely(hwirq >= host->revmap_data.linear.size))
> - return irq_find_mapping(host, hwirq);
> -
> - /* Check if revmap was allocated */
> - revmap = host->revmap_data.linear.revmap;
> - if (unlikely(revmap == NULL))
> - return irq_find_mapping(host, hwirq);
> -
> - /* Fill up revmap with slow path if no mapping found */
> - if (unlikely(revmap[hwirq] == NO_IRQ))
> - revmap[hwirq] = irq_find_mapping(host, hwirq);
> -
> - return revmap[hwirq];
> -}
> -
> -unsigned int irq_alloc_virt(struct irq_host *host,
> - unsigned int count,
> - unsigned int hint)
> -{
> - unsigned long flags;
> - unsigned int i, j, found = NO_IRQ;
> -
> - if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS))
> - return NO_IRQ;
> -
> - raw_spin_lock_irqsave(&irq_big_lock, flags);
> -
> - /* Use hint for 1 interrupt if any */
> - if (count == 1 && hint >= NUM_ISA_INTERRUPTS &&
> - hint < irq_virq_count && irq_map[hint].host == NULL) {
> - found = hint;
> - goto hint_found;
> - }
> -
> - /* Look for count consecutive numbers in the allocatable
> - * (non-legacy) space
> - */
> - for (i = NUM_ISA_INTERRUPTS, j = 0; i < irq_virq_count; i++) {
> - if (irq_map[i].host != NULL)
> - j = 0;
> - else
> - j++;
> -
> - if (j == count) {
> - found = i - count + 1;
> - break;
> - }
> - }
> - if (found == NO_IRQ) {
> - raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> - return NO_IRQ;
> - }
> - hint_found:
> - for (i = found; i < (found + count); i++) {
> - irq_map[i].hwirq = host->inval_irq;
> - smp_wmb();
> - irq_map[i].host = host;
> - }
> - raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> - return found;
> -}
> -
> -void irq_free_virt(unsigned int virq, unsigned int count)
> -{
> - unsigned long flags;
> - unsigned int i;
> -
> - WARN_ON (virq < NUM_ISA_INTERRUPTS);
> - WARN_ON (count == 0 || (virq + count) > irq_virq_count);
> -
> - raw_spin_lock_irqsave(&irq_big_lock, flags);
> - for (i = virq; i < (virq + count); i++) {
> - struct irq_host *host;
> -
> - if (i < NUM_ISA_INTERRUPTS ||
> - (virq + count) > irq_virq_count)
> - continue;
> -
> - host = irq_map[i].host;
> - irq_map[i].hwirq = host->inval_irq;
> - smp_wmb();
> - irq_map[i].host = NULL;
> - }
> - raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -}
> -
> int arch_early_irq_init(void)
> {
> struct irq_desc *desc;
> @@ -1090,118 +543,6 @@ int arch_init_chip_data(struct irq_desc *desc,
> int node)
> return 0;
> }
>
> -/* We need to create the radix trees late */
> -static int irq_late_init(void)
> -{
> - struct irq_host *h;
> - unsigned int i;
> -
> - /*
> - * No mutual exclusion with respect to accessors of the tree is
> needed
> - * here as the synchronization is done via the state variable
> - * revmap_trees_allocated.
> - */
> - list_for_each_entry(h, &irq_hosts, link) {
> - if (h->revmap_type == IRQ_HOST_MAP_TREE)
> - INIT_RADIX_TREE(&h->revmap_data.tree, GFP_KERNEL);
> - }
> -
> - /*
> - * Make sure the radix trees inits are visible before setting
> - * the flag
> - */
> - smp_wmb();
> - revmap_trees_allocated = 1;
> -
> - /*
> - * Insert the reverse mapping for those interrupts already
> present
> - * in irq_map[].
> - */
> - mutex_lock(&revmap_trees_mutex);
> - for (i = 0; i < irq_virq_count; i++) {
> - if (irq_map[i].host &&
> - (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE))
> -
radix_tree_insert(&irq_map[i].host->revmap_data.tree,
> - irq_map[i].hwirq, &irq_map[i]);
> - }
> - mutex_unlock(&revmap_trees_mutex);
> -
> - /*
> - * Make sure the radix trees insertions are visible before
> setting
> - * the flag
> - */
> - smp_wmb();
> - revmap_trees_allocated = 2;
> -
> - return 0;
> -}
> -arch_initcall(irq_late_init);
> -
> -#ifdef CONFIG_VIRQ_DEBUG
> -static int virq_debug_show(struct seq_file *m, void *private)
> -{
> - unsigned long flags;
> - struct irq_desc *desc;
> - const char *p;
> - char none[] = "none";
> - int i;
> -
> - seq_printf(m, "%-5s %-7s %-15s %s\n", "virq", "hwirq",
> - "chip name", "host name");
> -
> - for (i = 1; i < nr_irqs; i++) {
> - desc = irq_to_desc(i);
> - if (!desc)
> - continue;
> -
> - raw_spin_lock_irqsave(&desc->lock, flags);
> -
> - if (desc->action && desc->action->handler) {
> - seq_printf(m, "%5d ", i);
> - seq_printf(m, "0x%05lx ", virq_to_hw(i));
> -
> - if (desc->chip && desc->chip->name)
> - p = desc->chip->name;
> - else
> - p = none;
> - seq_printf(m, "%-15s ", p);
> -
> - if (irq_map[i].host && irq_map[i].host->of_node)
> - p = irq_map[i].host->of_node->full_name;
> - else
> - p = none;
> - seq_printf(m, "%s\n", p);
> - }
> -
> - raw_spin_unlock_irqrestore(&desc->lock, flags);
> - }
> -
> - return 0;
> -}
> -
> -static int virq_debug_open(struct inode *inode, struct file *file)
> -{
> - return single_open(file, virq_debug_show, inode->i_private);
> -}
> -
> -static const struct file_operations virq_debug_fops = {
> - .open = virq_debug_open,
> - .read = seq_read,
> - .llseek = seq_lseek,
> - .release = single_release,
> -};
> -
> -static int __init irq_debugfs_init(void)
> -{
> - if (debugfs_create_file("virq_mapping", S_IRUGO,
> powerpc_debugfs_root,
> - NULL, &virq_debug_fops) == NULL)
> - return -ENOMEM;
> -
> - return 0;
> -}
> -__initcall(irq_debugfs_init);
> -#endif /* CONFIG_VIRQ_DEBUG */
> -
> #ifdef CONFIG_PPC64
> static int __init setup_noirqdistrib(char *str)
> {
> diff --git a/include/linux/virq.h b/include/linux/virq.h
> new file mode 100644
> index 0000000..06035ef
> --- /dev/null
> +++ b/include/linux/virq.h
> @@ -0,0 +1,302 @@
> +/*
> + * Virtual IRQ infrastructure
> + *
> + * Virtual IRQs provides support for dynamically allocating ranges of
> IRQ
> + * numbers for use by interrupt controllers.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + */
> +
> +
> +#ifdef __KERNEL__
> +#ifndef _LINUX_VIRQ_H
> +#define _LINUX_VIRQ_H
> +
> +#include <asm/irq.h>
> +
> +#ifdef CONFIG_VIRQ
> +
> +/* Define a way to iterate across irqs. */
> +#define for_each_irq(i) \
> + for ((i) = 0; (i) < NR_IRQS; ++(i))
> +
> +/* This type is the placeholder for a hardware interrupt number. It
> has to
> + * be big enough to enclose whatever representation is used by a given
> + * platform.
> + */
> +typedef unsigned long irq_hw_number_t;
> +
> +/* Interrupt controller "host" data structure. This could be defined
> as a
> + * irq domain controller. That is, it handles the mapping between
> hardware
> + * and virtual interrupt numbers for a given interrupt domain. The
> host
> + * structure is generally created by the PIC code for a given PIC
> instance
> + * (though a host can cover more than one PIC if they have a flat
> number
> + * model). It's the host callbacks that are responsible for setting
> the
> + * irq_chip on a given irq_desc after it's been mapped.
> + *
> + * The host code and data structures are fairly agnostic to the fact
> that
> + * we use an open firmware device-tree. We do have references to
> struct
> + * device_node in two places: in irq_find_host() to find the host
> matching
> + * a given interrupt controller node, and of course as an argument to
> its
> + * counterpart host->ops->match() callback. However, those are treated
> as
> + * generic pointers by the core and the fact that it's actually a
> device-node
> + * pointer is purely a convention between callers and implementation.
> This
> + * code could thus be used on other architectures by replacing those
> two
> + * by some sort of arch-specific void * "token" used to identify
> interrupt
> + * controllers.
> + */
> +struct irq_host;
> +struct radix_tree_root;
> +struct device_node;
> +
> +/**
> + * struct irq_host_ops - operations for managing per-domain hw irq
> numbers
> + *
> + * Functions below are provided by the host and called whenever a new
> mapping
> + * is created or an old mapping is disposed. The host can then proceed
> to
> + * whatever internal data structures management is required. It also
> needs
> + * to setup the irq_desc when returning from map().
> + */
> +struct irq_host_ops {
> + /* Match an interrupt controller device node to a host, returns
> + * 1 on a match
> + */
> + int (*match)(struct irq_host *h, struct device_node *node);
> +
> + /* Create or update a mapping between a virtual irq number and a
> hw
> + * irq number. This is called only once for a given mapping.
> + */
> + int (*map)(struct irq_host *h, unsigned int virq, irq_hw_number_t
> hw);
> +
> + /* Dispose of such a mapping */
> + void (*unmap)(struct irq_host *h, unsigned int virq);
> +
> + /* Update of such a mapping */
> + void (*remap)(struct irq_host *h, unsigned int virq,
> irq_hw_number_t hw);
> +
> + /* Translate device-tree interrupt specifier from raw format
> coming
> + * from the firmware to a irq_hw_number_t (interrupt line number)
> and
> + * type (sense) that can be passed to set_irq_type(). In the
> absence
> + * of this callback, irq_create_of_mapping() and
> irq_of_parse_and_map()
> + * will return the hw number in the first cell and IRQ_TYPE_NONE
> for
> + * the type (which amount to keeping whatever default value the
> + * interrupt controller has for that line)
> + */
> + int (*xlate)(struct irq_host *h, struct device_node *ctrler,
> + const u32 *intspec, unsigned int intsize,
> + irq_hw_number_t *out_hwirq, unsigned int *out_type);
> +};
> +
> +/**
> + * struct irq_host - a single irq domain. maps hw irq numbers to Linux
> irq.
> + * @link: entry in global irq_host list
> + * @revmap_type: Method of reverse mapping hwirq to Linux irq number
> + * @revmap_data: reverse map data
> + * @ops: irq domain operations (documented above)
> + * @host_data: irq controller driver data; core does not touch this
> pointer
> + * @inval_irq: hw irq number used for unassigned virqs
> + * @of_node: Optional pointer to the irq controllers device tree node.
> + *
> + * One irq_host is allocated for each range (domain) of Linux irq
> numbers
> + * allocated. Typically, one irq_host is allocated per controller,
> but it
> + * is perfectly valid to manage multiple controllers with a single
> irq_host
> + * instance if need be.
> + */
> +struct irq_host {
> + struct list_head link;
> +
> + /* type of reverse mapping technique */
> + unsigned int revmap_type;
> +#define IRQ_HOST_MAP_LEGACY 0 /* legacy 8259, gets irqs 1..15 */
> +#define IRQ_HOST_MAP_NOMAP 1 /* no fast reverse mapping */
> +#define IRQ_HOST_MAP_LINEAR 2 /* linear map of interrupts */
> +#define IRQ_HOST_MAP_TREE 3 /* radix tree */
> + union {
> + struct {
> + unsigned int size;
> + unsigned int *revmap;
> + } linear;
> + struct radix_tree_root tree;
> + } revmap_data;
> + struct irq_host_ops *ops;
> + void *host_data;
> + irq_hw_number_t inval_irq;
> +
> + /* Optional device node pointer */
> + struct device_node *of_node;
> +};
> +
> +/**
> + * irq_alloc_host() - Allocate a new irq_host data structure
> + * @of_node: optional device-tree node of the interrupt controller
> + * @revmap_type: type of reverse mapping to use
> + * @revmap_arg: for IRQ_HOST_MAP_LINEAR linear only: size of the map
> + * @ops: map/unmap host callbacks
> + * @inval_irq: provide a hw number in that host space that is always
> invalid
> + *
> + * Allocates and initialize and irq_host structure. Note that in the
> case of
> + * IRQ_HOST_MAP_LEGACY, the map() callback will be called before this
> returns
> + * for all legacy interrupts except 0 (which is always the invalid irq
> for
> + * a legacy controller). For a IRQ_HOST_MAP_LINEAR, the map is
> allocated by
> + * this call as well. For a IRQ_HOST_MAP_TREE, the radix tree will be
> allocated
> + * later during boot automatically (the reverse mapping will use the
> slow path
> + * until that happens).
> + */
> +extern struct irq_host *irq_alloc_host(struct device_node *of_node,
> + unsigned int revmap_type,
> + unsigned int revmap_arg,
> + struct irq_host_ops *ops,
> + irq_hw_number_t inval_irq);
> +
> +/* The main irq map itself is an array of NR_IRQ entries containing
> the
> + * associate host and irq number. An entry with a host of NULL is
> free.
> + * An entry can be allocated if it's free, the allocator always then
> sets
> + * hwirq first to the host's invalid irq number and then fills ops.
> + */
> +struct irq_map_entry {
> + irq_hw_number_t hwirq;
> + struct irq_host *host;
> +};
> +extern struct irq_map_entry irq_map[NR_IRQS];
> +
> +extern irq_hw_number_t virq_to_hw(unsigned int virq);
> +
> +/**
> + * irq_find_host - Locates a host for a given device node
> + * @node: device-tree node of the interrupt controller
> + */
> +extern struct irq_host *irq_find_host(struct device_node *node);
> +
> +/**
> + * irq_set_default_host - Set a "default" host
> + * @host: default host pointer
> + *
> + * For convenience, it's possible to set a "default" host that will be
> used
> + * whenever NULL is passed to irq_create_mapping(). It makes life
> easier for
> + * platforms that want to manipulate a few hard coded interrupt
> numbers that
> + * aren't properly represented in the device-tree.
> + */
> +extern void irq_set_default_host(struct irq_host *host);
> +
> +/**
> + * irq_set_virq_count - Set the maximum number of virt irqs
> + * @count: number of linux virtual irqs, capped with NR_IRQS
> + *
> + * This is mainly for use by platforms like iSeries who want to
> program
> + * the virtual irq number in the controller to avoid the reverse
> mapping
> + */
> +extern void irq_set_virq_count(unsigned int count);
> +
> +/**
> + * irq_create_mapping - Map a hardware interrupt into linux virq space
> + * @host: host owning this hardware interrupt or NULL for default host
> + * @hwirq: hardware irq number in that host space
> + *
> + * Only one mapping per hardware interrupt is permitted. Returns a
> linux
> + * virq number.
> + * If the sense/trigger is to be specified, set_irq_type() should be
> called
> + * on the number returned from that call.
> + */
> +extern unsigned int irq_create_mapping(struct irq_host *host,
> + irq_hw_number_t hwirq);
> +
> +/**
> + * irq_dispose_mapping - Unmap an interrupt
> + * @virq: linux virq number of the interrupt to unmap
> + */
> +extern void irq_dispose_mapping(unsigned int virq);
> +
> +/**
> + * irq_find_mapping - Find a linux virq from an hw irq number.
> + * @host: host owning this hardware interrupt
> + * @hwirq: hardware irq number in that host space
> + *
> + * This is a slow path, for use by generic code. It's expected that an
> + * irq controller implementation directly calls the appropriate low
> level
> + * mapping function.
> + */
> +extern unsigned int irq_find_mapping(struct irq_host *host,
> + irq_hw_number_t hwirq);
> +
> +/**
> + * irq_create_direct_mapping - Allocate a virq for direct mapping
> + * @host: host to allocate the virq for or NULL for default host
> + *
> + * This routine is used for irq controllers which can choose the
> hardware
> + * interrupt numbers they generate. In such a case it's simplest to
> use
> + * the linux virq as the hardware interrupt number.
> + */
> +extern unsigned int irq_create_direct_mapping(struct irq_host *host);
> +
> +/**
> + * irq_radix_revmap_insert - Insert a hw irq to linux virq number
> mapping.
> + * @host: host owning this hardware interrupt
> + * @virq: linux irq number
> + * @hwirq: hardware irq number in that host space
> + *
> + * This is for use by irq controllers that use a radix tree reverse
> + * mapping for fast lookup.
> + */
> +extern void irq_radix_revmap_insert(struct irq_host *host, unsigned
> int virq,
> + irq_hw_number_t hwirq);
> +
> +/**
> + * irq_radix_revmap_lookup - Find a linux virq from a hw irq number.
> + * @host: host owning this hardware interrupt
> + * @hwirq: hardware irq number in that host space
> + *
> + * This is a fast path, for use by irq controller code that uses radix
> tree
> + * revmaps
> + */
> +extern unsigned int irq_radix_revmap_lookup(struct irq_host *host,
> + irq_hw_number_t hwirq);
> +
> +/**
> + * irq_linear_revmap - Find a linux virq from a hw irq number.
> + * @host: host owning this hardware interrupt
> + * @hwirq: hardware irq number in that host space
> + *
> + * This is a fast path, for use by irq controller code that uses
> linear
> + * revmaps. It does fallback to the slow path if the revmap doesn't
> exist
> + * yet and will create the revmap entry with appropriate locking
> + */
> +
> +extern unsigned int irq_linear_revmap(struct irq_host *host,
> + irq_hw_number_t hwirq);
> +
> +
> +
> +/**
> + * irq_alloc_virt - Allocate virtual irq numbers
> + * @host: host owning these new virtual irqs
> + * @count: number of consecutive numbers to allocate
> + * @hint: pass a hint number, the allocator will try to use a 1:1
> mapping
> + *
> + * This is a low level function that is used internally by
> irq_create_mapping()
> + * and that can be used by some irq controllers implementations for
> things
> + * like allocating ranges of numbers for MSIs. The revmaps are left
> untouched.
> + */
> +extern unsigned int irq_alloc_virt(struct irq_host *host,
> + unsigned int count,
> + unsigned int hint);
> +
> +/**
> + * irq_free_virt - Free virtual irq numbers
> + * @virq: virtual irq number of the first interrupt to free
> + * @count: number of interrupts to free
> + *
> + * This function is the opposite of irq_alloc_virt. It will not clear
> reverse
> + * maps, this should be done previously by unmap'ing the interrupt. In
> fact,
> + * all interrupts covered by the range being freed should have been
> unmapped
> + * prior to calling this.
> + */
> +extern void irq_free_virt(unsigned int virq, unsigned int count);
> +
> +
> +#endif /* CONFIG_VIRQ */
> +
> +#endif /* _LINUX_VIRQ_H */
> +#endif /* __KERNEL__ */
> +
> diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
> index 7d04780..f5207dc 100644
> --- a/kernel/irq/Makefile
> +++ b/kernel/irq/Makefile
> @@ -1,5 +1,6 @@
>
> obj-y := handle.o manage.o spurious.o resend.o chip.o devres.o
> +obj-$(CONFIG_VIRQ) += virq.o
> obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
> obj-$(CONFIG_PROC_FS) += proc.o
> obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
> diff --git a/kernel/irq/virq.c b/kernel/irq/virq.c
> new file mode 100644
> index 0000000..b3c0db3
> --- /dev/null
> +++ b/kernel/irq/virq.c
> @@ -0,0 +1,687 @@
> +/*
> + * Mapping support from per-controller hw irq numbers to linux irqs
> + *
> + * Derived from arch/i386/kernel/irq.c
> + * Copyright (C) 1992 Linus Torvalds
> + * Adapted from arch/i386 by Gary Thomas
> + * Copyright (C) 1995-1996 Gary Thomas (gdt at linuxppc.org)
> + * Updated and modified by Cort Dougan <cort at fsmlabs.com>
> + * Copyright (C) 1996-2001 Cort Dougan
> + * Adapted for Power Macintosh by Paul Mackerras
> + * Copyright (C) 1996 Paul Mackerras (paulus at cs.anu.edu.au)
> + * Generalized for virtual irq mapping on all platformes by Grant
> Likely
> + * Copyright (C) 2010 Secret Lab Technologies Ltd.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/radix-tree.h>
> +#include <linux/virq.h>
> +#include <linux/of_irq.h>
> +
> +/*
> + * IRQ controller and virtual interrupts
> + */
> +static LIST_HEAD(irq_hosts);
> +static DEFINE_RAW_SPINLOCK(irq_big_lock);
> +static unsigned int revmap_trees_allocated;
> +static DEFINE_MUTEX(revmap_trees_mutex);
> +struct irq_map_entry irq_map[NR_IRQS];
> +static unsigned int irq_virq_count = NR_IRQS;
> +static struct irq_host *irq_default_host;
> +
> +irq_hw_number_t virq_to_hw(unsigned int virq)
> +{
> + return irq_map[virq].hwirq;
> +}
> +EXPORT_SYMBOL_GPL(virq_to_hw);
> +
> +static int default_irq_host_match(struct irq_host *h, struct
> device_node *np)
> +{
> + return h->of_node != NULL && h->of_node == np;
> +}
> +
> +struct irq_host *irq_alloc_host(struct device_node *of_node,
> + unsigned int revmap_type,
> + unsigned int revmap_arg,
> + struct irq_host_ops *ops,
> + irq_hw_number_t inval_irq)
> +{
> + struct irq_host *host;
> + unsigned int size = sizeof(struct irq_host);
> + unsigned int i;
> + unsigned int *rmap;
> + unsigned long flags;
> +
> + /* Allocate structure and revmap table if using linear mapping */
> + if (revmap_type == IRQ_HOST_MAP_LINEAR)
> + size += revmap_arg * sizeof(unsigned int);
> + host = zalloc_maybe_bootmem(size, GFP_KERNEL);
> + if (host == NULL)
> + return NULL;
> +
> + /* Fill structure */
> + host->revmap_type = revmap_type;
> + host->inval_irq = inval_irq;
> + host->ops = ops;
> + host->of_node = of_node_get(of_node);
> +
> + if (host->ops->match == NULL)
> + host->ops->match = default_irq_host_match;
> +
> + raw_spin_lock_irqsave(&irq_big_lock, flags);
> +
> + /* If it's a legacy controller, check for duplicates and
> + * mark it as allocated (we use irq 0 host pointer for that
> + */
> + if (revmap_type == IRQ_HOST_MAP_LEGACY) {
> + if (irq_map[0].host != NULL) {
> + raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> + /* If we are early boot, we can't free the
structure,
> + * too bad...
> + * this will be fixed once slab is made available
> early
> + * instead of the current cruft
> + */
> + if (mem_init_done)
> + kfree(host);
> + return NULL;
> + }
> + irq_map[0].host = host;
> + }
> +
> + list_add(&host->link, &irq_hosts);
> + raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> +
> + /* Additional setups per revmap type */
> + switch(revmap_type) {
> + case IRQ_HOST_MAP_LEGACY:
> + /* 0 is always the invalid number for legacy */
> + host->inval_irq = 0;
> + /* setup us as the host for all legacy interrupts */
> + for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
> + irq_map[i].hwirq = i;
> + smp_wmb();
> + irq_map[i].host = host;
> + smp_wmb();
> +
> + /* Clear norequest flags */
> + irq_to_desc(i)->status &= ~IRQ_NOREQUEST;
> +
> + /* Legacy flags are left to default at this point,
> + * one can then use irq_create_mapping() to
> + * explicitly change them
> + */
> + ops->map(host, i, i);
> + }
> + break;
> + case IRQ_HOST_MAP_LINEAR:
> + rmap = (unsigned int *)(host + 1);
> + for (i = 0; i < revmap_arg; i++)
> + rmap[i] = NO_IRQ;
> + host->revmap_data.linear.size = revmap_arg;
> + smp_wmb();
> + host->revmap_data.linear.revmap = rmap;
> + break;
> + default:
> + break;
> + }
> +
> + pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type,
> host);
> +
> + return host;
> +}
> +
> +struct irq_host *irq_find_host(struct device_node *node)
> +{
> + struct irq_host *h, *found = NULL;
> + unsigned long flags;
> +
> + /* We might want to match the legacy controller last since
> + * it might potentially be set to match all interrupts in
> + * the absence of a device node. This isn't a problem so far
> + * yet though...
> + */
> + raw_spin_lock_irqsave(&irq_big_lock, flags);
> + list_for_each_entry(h, &irq_hosts, link)
> + if (h->ops->match(h, node)) {
> + found = h;
> + break;
> + }
> + raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> + return found;
> +}
> +EXPORT_SYMBOL_GPL(irq_find_host);
> +
> +void irq_set_default_host(struct irq_host *host)
> +{
> + pr_debug("irq: Default host set to @0x%p\n", host);
> +
> + irq_default_host = host;
> +}
> +
> +void irq_set_virq_count(unsigned int count)
> +{
> + pr_debug("irq: Trying to set virq count to %d\n", count);
> +
> + BUG_ON(count < NUM_ISA_INTERRUPTS);
> + if (count < NR_IRQS)
> + irq_virq_count = count;
> +}
> +
> +static int irq_setup_virq(struct irq_host *host, unsigned int virq,
> + irq_hw_number_t hwirq)
> +{
> + struct irq_desc *desc;
> +
> + desc = irq_to_desc_alloc_node(virq, 0);
> + if (!desc) {
> + pr_debug("irq: -> allocating desc failed\n");
> + goto error;
> + }
> +
> + /* Clear IRQ_NOREQUEST flag */
> + desc->status &= ~IRQ_NOREQUEST;
> +
> + /* map it */
> + smp_wmb();
> + irq_map[virq].hwirq = hwirq;
> + smp_mb();
> +
> + if (host->ops->map(host, virq, hwirq)) {
> + pr_debug("irq: -> mapping failed, freeing\n");
> + goto error;
> + }
> +
> + return 0;
> +
> +error:
> + irq_free_virt(virq, 1);
> + return -1;
> +}
> +
> +unsigned int irq_create_direct_mapping(struct irq_host *host)
> +{
> + unsigned int virq;
> +
> + if (host == NULL)
> + host = irq_default_host;
> +
> + BUG_ON(host == NULL);
> + WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP);
> +
> + virq = irq_alloc_virt(host, 1, 0);
> + if (virq == NO_IRQ) {
> + pr_debug("irq: create_direct virq allocation failed\n");
> + return NO_IRQ;
> + }
> +
> + pr_debug("irq: create_direct obtained virq %d\n", virq);
> +
> + if (irq_setup_virq(host, virq, virq))
> + return NO_IRQ;
> +
> + return virq;
> +}
> +
> +unsigned int irq_create_mapping(struct irq_host *host,
> + irq_hw_number_t hwirq)
> +{
> + unsigned int virq, hint;
> +
> + pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq);
> +
> + /* Look for default host if nececssary */
> + if (host == NULL)
> + host = irq_default_host;
> + if (host == NULL) {
> + printk(KERN_WARNING "irq_create_mapping called for"
> + " NULL host, hwirq=%lx\n", hwirq);
> + WARN_ON(1);
> + return NO_IRQ;
> + }
> + pr_debug("irq: -> using host @%p\n", host);
> +
> + /* Check if mapping already exist, if it does, call
> + * host->ops->map() to update the flags
> + */
> + virq = irq_find_mapping(host, hwirq);
> + if (virq != NO_IRQ) {
> + if (host->ops->remap)
> + host->ops->remap(host, virq, hwirq);
> + pr_debug("irq: -> existing mapping on virq %d\n", virq);
> + return virq;
> + }
> +
> + /* Get a virtual interrupt number */
> + if (host->revmap_type == IRQ_HOST_MAP_LEGACY) {
> + /* Handle legacy */
> + virq = (unsigned int)hwirq;
> + if (virq == 0 || virq >= NUM_ISA_INTERRUPTS)
> + return NO_IRQ;
> + return virq;
> + } else {
> + /* Allocate a virtual interrupt number */
> + hint = hwirq % irq_virq_count;
> + virq = irq_alloc_virt(host, 1, hint);
> + if (virq == NO_IRQ) {
> + pr_debug("irq: -> virq allocation failed\n");
> + return NO_IRQ;
> + }
> + }
> +
> + if (irq_setup_virq(host, virq, hwirq))
> + return NO_IRQ;
> +
> + printk(KERN_DEBUG "irq: irq %lu on host %s mapped to virtual irq
> %u\n",
> + hwirq, host->of_node ? host->of_node->full_name : "null",
> virq);
> +
> + return virq;
> +}
> +EXPORT_SYMBOL_GPL(irq_create_mapping);
> +
> +unsigned int irq_create_of_mapping(struct device_node *controller,
> + const u32 *intspec, unsigned int intsize)
> +{
> + struct irq_host *host;
> + irq_hw_number_t hwirq;
> + unsigned int type = IRQ_TYPE_NONE;
> + unsigned int virq;
> +
> + if (controller == NULL)
> + host = irq_default_host;
> + else
> + host = irq_find_host(controller);
> + if (host == NULL) {
> + printk(KERN_WARNING "irq: no irq host found for %s !\n",
> + controller->full_name);
> + return NO_IRQ;
> + }
> +
> + /* If host has no translation, then we assume interrupt line */
> + if (host->ops->xlate == NULL)
> + hwirq = intspec[0];
> + else {
> + if (host->ops->xlate(host, controller, intspec, intsize,
> + &hwirq, &type))
> + return NO_IRQ;
> + }
> +
> + /* Create mapping */
> + virq = irq_create_mapping(host, hwirq);
> + if (virq == NO_IRQ)
> + return virq;
> +
> + /* Set type if specified and different than the current one */
> + if (type != IRQ_TYPE_NONE &&
> + type != (irq_to_desc(virq)->status & IRQF_TRIGGER_MASK))
> + set_irq_type(virq, type);
> + return virq;
> +}
> +EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> +
> +void irq_dispose_mapping(unsigned int virq)
> +{
> + struct irq_host *host;
> + irq_hw_number_t hwirq;
> +
> + if (virq == NO_IRQ)
> + return;
> +
> + host = irq_map[virq].host;
> + WARN_ON (host == NULL);
> + if (host == NULL)
> + return;
> +
> + /* Never unmap legacy interrupts */
> + if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
> + return;
> +
> + /* remove chip and handler */
> + set_irq_chip_and_handler(virq, NULL, NULL);
> +
> + /* Make sure it's completed */
> + synchronize_irq(virq);
> +
> + /* Tell the PIC about it */
> + if (host->ops->unmap)
> + host->ops->unmap(host, virq);
> + smp_mb();
> +
> + /* Clear reverse map */
> + hwirq = irq_map[virq].hwirq;
> + switch(host->revmap_type) {
> + case IRQ_HOST_MAP_LINEAR:
> + if (hwirq < host->revmap_data.linear.size)
> + host->revmap_data.linear.revmap[hwirq] = NO_IRQ;
> + break;
> + case IRQ_HOST_MAP_TREE:
> + /*
> + * Check if radix tree allocated yet, if not then nothing
> to
> + * remove.
> + */
> + smp_rmb();
> + if (revmap_trees_allocated < 1)
> + break;
> + mutex_lock(&revmap_trees_mutex);
> + radix_tree_delete(&host->revmap_data.tree, hwirq);
> + mutex_unlock(&revmap_trees_mutex);
> + break;
> + }
> +
> + /* Destroy map */
> + smp_mb();
> + irq_map[virq].hwirq = host->inval_irq;
> +
> + /* Set some flags */
> + irq_to_desc(virq)->status |= IRQ_NOREQUEST;
> +
> + /* Free it */
> + irq_free_virt(virq, 1);
> +}
> +EXPORT_SYMBOL_GPL(irq_dispose_mapping);
> +
> +unsigned int irq_find_mapping(struct irq_host *host,
> + irq_hw_number_t hwirq)
> +{
> + unsigned int i;
> + unsigned int hint = hwirq % irq_virq_count;
> +
> + /* Look for default host if nececssary */
> + if (host == NULL)
> + host = irq_default_host;
> + if (host == NULL)
> + return NO_IRQ;
> +
> + /* legacy -> bail early */
> + if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
> + return hwirq;
> +
> + /* Slow path does a linear search of the map */
> + if (hint < NUM_ISA_INTERRUPTS)
> + hint = NUM_ISA_INTERRUPTS;
> + i = hint;
> + do {
> + if (irq_map[i].host == host &&
> + irq_map[i].hwirq == hwirq)
> + return i;
> + i++;
> + if (i >= irq_virq_count)
> + i = NUM_ISA_INTERRUPTS;
> + } while(i != hint);
> + return NO_IRQ;
> +}
> +EXPORT_SYMBOL_GPL(irq_find_mapping);
> +
> +
> +unsigned int irq_radix_revmap_lookup(struct irq_host *host,
> + irq_hw_number_t hwirq)
> +{
> + struct irq_map_entry *ptr;
> + unsigned int virq;
> +
> + WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
> +
> + /*
> + * Check if the radix tree exists and has bee initialized.
> + * If not, we fallback to slow mode
> + */
> + if (revmap_trees_allocated < 2)
> + return irq_find_mapping(host, hwirq);
> +
> + /* Now try to resolve */
> + /*
> + * No rcu_read_lock(ing) needed, the ptr returned can't go under
> us
> + * as it's referencing an entry in the static irq_map table.
> + */
> + ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
> +
> + /*
> + * If found in radix tree, then fine.
> + * Else fallback to linear lookup - this should not happen in
> practice
> + * as it means that we failed to insert the node in the radix
> tree.
> + */
> + if (ptr)
> + virq = ptr - irq_map;
> + else
> + virq = irq_find_mapping(host, hwirq);
> +
> + return virq;
> +}
> +
> +void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
> + irq_hw_number_t hwirq)
> +{
> +
> + WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
> +
> + /*
> + * Check if the radix tree exists yet.
> + * If not, then the irq will be inserted into the tree when it
> gets
> + * initialized.
> + */
> + smp_rmb();
> + if (revmap_trees_allocated < 1)
> + return;
> +
> + if (virq != NO_IRQ) {
> + mutex_lock(&revmap_trees_mutex);
> + radix_tree_insert(&host->revmap_data.tree, hwirq,
> + &irq_map[virq]);
> + mutex_unlock(&revmap_trees_mutex);
> + }
> +}
> +
> +unsigned int irq_linear_revmap(struct irq_host *host,
> + irq_hw_number_t hwirq)
> +{
> + unsigned int *revmap;
> +
> + WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR);
> +
> + /* Check revmap bounds */
> + if (unlikely(hwirq >= host->revmap_data.linear.size))
> + return irq_find_mapping(host, hwirq);
> +
> + /* Check if revmap was allocated */
> + revmap = host->revmap_data.linear.revmap;
> + if (unlikely(revmap == NULL))
> + return irq_find_mapping(host, hwirq);
> +
> + /* Fill up revmap with slow path if no mapping found */
> + if (unlikely(revmap[hwirq] == NO_IRQ))
> + revmap[hwirq] = irq_find_mapping(host, hwirq);
> +
> + return revmap[hwirq];
> +}
> +
> +unsigned int irq_alloc_virt(struct irq_host *host,
> + unsigned int count,
> + unsigned int hint)
> +{
> + unsigned long flags;
> + unsigned int i, j, found = NO_IRQ;
> +
> + if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS))
> + return NO_IRQ;
> +
> + raw_spin_lock_irqsave(&irq_big_lock, flags);
> +
> + /* Use hint for 1 interrupt if any */
> + if (count == 1 && hint >= NUM_ISA_INTERRUPTS &&
> + hint < irq_virq_count && irq_map[hint].host == NULL) {
> + found = hint;
> + goto hint_found;
> + }
> +
> + /* Look for count consecutive numbers in the allocatable
> + * (non-legacy) space
> + */
> + for (i = NUM_ISA_INTERRUPTS, j = 0; i < irq_virq_count; i++) {
> + if (irq_map[i].host != NULL)
> + j = 0;
> + else
> + j++;
> +
> + if (j == count) {
> + found = i - count + 1;
> + break;
> + }
> + }
> + if (found == NO_IRQ) {
> + raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> + return NO_IRQ;
> + }
> + hint_found:
> + for (i = found; i < (found + count); i++) {
> + irq_map[i].hwirq = host->inval_irq;
> + smp_wmb();
> + irq_map[i].host = host;
> + }
> + raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> + return found;
> +}
> +
> +void irq_free_virt(unsigned int virq, unsigned int count)
> +{
> + unsigned long flags;
> + unsigned int i;
> +
> + WARN_ON (virq < NUM_ISA_INTERRUPTS);
> + WARN_ON (count == 0 || (virq + count) > irq_virq_count);
> +
> + raw_spin_lock_irqsave(&irq_big_lock, flags);
> + for (i = virq; i < (virq + count); i++) {
> + struct irq_host *host;
> +
> + if (i < NUM_ISA_INTERRUPTS ||
> + (virq + count) > irq_virq_count)
> + continue;
> +
> + host = irq_map[i].host;
> + irq_map[i].hwirq = host->inval_irq;
> + smp_wmb();
> + irq_map[i].host = NULL;
> + }
> + raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> +}
> +
> +/* We need to create the radix trees late */
> +static int irq_late_init(void)
> +{
> + struct irq_host *h;
> + unsigned int i;
> +
> + /*
> + * No mutual exclusion with respect to accessors of the tree is
> needed
> + * here as the synchronization is done via the state variable
> + * revmap_trees_allocated.
> + */
> + list_for_each_entry(h, &irq_hosts, link) {
> + if (h->revmap_type == IRQ_HOST_MAP_TREE)
> + INIT_RADIX_TREE(&h->revmap_data.tree, GFP_KERNEL);
> + }
> +
> + /*
> + * Make sure the radix trees inits are visible before setting
> + * the flag
> + */
> + smp_wmb();
> + revmap_trees_allocated = 1;
> +
> + /*
> + * Insert the reverse mapping for those interrupts already
> present
> + * in irq_map[].
> + */
> + mutex_lock(&revmap_trees_mutex);
> + for (i = 0; i < irq_virq_count; i++) {
> + if (irq_map[i].host &&
> + (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE))
> +
radix_tree_insert(&irq_map[i].host->revmap_data.tree,
> + irq_map[i].hwirq, &irq_map[i]);
> + }
> + mutex_unlock(&revmap_trees_mutex);
> +
> + /*
> + * Make sure the radix trees insertions are visible before
> setting
> + * the flag
> + */
> + smp_wmb();
> + revmap_trees_allocated = 2;
> +
> + return 0;
> +}
> +arch_initcall(irq_late_init);
> +
> +#ifdef CONFIG_VIRQ_DEBUG
> +static int virq_debug_show(struct seq_file *m, void *private)
> +{
> + unsigned long flags;
> + struct irq_desc *desc;
> + const char *p;
> + char none[] = "none";
> + int i;
> +
> + seq_printf(m, "%-5s %-7s %-15s %s\n", "virq", "hwirq",
> + "chip name", "host name");
> +
> + for (i = 1; i < nr_irqs; i++) {
> + desc = irq_to_desc(i);
> + if (!desc)
> + continue;
> +
> + raw_spin_lock_irqsave(&desc->lock, flags);
> +
> + if (desc->action && desc->action->handler) {
> + seq_printf(m, "%5d ", i);
> + seq_printf(m, "0x%05lx ", virq_to_hw(i));
> +
> + if (desc->chip && desc->chip->name)
> + p = desc->chip->name;
> + else
> + p = none;
> + seq_printf(m, "%-15s ", p);
> +
> + if (irq_map[i].host && irq_map[i].host->of_node)
> + p = irq_map[i].host->of_node->full_name;
> + else
> + p = none;
> + seq_printf(m, "%s\n", p);
> + }
> +
> + raw_spin_unlock_irqrestore(&desc->lock, flags);
> + }
> +
> + return 0;
> +}
> +
> +static int virq_debug_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, virq_debug_show, inode->i_private);
> +}
> +
> +static const struct file_operations virq_debug_fops = {
> + .open = virq_debug_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static int __init irq_debugfs_init(void)
> +{
> + if (debugfs_create_file("virq_mapping", S_IRUGO,
> powerpc_debugfs_root,
> + NULL, &virq_debug_fops) == NULL)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +__initcall(irq_debugfs_init);
> +#endif /* CONFIG_VIRQ_DEBUG */
> +
>
> _______________________________________________
> devicetree-discuss mailing list
> devicetree-discuss at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/devicetree-discuss
More information about the devicetree-discuss
mailing list