Linux ARM中断控制器注册(4)

本文以S5PV210芯片为参照,S5PV210的中断控制器采用了ARM VIC(Vectored Interrupt Controller
,PL192 ,ARM PrimeCell Vectored Interrupt Controller )。ARM VIC的驱动代码位于drivers/irqchip/irq-vic.c。首先介绍Linux中断框架中使用的一些数据结构。

1. struct irq_domain_ops

//include/linux/irqdomain.h
/**
 * struct irq_domain_ops - Methods for irq_domain objects
 * @match: Match an interrupt controller device node to a host, returns
 *         1 on a match
 * @map: Create or update a mapping between a virtual irq number and a hw
 *       irq number. This is called only once for a given mapping.
 * @unmap: Dispose of such a mapping
 * @xlate: Given a device tree node and interrupt specifier, decode
 *         the hardware irq number and linux irq type value.
 *
 * Functions below are provided by the driver and called whenever a new mapping
 * is created or an old mapping is disposed. The driver 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_domain_ops {
        int (*match)(struct irq_domain *d, struct device_node *node,
                     enum irq_domain_bus_token bus_token);
        int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);
        void (*unmap)(struct irq_domain *d, unsigned int virq);
        int (*xlate)(struct irq_domain *d, struct device_node *node,
                     const u32 *intspec, unsigned int intsize,
                     unsigned long *out_hwirq, unsigned int *out_type);

#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY
        /* extended V2 interfaces to support hierarchy irq_domains */
        int (*alloc)(struct irq_domain *d, unsigned int virq,
                     unsigned int nr_irqs, void *arg);
        void (*free)(struct irq_domain *d, unsigned int virq,
                     unsigned int nr_irqs);
        void (*activate)(struct irq_domain *d, struct irq_data *irq_data);
        void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
        int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,
                         unsigned long *out_hwirq, unsigned int *out_type);
#endif
};

2. struct irq_domain

//include/linux/irqdomain.h
/**
 * struct irq_domain - Hardware interrupt number translation object
 * @link: Element in global irq_domain list.
 * @name: Name of interrupt domain
 * @ops: pointer to irq_domain methods
 * @host_data: private data pointer for use by owner.  Not touched by irq_domain
 *             core code.
 * @flags: host per irq_domain flags
 *
 * Optional elements
 * @of_node: Pointer to device tree nodes associated with the irq_domain. Used
 *           when decoding device tree interrupt specifiers.
 * @gc: Pointer to a list of generic chips. There is a helper function for
 *      setting up one or more generic chips for interrupt controllers
 *      drivers using the generic chip library which uses this pointer.
 * @parent: Pointer to parent irq_domain to support hierarchy irq_domains
 *
 * Revmap data, used internally by irq_domain
 * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that
 *                         support direct mapping
 * @revmap_size: Size of the linear map table @linear_revmap[]
 * @revmap_tree: Radix map tree for hwirqs that don't fit in the linear map
 * @linear_revmap: Linear table of hwirq->virq reverse mappings
 */
struct irq_domain {
        struct list_head link;
        const char *name;
        const struct irq_domain_ops *ops;
        void *host_data;
        unsigned int flags;

        /* Optional data */
        struct fwnode_handle *fwnode;
        enum irq_domain_bus_token bus_token;
        struct irq_domain_chip_generic *gc;
#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY
        struct irq_domain *parent;
#endif

        /* reverse map data. The linear map gets appended to the irq_domain */
        irq_hw_number_t hwirq_max;
        unsigned int revmap_direct_max_irq;
        unsigned int revmap_size;
        struct radix_tree_root revmap_tree;
        unsigned int linear_revmap[];
};

3. struct irq_data 

//include/linux/irq.h
/**
 * struct irq_data - per irq chip data passed down to chip functions
 * @mask:               precomputed bitmask for accessing the chip registers
 * @irq:                interrupt number
 * @hwirq:              hardware interrupt number, local to the interrupt domain
 * @common:             point to data shared by all irqchips
 * @chip:               low level interrupt hardware access
 * @domain:             Interrupt translation domain; responsible for mapping
 *                      between hwirq number and linux irq number.
 * @parent_data:        pointer to parent struct irq_data to support hierarchy
 *                      irq_domain
 * @chip_data:          platform-specific per-chip private data for the chip
 *                      methods, to allow shared chip implementations
 */
struct irq_data {
        u32                     mask;
        unsigned int            irq;
        unsigned long           hwirq;
        struct irq_common_data  *common;
        struct irq_chip         *chip;
        struct irq_domain       *domain;
#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY
        struct irq_data         *parent_data;
#endif
        void                    *chip_data;
};

 4. struct irq_chip

//include/linux/irq.h
/**
 * struct irq_chip - hardware interrupt chip descriptor
 *
 * @name:               name for /proc/interrupts
 * @irq_startup:        start up the interrupt (defaults to ->enable if NULL)
 * @irq_shutdown:       shut down the interrupt (defaults to ->disable if NULL)
 * @irq_enable:         enable the interrupt (defaults to chip->unmask if NULL)
 * @irq_disable:        disable the interrupt
 * @irq_ack:            start of a new interrupt
 * @irq_mask:           mask an interrupt source
 * @irq_mask_ack:       ack and mask an interrupt source
 * @irq_unmask:         unmask an interrupt source
 * @irq_eoi:            end of interrupt
 * @irq_set_affinity:   set the CPU affinity on SMP machines
 * @irq_retrigger:      resend an IRQ to the CPU
 * @irq_set_type:       set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
 * @irq_set_wake:       enable/disable power-management wake-on of an IRQ
 * @irq_bus_lock:       function to lock access to slow bus (i2c) chips
 * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
 * @irq_cpu_online:     configure an interrupt source for a secondary CPU
 * @irq_cpu_offline:    un-configure an interrupt source for a secondary CPU
 * @irq_suspend:        function called from core code on suspend once per
 *                      chip, when one or more interrupts are installed
 * @irq_resume:         function called from core code on resume once per chip,
 *                      when one ore more interrupts are installed
 * @irq_pm_shutdown:    function called from core code on shutdown once per chip
 * @irq_calc_mask:      Optional function to set irq_data.mask for special cases
 * @irq_print_chip:     optional to print special chip info in show_interrupts
 * @irq_request_resources:      optional to request resources before calling
 *                              any other callback related to this irq
 * @irq_release_resources:      optional to release resources acquired with
 *                              irq_request_resources
 * @irq_compose_msi_msg:        optional to compose message content for MSI
 * @irq_write_msi_msg:  optional to write message content for MSI
 * @irq_get_irqchip_state:      return the internal state of an interrupt
 * @irq_set_irqchip_state:      set the internal state of a interrupt
 * @irq_set_vcpu_affinity:      optional to target a vCPU in a virtual machine
 * @flags:              chip specific flags
 */
struct irq_chip {
        const char      *name;
        unsigned int    (*irq_startup)(struct irq_data *data);
        void            (*irq_shutdown)(struct irq_data *data);
        void            (*irq_enable)(struct irq_data *data);
        void            (*irq_disable)(struct irq_data *data);

        void            (*irq_ack)(struct irq_data *data);
        void            (*irq_mask)(struct irq_data *data);
        void            (*irq_mask_ack)(struct irq_data *data);
        void            (*irq_unmask)(struct irq_data *data);
        void            (*irq_eoi)(struct irq_data *data);

        int             (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
        int             (*irq_retrigger)(struct irq_data *data);
        int             (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
        int             (*irq_set_wake)(struct irq_data *data, unsigned int on);

        void            (*irq_bus_lock)(struct irq_data *data);
        void            (*irq_bus_sync_unlock)(struct irq_data *data);

        void            (*irq_cpu_online)(struct irq_data *data);
        void            (*irq_cpu_offline)(struct irq_data *data);
       void            (*irq_suspend)(struct irq_data *data);
        void            (*irq_resume)(struct irq_data *data);
        void            (*irq_pm_shutdown)(struct irq_data *data);

        void            (*irq_calc_mask)(struct irq_data *data);

        void            (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
        int             (*irq_request_resources)(struct irq_data *data);
        void            (*irq_release_resources)(struct irq_data *data);

        void            (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
        void            (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);

        int             (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
        int             (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);

        int             (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);

        unsigned long   flags;
};

5. struct irq_desc 

//include/linux/irqdesc.h
/**
 * struct irq_desc - interrupt descriptor
 * @irq_common_data:    per irq and chip data passed down to chip functions
 * @kstat_irqs:         irq stats per cpu
 * @handle_irq:         highlevel irq-events handler
 * @preflow_handler:    handler called before the flow handler (currently used by sparc)
 * @action:             the irq action chain
 * @status:             status information
 * @core_internal_state__do_not_mess_with_it: core internal status information
 * @depth:              disable-depth, for nested irq_disable() calls
 * @wake_depth:         enable depth, for multiple irq_set_irq_wake() callers
 * @irq_count:          stats field to detect stalled irqs
 * @last_unhandled:     aging timer for unhandled count
 * @irqs_unhandled:     stats field for spurious unhandled interrupts
 * @threads_handled:    stats field for deferred spurious detection of threaded handlers
 * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers
 * @lock:               locking for SMP
 * @affinity_hint:      hint to user space for preferred irq affinity
 * @affinity_notify:    context for notification of affinity changes
 * @pending_mask:       pending rebalanced interrupts
 * @threads_oneshot:    bitfield to handle shared oneshot threads
 * @threads_active:     number of irqaction threads currently running
 * @wait_for_threads:   wait queue for sync_irq to wait for threaded handlers
 * @nr_actions:         number of installed actions on this descriptor
 * @no_suspend_depth:   number of irqactions on a irq descriptor with
 *                      IRQF_NO_SUSPEND set
 * @force_resume_depth: number of irqactions on a irq descriptor with
 *                      IRQF_FORCE_RESUME set
 * @dir:                /proc/irq/ procfs entry
 * @name:               flow handler name for /proc/interrupts output
 */
struct irq_desc {
        struct irq_common_data  irq_common_data;
        struct irq_data         irq_data;
        unsigned int __percpu   *kstat_irqs;
        irq_flow_handler_t      handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
        irq_preflow_handler_t   preflow_handler;
#endif
        struct irqaction        *action;        /* IRQ action list */
        unsigned int            status_use_accessors;
        unsigned int            core_internal_state__do_not_mess_with_it;
        unsigned int            depth;          /* nested irq disables */
        unsigned int            wake_depth;     /* nested wake enables */
        unsigned int            irq_count;      /* For detecting broken IRQs */
        unsigned long           last_unhandled; /* Aging timer for unhandled count */
        unsigned int            irqs_unhandled;
        atomic_t                threads_handled;
        int                     threads_handled_last;
        raw_spinlock_t          lock;
        struct cpumask          *percpu_enabled;
#ifdef CONFIG_SMP
        const struct cpumask    *affinity_hint;
        struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
        cpumask_var_t           pending_mask;
#endif
#endif
        unsigned long           threads_oneshot;
        atomic_t                threads_active;
        wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PM_SLEEP
        unsigned int            nr_actions;
        unsigned int            no_suspend_depth;
        unsigned int            cond_suspend_depth;
        unsigned int            force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
        struct proc_dir_entry   *dir;
#endif
        int                     parent_irq;
        struct module           *owner;
        const char              *name;
} ____cacheline_internodealigned_in_smp;

6.  IRQCHIP_DECLARE

//driver/irqchip/irq-vic.c
/*
 *通过IRQCHIP_DECLARE定义一个位于__irqchip_of_table段的全局量struct of_device_id 
 *__of_table_arm_pl192_vic,linux在中断初始化阶段在设备树中寻找是否有对应的节点,找到
 *相对应的节点后,随即执行vic_of_init
 */
IRQCHIP_DECLARE(arm_pl192_vic, "arm,pl192-vic", vic_of_init);


/*
 * include/linux/irqchip.h
 * @name: name that must be unique accross all IRQCHIP_DECLARE of the
 * same file.
 * @compstr: compatible string of the irqchip driver
 * @fn: initialization function
 */
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)


//include/linux/of.h
#ifdef CONFIG_OF
#define _OF_DECLARE(table, name, compat, fn, fn_type)                   \
        static const struct of_device_id __of_table_##name              \
                __used __section(__##table##_of_table)                  \
                 = { .compatible = compat,                              \
                     .data = (fn == (fn_type)NULL) ? fn : fn  }
#else
#define _OF_DECLARE(table, name, compat, fn, fn_type)                   \
        static const struct of_device_id __of_table_##name              \
                __attribute__((unused))                                 \
                 = { .compatible = compat,                              \
                     .data = (fn == (fn_type)NULL) ? fn : fn }
#endif

typedef int (*of_init_fn_2)(struct device_node *, struct device_node *);
typedef void (*of_init_fn_1)(struct device_node *);

#define OF_DECLARE_1(table, name, compat, fn) \
                _OF_DECLARE(table, name, compat, fn, of_init_fn_1)
#define OF_DECLARE_2(table, name, compat, fn) \
                _OF_DECLARE(table, name, compat, fn, of_init_fn_2)

7.  vic_of_init

//driver/irqchip/irq-vic.c
int __init vic_of_init(struct device_node *node, struct device_node *parent)
{
        void __iomem *regs;
        u32 interrupt_mask = ~0; 
        u32 wakeup_mask = ~0; 

        if (WARN(parent, "non-root VICs are not supported"))
                return -EINVAL;

        regs = of_iomap(node, 0); 
        if (WARN_ON(!regs))
                return -EIO;
  
        /*
         *设备树中没有valid-mask、valid-wakeup-mask这两个属性,保持原值
         *valid-mask=0xFFFFFFFF, valid-wakeup-mask=0xFFFFFFFF.
         */         
        of_property_read_u32(node, "valid-mask", &interrupt_mask);
        of_property_read_u32(node, "valid-wakeup-mask", &wakeup_mask);

        /*
         * Passing 0 as first IRQ makes the simple domain allocate descriptors
         */
        __vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node);

        return 0;
}

8.  __vic_init

 //driver/irqchip/irq-vic.c
//__vic_init(regs, 0, 0, 0xffffffff, 0xffffffff, node);
void __init __vic_init(void __iomem *base, int parent_irq, int irq_start,
                              u32 vic_sources, u32 resume_sources,
                              struct device_node *node)
{
        unsigned int i;
        u32 cellid = 0;
        enum amba_vendor vendor;

        /* Identify which VIC cell this one is, by reading the ID */
        for (i = 0; i < 4; i++) {
                void __iomem *addr;
                addr = (void __iomem *)((u32)base & PAGE_MASK) + 0xfe0 + (i * 4);
                cellid |= (readl(addr) & 0xff) << (8 * i);
        }
        vendor = (cellid >> 12) & 0xff;
        printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n",
               base, cellid, vendor);

        switch(vendor) {
        case AMBA_VENDOR_ST:
                vic_init_st(base, irq_start, vic_sources, node);
                return;
        default:
                printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n");
                /* fall through */
        case AMBA_VENDOR_ARM:
                break;
        }
       /* Disable all interrupts initially. */
        vic_disable(base);

        /* Make sure we clear all existing interrupts */
        vic_clear_interrupts(base);

        vic_init2(base);

        vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node);
}

9.  vic_register

//drive/irqchip/irq-vic.c
//vic_register(base, 0, 0, 0xffffffff, 0xffffffff, node);
static void __init vic_register(void __iomem *base, unsigned int parent_irq,
                                unsigned int irq,
                                u32 valid_sources, u32 resume_sources,
                                struct device_node *node)
{
        struct vic_device *v;
        int i;

        if (vic_id >= ARRAY_SIZE(vic_devices)) {
                printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__);
                return;
        }

        v = &vic_devices[vic_id];
        v->base = base;
        v->valid_sources = valid_sources;
        v->resume_sources = resume_sources;
        set_handle_irq(vic_handle_irq);
        vic_id++;
        /*
         *添加打印代码:
         *early_print("valid_sources:0x%x, fls(valid_sources):0x%x, 
         *             resume_source:0x%x\n.",valid_sources,fls(valid_sources)
         *             ,resume_sources);
         *打印信息为:(设备树里有四个interrupt-controller节点,所以会注册四次)
         *valid_sources:0xffffffff, fls(valid_sources):0x20, resume_source:0xffffffff.
         *valid_sources:0xffffffff, fls(valid_sources):0x20, resume_source:0xffffffff.
         *valid_sources:0xffffffff, fls(valid_sources):0x20, resume_source:0xffffffff.
         *valid_sources:0xffffffff, fls(valid_sources):0x20, resume_source:0xffffffff.
         */
        if (parent_irq) {
                irq_set_chained_handler_and_data(parent_irq,
                                                 vic_handle_irq_cascaded, v);
        }

        v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
                                          &vic_irqdomain_ops, v);
        /* create an IRQ mapping for each valid IRQ */
        for (i = 0; i < fls(valid_sources); i++) {

                if (valid_sources & (1 << i)) {
                        irq_create_mapping(v->domain, i);
                }
        }
        /* If no base IRQ was passed, figure out our allocated base */
        if (irq)
                v->irq = irq;
        else
                v->irq = irq_find_mapping(v->domain, 0);
}

10.  irq_domain_add_simple

//kernel/irq/irqdomain.c
/**
 * irq_domain_add_simple() - Register an irq_domain and optionally map a range of irqs
 * @of_node: pointer to interrupt controller's device tree node.
 * @size: total number of irqs in mapping
 * @first_irq: first number of irq block assigned to the domain,
 *      pass zero to assign irqs on-the-fly. If first_irq is non-zero, then
 *      pre-map all of the irqs in the domain to virqs starting at first_irq.
 * @ops: domain callbacks
 * @host_data: Controller private data pointer
 *
 * Allocates an irq_domain, and optionally if first_irq is positive then also
 * allocate irq_descs and map all of the hwirqs to virqs starting at first_irq.
 *
 * This is intended to implement the expected behaviour for most
 * interrupt controllers. If device tree is used, then first_irq will be 0 and
 * irqs get mapped dynamically on the fly. However, if the controller requires
 * static virq assignments (non-DT boot) then it will set that up correctly.
 */
struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
                                         unsigned int size,
                                         unsigned int first_irq,
                                         const struct irq_domain_ops *ops,
                                         void *host_data)
{
        struct irq_domain *domain;

        domain = __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);
        if (!domain)
                return NULL;    
        /*
         *early_print("%s,%d,%d.\n",__FILE__,__LINE__,first_irq);
         *传过来的first_irq为零。
         */
        if (first_irq > 0) {
                if (IS_ENABLED(CONFIG_SPARSE_IRQ)) {
                        /* attempt to allocated irq_descs */
                        int rc = irq_alloc_descs(first_irq, first_irq, size,
                                                 of_node_to_nid(of_node));
                        if (rc < 0)
                                pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
                                        first_irq);
                }
                irq_domain_associate_many(domain, first_irq, 0, size);
        }

        return domain;
}
EXPORT_SYMBOL_GPL(irq_domain_add_simple);

11.  of_node_to_fwnode

//include/linux/irqdomain.h
static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
{
        return node ? &node->fwnode : NULL;
}


//include/linux/fwnode.h
/*
 * fwnode.h - Firmware device node object handle type definition.
 *
 * Copyright (C) 2015, Intel Corporation
 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.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.
 */

#ifndef _LINUX_FWNODE_H_
#define _LINUX_FWNODE_H_

enum fwnode_type {
        FWNODE_INVALID = 0,
        FWNODE_OF,
        FWNODE_ACPI,
        FWNODE_ACPI_DATA,
        FWNODE_PDATA,
        FWNODE_IRQCHIP,
};

struct fwnode_handle {
        enum fwnode_type type;
        struct fwnode_handle *secondary;
};

#endif

 12. __irq_domain_add

/**
 * __irq_domain_add() - Allocate a new irq_domain data structure
 * @of_node: optional device-tree node of the interrupt controller
 * @size: Size of linear map; 0 for radix mapping only
 * @hwirq_max: Maximum number of interrupts supported by controller
 * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
 *              direct mapping
 * @ops: domain callbacks
 * @host_data: Controller private data pointer
 *
 * Allocates and initialize and irq_domain structure.
 * Returns pointer to IRQ domain, or NULL on failure.
 */
struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    irq_hw_number_t hwirq_max, int direct_max,
                                    const struct irq_domain_ops *ops,
                                    void *host_data)
{
        struct irq_domain *domain;
        struct device_node *of_node;

        of_node = to_of_node(fwnode);

        domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
                              GFP_KERNEL, of_node_to_nid(of_node));
        if (WARN_ON(!domain))
                return NULL;

        of_node_get(of_node);

        /* Fill structure */
        INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
        domain->ops = ops;
        domain->host_data = host_data;
        domain->fwnode = fwnode;
        domain->hwirq_max = hwirq_max;
        domain->revmap_size = size;
        domain->revmap_direct_max_irq = direct_max;
        irq_domain_check_hierarchy(domain);

        mutex_lock(&irq_domain_mutex);
        list_add(&domain->link, &irq_domain_list);
        mutex_unlock(&irq_domain_mutex);

        pr_debug("Added domain %s\n", domain->name);
        return domain;
}
EXPORT_SYMBOL_GPL(__irq_domain_add);


//include/linux/slab.h
/**
 * kzalloc_node - allocate zeroed memory from a particular memory node.
 * @size: how many bytes of memory are required.
 * @flags: the type of memory to allocate (see kmalloc).
 * @node: memory node from which to allocate
 */
static inline void *kzalloc_node(size_t size, gfp_t flags, int node)
{
        return kmalloc_node(size, flags | __GFP_ZERO, node);
}


static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node)
{
#ifndef CONFIG_SLOB
        if (__builtin_constant_p(size) &&
                size <= KMALLOC_MAX_CACHE_SIZE && !(flags & GFP_DMA)) {
                int i = kmalloc_index(size);

                if (!i)
                        return ZERO_SIZE_PTR;

                return kmem_cache_alloc_node_trace(kmalloc_caches[i],
                                                flags, node, size);
        }
#endif
        return __kmalloc_node(size, flags, node);
}


static __always_inline void *__kmalloc_node(size_t size, gfp_t flags, int node)
{
        return __kmalloc(size, flags);
}


13.  irq_create_mapping

//kernel/irq/irqdomain.c
/**
 * irq_create_mapping() - Map a hardware interrupt into linux irq space
 * @domain: domain owning this hardware interrupt or NULL for default domain
 * @hwirq: hardware irq number in that domain space
 *
 * Only one mapping per hardware interrupt is permitted. Returns a linux
 * irq number.
 * If the sense/trigger is to be specified, set_irq_type() should be called
 * on the number returned from that call.
 */
unsigned int irq_create_mapping(struct irq_domain *domain,
                                irq_hw_number_t hwirq)
{
        struct device_node *of_node;
        int virq;

        pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);

        /* Look for default domain if nececssary */
        if (domain == NULL)
                domain = irq_default_domain;
        if (domain == NULL) {
                WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
                return 0;
        }
        pr_debug("-> using domain @%p\n", domain);

        of_node = irq_domain_get_of_node(domain);

        /* Check if mapping already exists */
        virq = irq_find_mapping(domain, hwirq);
        if (virq) {
                pr_debug("-> existing mapping on virq %d\n", virq);
                return virq;
        }

        /* Allocate a virtual interrupt number */
        virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node));
        if (virq <= 0) {
                pr_debug("-> virq allocation failed\n");
                return 0;
        }

        if (irq_domain_associate(domain, virq, hwirq)) {
                irq_free_desc(virq);
                return 0;
        }
        pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
                hwirq, of_node_full_name(of_node), virq);

        return virq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);


//include/linux/irqdomain.c
static inline struct device_node *irq_domain_get_of_node(struct irq_domain *d) 
{
        return to_of_node(d->fwnode);
}


//include/linux/of.h
static inline bool is_of_node(struct fwnode_handle *fwnode)
{
        return fwnode && fwnode->type == FWNODE_OF;
}

static inline struct device_node *to_of_node(struct fwnode_handle *fwnode)
{
        return is_of_node(fwnode) ?
                container_of(fwnode, struct device_node, fwnode) : NULL;
}


//include/linux/of.h
#if defined(CONFIG_OF) && defined(CONFIG_NUMA)
extern int of_node_to_nid(struct device_node *np);
#else
static inline int of_node_to_nid(struct device_node *device)
{
        return NUMA_NO_NODE;
}
#endif


14.  irq_domain_alloc_descs

//kernel/irq/irqdomain.c
/*
 *irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node));
 *virq传进来为-1,自动分配虚拟irq编号
 */
static int irq_domain_alloc_descs(int virq, unsigned int cnt, 
                                  irq_hw_number_t hwirq, int node)
{
        unsigned int hint;

        if (virq >= 0) { 
                virq = irq_alloc_descs(virq, virq, cnt, node);
        } else {
                hint = hwirq % nr_irqs;
                if (hint == 0)
                        hint++;
                /*
                 *此处添加打印
                 *early_print("%s,%d,hwirq:0x%x,hint:0x%x,nr_irqs:0x%x\n"
                 *,__FILE__,__LINE__,hwirq,hint,nr_irqs);打印信息参看下面
                 *通过打印信息得治nr_irqs每分配一个irq_desc,nr_irqs就会增加1.
                 */
                virq = irq_alloc_descs_from(hint, cnt, node);
                if (virq <= 0 && hint > 1) 
                        virq = irq_alloc_descs_from(1, cnt, node);
        }
                        /*
       /*此处添加打印
        *early_print("%s,%d,hwirq:0x%x,hint:0x%x\n",__FILE__,__LINE__,hwirq,hint);
        *打印信息如下
        */
        return virq;
}

kernel/irq/irqdomain.c,847,hwirq:0x0,hint:0x1,nr_irqs:0x10
kernel/irq/irqdomain.c,852,hwirq:0x0,virq:0x10
kernel/irq/irqdomain.c,847,hwirq:0x1,hint:0x1,nr_irqs:0x11
kernel/irq/irqdomain.c,852,hwirq:0x1,virq:0x11
kernel/irq/irqdomain.c,847,hwirq:0x2,hint:0x2,nr_irqs:0x12
kernel/irq/irqdomain.c,852,hwirq:0x2,virq:0x12
kernel/irq/irqdomain.c,847,hwirq:0x3,hint:0x3,nr_irqs:0x13
kernel/irq/irqdomain.c,852,hwirq:0x3,virq:0x13
kernel/irq/irqdomain.c,847,hwirq:0x4,hint:0x4,nr_irqs:0x14
kernel/irq/irqdomain.c,852,hwirq:0x4,virq:0x14
kernel/irq/irqdomain.c,847,hwirq:0x5,hint:0x5,nr_irqs:0x15
kernel/irq/irqdomain.c,852,hwirq:0x5,virq:0x15
kernel/irq/irqdomain.c,847,hwirq:0x6,hint:0x6,nr_irqs:0x16
kernel/irq/irqdomain.c,852,hwirq:0x6,virq:0x16
kernel/irq/irqdomain.c,847,hwirq:0x7,hint:0x7,nr_irqs:0x17
kernel/irq/irqdomain.c,852,hwirq:0x7,virq:0x17
kernel/irq/irqdomain.c,847,hwirq:0x8,hint:0x8,nr_irqs:0x18
kernel/irq/irqdomain.c,852,hwirq:0x8,virq:0x18
kernel/irq/irqdomain.c,847,hwirq:0x9,hint:0x9,nr_irqs:0x19
kernel/irq/irqdomain.c,852,hwirq:0x9,virq:0x19
kernel/irq/irqdomain.c,847,hwirq:0xa,hint:0xa,nr_irqs:0x1a
kernel/irq/irqdomain.c,852,hwirq:0xa,virq:0x1a
kernel/irq/irqdomain.c,847,hwirq:0xb,hint:0xb,nr_irqs:0x1b
kernel/irq/irqdomain.c,852,hwirq:0xb,virq:0x1b
kernel/irq/irqdomain.c,847,hwirq:0xc,hint:0xc,nr_irqs:0x1c
kernel/irq/irqdomain.c,852,hwirq:0xc,virq:0x1c
kernel/irq/irqdomain.c,847,hwirq:0xd,hint:0xd,nr_irqs:0x1d
kernel/irq/irqdomain.c,852,hwirq:0xd,virq:0x1d
kernel/irq/irqdomain.c,847,hwirq:0xe,hint:0xe,nr_irqs:0x1e
kernel/irq/irqdomain.c,852,hwirq:0xe,virq:0x1e
kernel/irq/irqdomain.c,847,hwirq:0xf,hint:0xf,nr_irqs:0x1f
kernel/irq/irqdomain.c,852,hwirq:0xf,virq:0x1f
kernel/irq/irqdomain.c,847,hwirq:0x10,hint:0x10,nr_irqs:0x20
kernel/irq/irqdomain.c,852,hwirq:0x10,virq:0x20
kernel/irq/irqdomain.c,847,hwirq:0x11,hint:0x11,nr_irqs:0x21
kernel/irq/irqdomain.c,852,hwirq:0x11,virq:0x21
kernel/irq/irqdomain.c,847,hwirq:0x12,hint:0x12,nr_irqs:0x22
kernel/irq/irqdomain.c,852,hwirq:0x12,virq:0x22
kernel/irq/irqdomain.c,847,hwirq:0x13,hint:0x13,nr_irqs:0x23
kernel/irq/irqdomain.c,852,hwirq:0x13,virq:0x23
kernel/irq/irqdomain.c,847,hwirq:0x14,hint:0x14,nr_irqs:0x24
kernel/irq/irqdomain.c,852,hwirq:0x14,virq:0x24
kernel/irq/irqdomain.c,847,hwirq:0x15,hint:0x15,nr_irqs:0x25
kernel/irq/irqdomain.c,852,hwirq:0x15,virq:0x25
kernel/irq/irqdomain.c,847,hwirq:0x16,hint:0x16,nr_irqs:0x26
kernel/irq/irqdomain.c,852,hwirq:0x16,virq:0x26
kernel/irq/irqdomain.c,847,hwirq:0x17,hint:0x17,nr_irqs:0x27
kernel/irq/irqdomain.c,852,hwirq:0x17,virq:0x27
kernel/irq/irqdomain.c,847,hwirq:0x18,hint:0x18,nr_irqs:0x28
kernel/irq/irqdomain.c,852,hwirq:0x18,virq:0x28
kernel/irq/irqdomain.c,847,hwirq:0x19,hint:0x19,nr_irqs:0x29
kernel/irq/irqdomain.c,852,hwirq:0x19,virq:0x29
kernel/irq/irqdomain.c,847,hwirq:0x1a,hint:0x1a,nr_irqs:0x2a
kernel/irq/irqdomain.c,852,hwirq:0x1a,virq:0x2a
kernel/irq/irqdomain.c,847,hwirq:0x1b,hint:0x1b,nr_irqs:0x2b
kernel/irq/irqdomain.c,852,hwirq:0x1b,virq:0x2b
kernel/irq/irqdomain.c,847,hwirq:0x1c,hint:0x1c,nr_irqs:0x2c
kernel/irq/irqdomain.c,852,hwirq:0x1c,virq:0x2c
kernel/irq/irqdomain.c,847,hwirq:0x1d,hint:0x1d,nr_irqs:0x2d
kernel/irq/irqdomain.c,852,hwirq:0x1d,virq:0x2d
kernel/irq/irqdomain.c,847,hwirq:0x1e,hint:0x1e,nr_irqs:0x2e
kernel/irq/irqdomain.c,852,hwirq:0x1e,virq:0x2e
kernel/irq/irqdomain.c,847,hwirq:0x1f,hint:0x1f,nr_irqs:0x2f
kernel/irq/irqdomain.c,852,hwirq:0x1f,virq:0x2f
kernel/irq/irqdomain.c,847,hwirq:0x0,hint:0x1,nr_irqs:0x30
kernel/irq/irqdomain.c,852,hwirq:0x0,virq:0x30
kernel/irq/irqdomain.c,847,hwirq:0x1,hint:0x1,nr_irqs:0x31
kernel/irq/irqdomain.c,852,hwirq:0x1,virq:0x31
kernel/irq/irqdomain.c,847,hwirq:0x2,hint:0x2,nr_irqs:0x32
kernel/irq/irqdomain.c,852,hwirq:0x2,virq:0x32
kernel/irq/irqdomain.c,847,hwirq:0x3,hint:0x3,nr_irqs:0x33
kernel/irq/irqdomain.c,852,hwirq:0x3,virq:0x33
kernel/irq/irqdomain.c,847,hwirq:0x4,hint:0x4,nr_irqs:0x34
kernel/irq/irqdomain.c,852,hwirq:0x4,virq:0x34
kernel/irq/irqdomain.c,847,hwirq:0x5,hint:0x5,nr_irqs:0x35
kernel/irq/irqdomain.c,852,hwirq:0x5,virq:0x35
kernel/irq/irqdomain.c,847,hwirq:0x6,hint:0x6,nr_irqs:0x36
kernel/irq/irqdomain.c,852,hwirq:0x6,virq:0x36
kernel/irq/irqdomain.c,847,hwirq:0x7,hint:0x7,nr_irqs:0x37
kernel/irq/irqdomain.c,852,hwirq:0x7,virq:0x37
kernel/irq/irqdomain.c,847,hwirq:0x8,hint:0x8,nr_irqs:0x38
kernel/irq/irqdomain.c,852,hwirq:0x8,virq:0x38
kernel/irq/irqdomain.c,847,hwirq:0x9,hint:0x9,nr_irqs:0x39
kernel/irq/irqdomain.c,852,hwirq:0x9,virq:0x39
kernel/irq/irqdomain.c,847,hwirq:0xa,hint:0xa,nr_irqs:0x3a
kernel/irq/irqdomain.c,852,hwirq:0xa,virq:0x3a
kernel/irq/irqdomain.c,847,hwirq:0xb,hint:0xb,nr_irqs:0x3b
kernel/irq/irqdomain.c,852,hwirq:0xb,virq:0x3b
kernel/irq/irqdomain.c,847,hwirq:0xc,hint:0xc,nr_irqs:0x3c
kernel/irq/irqdomain.c,852,hwirq:0xc,virq:0x3c
kernel/irq/irqdomain.c,847,hwirq:0xd,hint:0xd,nr_irqs:0x3d
kernel/irq/irqdomain.c,852,hwirq:0xd,virq:0x3d
kernel/irq/irqdomain.c,847,hwirq:0xe,hint:0xe,nr_irqs:0x3e
kernel/irq/irqdomain.c,852,hwirq:0xe,virq:0x3e
kernel/irq/irqdomain.c,847,hwirq:0xf,hint:0xf,nr_irqs:0x3f
kernel/irq/irqdomain.c,852,hwirq:0xf,virq:0x3f
kernel/irq/irqdomain.c,847,hwirq:0x10,hint:0x10,nr_irqs:0x40
kernel/irq/irqdomain.c,852,hwirq:0x10,virq:0x40
kernel/irq/irqdomain.c,847,hwirq:0x11,hint:0x11,nr_irqs:0x41
kernel/irq/irqdomain.c,852,hwirq:0x11,virq:0x41
kernel/irq/irqdomain.c,847,hwirq:0x12,hint:0x12,nr_irqs:0x42
kernel/irq/irqdomain.c,852,hwirq:0x12,virq:0x42
kernel/irq/irqdomain.c,847,hwirq:0x13,hint:0x13,nr_irqs:0x43
kernel/irq/irqdomain.c,852,hwirq:0x13,virq:0x43
kernel/irq/irqdomain.c,847,hwirq:0x14,hint:0x14,nr_irqs:0x44
kernel/irq/irqdomain.c,852,hwirq:0x14,virq:0x44
kernel/irq/irqdomain.c,847,hwirq:0x15,hint:0x15,nr_irqs:0x45
kernel/irq/irqdomain.c,852,hwirq:0x15,virq:0x45
kernel/irq/irqdomain.c,847,hwirq:0x16,hint:0x16,nr_irqs:0x46
kernel/irq/irqdomain.c,852,hwirq:0x16,virq:0x46
kernel/irq/irqdomain.c,847,hwirq:0x17,hint:0x17,nr_irqs:0x47
kernel/irq/irqdomain.c,852,hwirq:0x17,virq:0x47
kernel/irq/irqdomain.c,847,hwirq:0x18,hint:0x18,nr_irqs:0x48
kernel/irq/irqdomain.c,852,hwirq:0x18,virq:0x48
kernel/irq/irqdomain.c,847,hwirq:0x19,hint:0x19,nr_irqs:0x49
kernel/irq/irqdomain.c,852,hwirq:0x19,virq:0x49
kernel/irq/irqdomain.c,847,hwirq:0x1a,hint:0x1a,nr_irqs:0x4a
kernel/irq/irqdomain.c,852,hwirq:0x1a,virq:0x4a
kernel/irq/irqdomain.c,847,hwirq:0x1b,hint:0x1b,nr_irqs:0x4b
kernel/irq/irqdomain.c,852,hwirq:0x1b,virq:0x4b
kernel/irq/irqdomain.c,847,hwirq:0x1c,hint:0x1c,nr_irqs:0x4c
kernel/irq/irqdomain.c,852,hwirq:0x1c,virq:0x4c
kernel/irq/irqdomain.c,847,hwirq:0x1d,hint:0x1d,nr_irqs:0x4d
kernel/irq/irqdomain.c,852,hwirq:0x1d,virq:0x4d
kernel/irq/irqdomain.c,847,hwirq:0x1e,hint:0x1e,nr_irqs:0x4e
kernel/irq/irqdomain.c,852,hwirq:0x1e,virq:0x4e
kernel/irq/irqdomain.c,847,hwirq:0x1f,hint:0x1f,nr_irqs:0x4f
kernel/irq/irqdomain.c,852,hwirq:0x1f,virq:0x4f
kernel/irq/irqdomain.c,847,hwirq:0x0,hint:0x1,nr_irqs:0x50
kernel/irq/irqdomain.c,852,hwirq:0x0,virq:0x50
kernel/irq/irqdomain.c,847,hwirq:0x1,hint:0x1,nr_irqs:0x51
kernel/irq/irqdomain.c,852,hwirq:0x1,virq:0x51
kernel/irq/irqdomain.c,847,hwirq:0x2,hint:0x2,nr_irqs:0x52
kernel/irq/irqdomain.c,852,hwirq:0x2,virq:0x52
kernel/irq/irqdomain.c,847,hwirq:0x3,hint:0x3,nr_irqs:0x53
kernel/irq/irqdomain.c,852,hwirq:0x3,virq:0x53
kernel/irq/irqdomain.c,847,hwirq:0x4,hint:0x4,nr_irqs:0x54
kernel/irq/irqdomain.c,852,hwirq:0x4,virq:0x54
kernel/irq/irqdomain.c,847,hwirq:0x5,hint:0x5,nr_irqs:0x55
kernel/irq/irqdomain.c,852,hwirq:0x5,virq:0x55
kernel/irq/irqdomain.c,847,hwirq:0x6,hint:0x6,nr_irqs:0x56
kernel/irq/irqdomain.c,852,hwirq:0x6,virq:0x56
kernel/irq/irqdomain.c,847,hwirq:0x7,hint:0x7,nr_irqs:0x57
kernel/irq/irqdomain.c,852,hwirq:0x7,virq:0x57
kernel/irq/irqdomain.c,847,hwirq:0x8,hint:0x8,nr_irqs:0x58
kernel/irq/irqdomain.c,852,hwirq:0x8,virq:0x58
kernel/irq/irqdomain.c,847,hwirq:0x9,hint:0x9,nr_irqs:0x59
kernel/irq/irqdomain.c,852,hwirq:0x9,virq:0x59
kernel/irq/irqdomain.c,847,hwirq:0xa,hint:0xa,nr_irqs:0x5a
kernel/irq/irqdomain.c,852,hwirq:0xa,virq:0x5a
kernel/irq/irqdomain.c,847,hwirq:0xb,hint:0xb,nr_irqs:0x5b
kernel/irq/irqdomain.c,852,hwirq:0xb,virq:0x5b
kernel/irq/irqdomain.c,847,hwirq:0xc,hint:0xc,nr_irqs:0x5c
kernel/irq/irqdomain.c,852,hwirq:0xc,virq:0x5c
kernel/irq/irqdomain.c,847,hwirq:0xd,hint:0xd,nr_irqs:0x5d
kernel/irq/irqdomain.c,852,hwirq:0xd,virq:0x5d
kernel/irq/irqdomain.c,847,hwirq:0xe,hint:0xe,nr_irqs:0x5e
kernel/irq/irqdomain.c,852,hwirq:0xe,virq:0x5e
kernel/irq/irqdomain.c,847,hwirq:0xf,hint:0xf,nr_irqs:0x5f
kernel/irq/irqdomain.c,852,hwirq:0xf,virq:0x5f
kernel/irq/irqdomain.c,847,hwirq:0x10,hint:0x10,nr_irqs:0x60
kernel/irq/irqdomain.c,852,hwirq:0x10,virq:0x60
kernel/irq/irqdomain.c,847,hwirq:0x11,hint:0x11,nr_irqs:0x61
kernel/irq/irqdomain.c,852,hwirq:0x11,virq:0x61
kernel/irq/irqdomain.c,847,hwirq:0x12,hint:0x12,nr_irqs:0x62
kernel/irq/irqdomain.c,852,hwirq:0x12,virq:0x62
kernel/irq/irqdomain.c,847,hwirq:0x13,hint:0x13,nr_irqs:0x63
kernel/irq/irqdomain.c,852,hwirq:0x13,virq:0x63
kernel/irq/irqdomain.c,847,hwirq:0x14,hint:0x14,nr_irqs:0x64
kernel/irq/irqdomain.c,852,hwirq:0x14,virq:0x64
kernel/irq/irqdomain.c,847,hwirq:0x15,hint:0x15,nr_irqs:0x65
kernel/irq/irqdomain.c,852,hwirq:0x15,virq:0x65
kernel/irq/irqdomain.c,847,hwirq:0x16,hint:0x16,nr_irqs:0x66
kernel/irq/irqdomain.c,852,hwirq:0x16,virq:0x66
kernel/irq/irqdomain.c,847,hwirq:0x17,hint:0x17,nr_irqs:0x67
kernel/irq/irqdomain.c,852,hwirq:0x17,virq:0x67
kernel/irq/irqdomain.c,847,hwirq:0x18,hint:0x18,nr_irqs:0x68
kernel/irq/irqdomain.c,852,hwirq:0x18,virq:0x68
kernel/irq/irqdomain.c,847,hwirq:0x19,hint:0x19,nr_irqs:0x69
kernel/irq/irqdomain.c,852,hwirq:0x19,virq:0x69
kernel/irq/irqdomain.c,847,hwirq:0x1a,hint:0x1a,nr_irqs:0x6a
kernel/irq/irqdomain.c,852,hwirq:0x1a,virq:0x6a
kernel/irq/irqdomain.c,847,hwirq:0x1b,hint:0x1b,nr_irqs:0x6b
kernel/irq/irqdomain.c,852,hwirq:0x1b,virq:0x6b
kernel/irq/irqdomain.c,847,hwirq:0x1c,hint:0x1c,nr_irqs:0x6c
kernel/irq/irqdomain.c,852,hwirq:0x1c,virq:0x6c
kernel/irq/irqdomain.c,847,hwirq:0x1d,hint:0x1d,nr_irqs:0x6d
kernel/irq/irqdomain.c,852,hwirq:0x1d,virq:0x6d
kernel/irq/irqdomain.c,847,hwirq:0x1e,hint:0x1e,nr_irqs:0x6e
kernel/irq/irqdomain.c,852,hwirq:0x1e,virq:0x6e
kernel/irq/irqdomain.c,847,hwirq:0x1f,hint:0x1f,nr_irqs:0x6f
kernel/irq/irqdomain.c,852,hwirq:0x1f,virq:0x6f
kernel/irq/irqdomain.c,847,hwirq:0x0,hint:0x1,nr_irqs:0x70
kernel/irq/irqdomain.c,852,hwirq:0x0,virq:0x70
kernel/irq/irqdomain.c,847,hwirq:0x1,hint:0x1,nr_irqs:0x71
kernel/irq/irqdomain.c,852,hwirq:0x1,virq:0x71
kernel/irq/irqdomain.c,847,hwirq:0x2,hint:0x2,nr_irqs:0x72
kernel/irq/irqdomain.c,852,hwirq:0x2,virq:0x72
kernel/irq/irqdomain.c,847,hwirq:0x3,hint:0x3,nr_irqs:0x73
kernel/irq/irqdomain.c,852,hwirq:0x3,virq:0x73
kernel/irq/irqdomain.c,847,hwirq:0x4,hint:0x4,nr_irqs:0x74
kernel/irq/irqdomain.c,852,hwirq:0x4,virq:0x74
kernel/irq/irqdomain.c,847,hwirq:0x5,hint:0x5,nr_irqs:0x75
kernel/irq/irqdomain.c,852,hwirq:0x5,virq:0x75
kernel/irq/irqdomain.c,847,hwirq:0x6,hint:0x6,nr_irqs:0x76
kernel/irq/irqdomain.c,852,hwirq:0x6,virq:0x76
kernel/irq/irqdomain.c,847,hwirq:0x7,hint:0x7,nr_irqs:0x77
kernel/irq/irqdomain.c,852,hwirq:0x7,virq:0x77
kernel/irq/irqdomain.c,847,hwirq:0x8,hint:0x8,nr_irqs:0x78
kernel/irq/irqdomain.c,852,hwirq:0x8,virq:0x78
kernel/irq/irqdomain.c,847,hwirq:0x9,hint:0x9,nr_irqs:0x79
kernel/irq/irqdomain.c,852,hwirq:0x9,virq:0x79
kernel/irq/irqdomain.c,847,hwirq:0xa,hint:0xa,nr_irqs:0x7a
kernel/irq/irqdomain.c,852,hwirq:0xa,virq:0x7a
kernel/irq/irqdomain.c,847,hwirq:0xb,hint:0xb,nr_irqs:0x7b
kernel/irq/irqdomain.c,852,hwirq:0xb,virq:0x7b
kernel/irq/irqdomain.c,847,hwirq:0xc,hint:0xc,nr_irqs:0x7c
kernel/irq/irqdomain.c,852,hwirq:0xc,virq:0x7c
kernel/irq/irqdomain.c,847,hwirq:0xd,hint:0xd,nr_irqs:0x7d
kernel/irq/irqdomain.c,852,hwirq:0xd,virq:0x7d
kernel/irq/irqdomain.c,847,hwirq:0xe,hint:0xe,nr_irqs:0x7e
kernel/irq/irqdomain.c,852,hwirq:0xe,virq:0x7e
kernel/irq/irqdomain.c,847,hwirq:0xf,hint:0xf,nr_irqs:0x7f
kernel/irq/irqdomain.c,852,hwirq:0xf,virq:0x7f
kernel/irq/irqdomain.c,847,hwirq:0x10,hint:0x10,nr_irqs:0x80
kernel/irq/irqdomain.c,852,hwirq:0x10,virq:0x80
kernel/irq/irqdomain.c,847,hwirq:0x11,hint:0x11,nr_irqs:0x81
kernel/irq/irqdomain.c,852,hwirq:0x11,virq:0x81
kernel/irq/irqdomain.c,847,hwirq:0x12,hint:0x12,nr_irqs:0x82
kernel/irq/irqdomain.c,852,hwirq:0x12,virq:0x82
kernel/irq/irqdomain.c,847,hwirq:0x13,hint:0x13,nr_irqs:0x83
kernel/irq/irqdomain.c,852,hwirq:0x13,virq:0x83
kernel/irq/irqdomain.c,847,hwirq:0x14,hint:0x14,nr_irqs:0x84
kernel/irq/irqdomain.c,852,hwirq:0x14,virq:0x84
kernel/irq/irqdomain.c,847,hwirq:0x15,hint:0x15,nr_irqs:0x85
kernel/irq/irqdomain.c,852,hwirq:0x15,virq:0x85
kernel/irq/irqdomain.c,847,hwirq:0x16,hint:0x16,nr_irqs:0x86
kernel/irq/irqdomain.c,852,hwirq:0x16,virq:0x86
kernel/irq/irqdomain.c,847,hwirq:0x17,hint:0x17,nr_irqs:0x87
kernel/irq/irqdomain.c,852,hwirq:0x17,virq:0x87
kernel/irq/irqdomain.c,847,hwirq:0x18,hint:0x18,nr_irqs:0x88
kernel/irq/irqdomain.c,852,hwirq:0x18,virq:0x88
kernel/irq/irqdomain.c,847,hwirq:0x19,hint:0x19,nr_irqs:0x89
kernel/irq/irqdomain.c,852,hwirq:0x19,virq:0x89
kernel/irq/irqdomain.c,847,hwirq:0x1a,hint:0x1a,nr_irqs:0x8a
kernel/irq/irqdomain.c,852,hwirq:0x1a,virq:0x8a
kernel/irq/irqdomain.c,847,hwirq:0x1b,hint:0x1b,nr_irqs:0x8b
kernel/irq/irqdomain.c,852,hwirq:0x1b,virq:0x8b
kernel/irq/irqdomain.c,847,hwirq:0x1c,hint:0x1c,nr_irqs:0x8c
kernel/irq/irqdomain.c,852,hwirq:0x1c,virq:0x8c
kernel/irq/irqdomain.c,847,hwirq:0x1d,hint:0x1d,nr_irqs:0x8d
kernel/irq/irqdomain.c,852,hwirq:0x1d,virq:0x8d
kernel/irq/irqdomain.c,847,hwirq:0x1e,hint:0x1e,nr_irqs:0x8e
kernel/irq/irqdomain.c,852,hwirq:0x1e,virq:0x8e
kernel/irq/irqdomain.c,847,hwirq:0x1f,hint:0x1f,nr_irqs:0x8f
kernel/irq/irqdomain.c,852,hwirq:0x1f,virq:0x8f

15. irq_alloc_descs_from

//include/linux/irq.h
#define irq_alloc_descs(irq, from, cnt, node)   \
        __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE)

#define irq_alloc_desc(node)                    \
        irq_alloc_descs(-1, 0, 1, node)

#define irq_alloc_desc_at(at, node)             \
        irq_alloc_descs(at, at, 1, node)

#define irq_alloc_desc_from(from, node)         \
        irq_alloc_descs(-1, from, 1, node)

#define irq_alloc_descs_from(from, cnt, node)   \
        irq_alloc_descs(-1, from, cnt, node)

16. __irq_alloc_descs

//kernel/irq/irqdesc.c
/**
 * irq_alloc_descs - allocate and initialize a range of irq descriptors
 * @irq:        Allocate for specific irq number if irq >= 0
 * @from:       Start the search from this irq number
 * @cnt:        Number of consecutive irqs to allocate.
 * @node:       Preferred node on which the irq descriptor should be allocated
 * @owner:      Owning module (can be NULL)
 *
 * Returns the first irq number or error code
 */
int __ref
__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
                  struct module *owner)
{
        int start, ret;

        if (!cnt)
                return -EINVAL;

        if (irq >= 0) {
                if (from > irq)
                        return -EINVAL;
                from = irq;
        } else {
                /*
                 * For interrupts which are freely allocated the
                 * architecture can force a lower bound to the @from
                 * argument. x86 uses this to exclude the GSI space.
                 */
                from = arch_dynirq_lower_bound(from);
        }

        mutex_lock(&sparse_irq_lock);

        start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,
                                           from, cnt, 0);
        /*
         *添加打印
         *early_print("%s,%d,start:0x%x,form:0x%x,cnt:0x%x\n",
         *            __FILE__,__LINE__,start,from,cnt);   
         *打印信息如下
         */  
        ret = -EEXIST;
        if (irq >=0 && start != irq)
                goto err;

        if (start + cnt > nr_irqs) {
                ret = irq_expand_nr_irqs(start + cnt);
                if (ret)
                        goto err;
        }

        bitmap_set(allocated_irqs, start, cnt);
        mutex_unlock(&sparse_irq_lock);
        return alloc_descs(start, cnt, node, owner);

err:
        mutex_unlock(&sparse_irq_lock);
        return ret;
}
EXPORT_SYMBOL_GPL(__irq_alloc_descs);


//kernel/softirq.c
unsigned int __weak arch_dynirq_lower_bound(unsigned int from)
{
        return from;
}


kernel/irq/irqdesc.c,457,start:0x10,form:0x1,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x11,form:0x1,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x12,form:0x2,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x13,form:0x3,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x14,form:0x4,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x15,form:0x5,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x16,form:0x6,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x17,form:0x7,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x18,form:0x8,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x19,form:0x9,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x1a,form:0xa,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x1b,form:0xb,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x1c,form:0xc,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x1d,form:0xd,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x1e,form:0xe,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x1f,form:0xf,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x20,form:0x10,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x21,form:0x11,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x22,form:0x12,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x23,form:0x13,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x24,form:0x14,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x25,form:0x15,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x26,form:0x16,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x27,form:0x17,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x28,form:0x18,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x29,form:0x19,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x2a,form:0x1a,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x2b,form:0x1b,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x2c,form:0x1c,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x2d,form:0x1d,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x2e,form:0x1e,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x2f,form:0x1f,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x30,form:0x1,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x31,form:0x1,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x32,form:0x2,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x33,form:0x3,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x34,form:0x4,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x35,form:0x5,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x36,form:0x6,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x37,form:0x7,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x38,form:0x8,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x39,form:0x9,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x3a,form:0xa,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x3b,form:0xb,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x3c,form:0xc,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x3d,form:0xd,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x3e,form:0xe,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x3f,form:0xf,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x40,form:0x10,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x41,form:0x11,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x42,form:0x12,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x43,form:0x13,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x44,form:0x14,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x45,form:0x15,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x46,form:0x16,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x47,form:0x17,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x48,form:0x18,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x49,form:0x19,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x4a,form:0x1a,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x4b,form:0x1b,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x4c,form:0x1c,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x4d,form:0x1d,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x4e,form:0x1e,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x4f,form:0x1f,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x50,form:0x1,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x51,form:0x1,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x52,form:0x2,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x53,form:0x3,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x54,form:0x4,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x55,form:0x5,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x56,form:0x6,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x57,form:0x7,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x58,form:0x8,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x59,form:0x9,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x5a,form:0xa,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x5b,form:0xb,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x5c,form:0xc,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x5d,form:0xd,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x5e,form:0xe,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x5f,form:0xf,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x60,form:0x10,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x61,form:0x11,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x62,form:0x12,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x63,form:0x13,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x64,form:0x14,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x65,form:0x15,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x66,form:0x16,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x67,form:0x17,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x68,form:0x18,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x69,form:0x19,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x6a,form:0x1a,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x6b,form:0x1b,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x6c,form:0x1c,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x6d,form:0x1d,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x6e,form:0x1e,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x6f,form:0x1f,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x70,form:0x1,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x71,form:0x1,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x72,form:0x2,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x73,form:0x3,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x74,form:0x4,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x75,form:0x5,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x76,form:0x6,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x77,form:0x7,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x78,form:0x8,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x79,form:0x9,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x7a,form:0xa,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x7b,form:0xb,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x7c,form:0xc,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x7d,form:0xd,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x7e,form:0xe,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x7f,form:0xf,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x80,form:0x10,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x81,form:0x11,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x82,form:0x12,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x83,form:0x13,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x84,form:0x14,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x85,form:0x15,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x86,form:0x16,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x87,form:0x17,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x88,form:0x18,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x89,form:0x19,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x8a,form:0x1a,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x8b,form:0x1b,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x8c,form:0x1c,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x8d,form:0x1d,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x8e,form:0x1e,cnt:0x1
kernel/irq/irqdesc.c,457,start:0x8f,form:0x1f,cnt:0x1


//kernel/irq/irqdesc.c
static int irq_expand_nr_irqs(unsigned int nr) 
{
        if (nr > IRQ_BITMAP_BITS)
                return -ENOMEM;
        nr_irqs = nr; 
        return 0;
}

17. alloc_descs

//kernel/irq/irqdesc.c
static int alloc_descs(unsigned int start, unsigned int cnt, int node,
                       struct module *owner)
{
        struct irq_desc *desc;
        int i;

        for (i = 0; i < cnt; i++) {
                desc = alloc_desc(start + i, node, owner);
                if (!desc)
                        goto err;
                mutex_lock(&sparse_irq_lock);
                irq_insert_desc(start + i, desc);
                mutex_unlock(&sparse_irq_lock);
        }
        return start;

err:
        for (i--; i >= 0; i--)
                free_desc(start + i);

        mutex_lock(&sparse_irq_lock);
        bitmap_clear(allocated_irqs, start, cnt);
        mutex_unlock(&sparse_irq_lock);
        return -ENOMEM;
}

18.  alloc_desc

//kernel/irq/irqdesc.c
static struct irq_desc *alloc_desc(int irq, int node, struct module *owner)
{
        struct irq_desc *desc;
        gfp_t gfp = GFP_KERNEL;

        desc = kzalloc_node(sizeof(*desc), gfp, node);
        if (!desc)
                return NULL;
        /* allocate based on nr_cpu_ids */
        desc->kstat_irqs = alloc_percpu(unsigned int);
        if (!desc->kstat_irqs)
                goto err_desc;

        if (alloc_masks(desc, gfp, node))
                goto err_kstat;

        raw_spin_lock_init(&desc->lock);
        lockdep_set_class(&desc->lock, &irq_desc_lock_class);

        desc_set_defaults(irq, desc, node, owner);

        return desc;

err_kstat:
        free_percpu(desc->kstat_irqs);
err_desc:
        kfree(desc);
        return NULL;
}

19. desc_set_defaults

//kernel/irq/irqdesc.c
static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
                struct module *owner)
{
        int cpu;

        desc->irq_common_data.handler_data = NULL;
        desc->irq_common_data.msi_desc = NULL;

        desc->irq_data.common = &desc->irq_common_data;
        desc->irq_data.irq = irq;
        desc->irq_data.chip = &no_irq_chip;
        desc->irq_data.chip_data = NULL;
        irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS);
        irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
        desc->handle_irq = handle_bad_irq;
        desc->depth = 1;
        desc->irq_count = 0;
        desc->irqs_unhandled = 0;
        desc->name = NULL;
        desc->owner = owner;
        for_each_possible_cpu(cpu)
                *per_cpu_ptr(desc->kstat_irqs, cpu) = 0;
        desc_smp_init(desc, node);
}

//kernel/irq/settings.h
#define IRQ_PER_CPU             GOT_YOU_MORON
#define IRQ_NO_BALANCING        GOT_YOU_MORON
#define IRQ_LEVEL               GOT_YOU_MORON
#define IRQ_NOPROBE             GOT_YOU_MORON
#define IRQ_NOREQUEST           GOT_YOU_MORON
#define IRQ_NOTHREAD            GOT_YOU_MORON
#define IRQ_NOAUTOEN            GOT_YOU_MORON
#define IRQ_NESTED_THREAD       GOT_YOU_MORON
#define IRQ_PER_CPU_DEVID       GOT_YOU_MORON
#define IRQ_IS_POLLED           GOT_YOU_MORON
#define IRQ_DISABLE_UNLAZY      GOT_YOU_MORON
#undef IRQF_MODIFY_MASK
#define IRQF_MODIFY_MASK        GOT_YOU_MORON

static inline void
irq_settings_clr_and_set(struct irq_desc *desc, u32 clr, u32 set)
{
        desc->status_use_accessors &= ~(clr & _IRQF_MODIFY_MASK);
        desc->status_use_accessors |= (set & _IRQF_MODIFY_MASK);
}

20. irq_domain_associate

//kernel/irq/irqdomain.c
int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
                         irq_hw_number_t hwirq)
{
        struct irq_data *irq_data = irq_get_irq_data(virq);
        int ret; 

        if (WARN(hwirq >= domain->hwirq_max,
                 "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
                return -EINVAL;
        if (WARN(!irq_data, "error: virq%i is not allocated", virq))
                return -EINVAL;
        if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
                return -EINVAL;

        mutex_lock(&irq_domain_mutex);
        irq_data->hwirq = hwirq;
        irq_data->domain = domain;
        if (domain->ops->map) {
                /*
                 *从driver/irqchip/irq-vic.c分析完成了irq_data中irq_chip的绑定,见下面22
                 */
                ret = domain->ops->map(domain, virq, hwirq);
                if (ret != 0) {
 /*
                         * If map() returns -EPERM, this interrupt is protected
                         * by the firmware or some other service and shall not
                         * be mapped. Don't bother telling the user about it.
                         */
                        if (ret != -EPERM) {
                                pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
                                       domain->name, hwirq, virq, ret);
                        }
                        irq_data->domain = NULL;
                        irq_data->hwirq = 0;
                        mutex_unlock(&irq_domain_mutex);
                        return ret;
                }

                /* If not already assigned, give the domain the chip's name */
                if (!domain->name && irq_data->chip)
                        domain->name = irq_data->chip->name;
        }

        if (hwirq < domain->revmap_size) {
                domain->linear_revmap[hwirq] = virq;
        } else {
                mutex_lock(&revmap_trees_mutex);
                radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
                mutex_unlock(&revmap_trees_mutex);
        }
        mutex_unlock(&irq_domain_mutex);

        irq_clear_status_flags(virq, IRQ_NOREQUEST);

        return 0;
}
EXPORT_SYMBOL_GPL(irq_domain_associate);


//kernel/irq/chip.c
struct irq_data *irq_get_irq_data(unsigned int irq) 
{
        struct irq_desc *desc = irq_to_desc(irq);

        return desc ? &desc->irq_data : NULL;
}


//kernel/irq/irqdesc.c
struct irq_desc *irq_to_desc(unsigned int irq)
{
        return radix_tree_lookup(&irq_desc_tree, irq);
}
EXPORT_SYMBOL(irq_to_desc);

21. irq_find_mapping

//kernel/irq/irqdomain.c
/**
 * irq_find_mapping() - Find a linux irq from an hw irq number.
 * @domain: domain owning this hardware interrupt
 * @hwirq: hardware irq number in that domain space
 */
unsigned int irq_find_mapping(struct irq_domain *domain,
                              irq_hw_number_t hwirq)
{
        struct irq_data *data;

        /* Look for default domain if nececssary */
        if (domain == NULL)
                domain = irq_default_domain;
        if (domain == NULL)
                return 0;

        if (hwirq < domain->revmap_direct_max_irq) { //代表的hwirq和virq是直接映射
                data = irq_domain_get_irq_data(domain, hwirq);
                if (data && data->hwirq == hwirq)
                        return hwirq;
        }

        /* Check if the hwirq is in the linear revmap. */
        if (hwirq < domain->revmap_size)
                return domain->linear_revmap[hwirq];

        rcu_read_lock();
        data = radix_tree_lookup(&domain->revmap_tree, hwirq);
        rcu_read_unlock();
        return data ? data->irq : 0;
}
EXPORT_SYMBOL_GPL(irq_find_mapping);


//kernel/irq/irqdomain.c
/**
 * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain
 * @domain:     domain to match
 * @virq:       IRQ number to get irq_data
 */
struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
                                         unsigned int virq)
{
        struct irq_data *irq_data = irq_get_irq_data(virq);

        return (irq_data && irq_data->domain == domain) ? irq_data : NULL;
}

22. 

//drivers/irqchip/irq-vic.c
static const struct irq_domain_ops vic_irqdomain_ops = { 
        /*
         *map函数的调用流程为irq_create_mapping-->irq_domain_associate-->map
         */
        .map = vic_irqdomain_map,  
        .xlate = irq_domain_xlate_onetwocell,
};


//drivers/irqchip/irq-vic.c
/*
 *在drivers/irqchip/irq-vic.c中的vic_register函数中调用
 *v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,&vic_irqdomain_ops, v);
 * 填充到了struct domain结构体变量中                                      
 */

static int vic_irqdomain_map(struct irq_domain *d, unsigned int irq,
                             irq_hw_number_t hwirq)
{
        struct vic_device *v = d->host_data;

        /* Skip invalid IRQs, only register handlers for the real ones */
        if (!(v->valid_sources & (1 << hwirq)))
                return -EPERM;
        irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq);
        irq_set_chip_data(irq, v->base); 
        irq_set_probe(irq);
        return 0;
}


//include/linux/irq.h
static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip,
                                            irq_flow_handler_t handle)
{
        irq_set_chip_and_handler_name(irq, chip, handle, NULL);
}


//kernel/irq/chip.c
void irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
                              irq_flow_handler_t handle, const char *name)
{
        irq_set_chip(irq, chip);
        __irq_set_handler(irq, handle, 0, name);
}
EXPORT_SYMBOL_GPL(irq_set_chip_and_handler_name);


//kernel/irq/chip.c
/**
 *      irq_set_chip - set the irq chip for an irq
 *      @irq:   irq number
 *      @chip:  pointer to irq chip description structure
 */
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
        unsigned long flags;
        struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);

        if (!desc)
                return -EINVAL;

        if (!chip)
                chip = &no_irq_chip;

        desc->irq_data.chip = chip;
        irq_put_desc_unlock(desc, flags);
        /*
         * For !CONFIG_SPARSE_IRQ make the irq show up in
         * allocated_irqs.
         */
        irq_mark_irq(irq);
        return 0;
}
EXPORT_SYMBOL(irq_set_chip);


//kernel/irq/chip.c
void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
                  const char *name)
{
        unsigned long flags;
        struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);

        if (!desc)
                return;

        __irq_do_set_handler(desc, handle, is_chained, name);
        irq_put_desc_busunlock(desc, flags);
}
EXPORT_SYMBOL_GPL(__irq_set_handler);


//kernel/irq/chip.c
void __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
                     int is_chained, const char *name)
{
        if (!handle) {
                handle = handle_bad_irq;
        } else {
                struct irq_data *irq_data = &desc->irq_data;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
                /*
                 * With hierarchical domains we might run into a
                 * situation where the outermost chip is not yet set
                 * up, but the inner chips are there.  Instead of
                 * bailing we install the handler, but obviously we
                 * cannot enable/startup the interrupt at this point.
                 */
                 while (irq_data) {
                        if (irq_data->chip != &no_irq_chip)
                                break;
                        /*
                         * Bail out if the outer chip is not set up
                         * and the interrrupt supposed to be started
                         * right away.
                         */
                        if (WARN_ON(is_chained))
                                return;
                        /* Try the parent */
                        irq_data = irq_data->parent_data;
                }
#endif
                if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip))
                        return;
        }

        /* Uninstall? */
        if (handle == handle_bad_irq) {
                if (desc->irq_data.chip != &no_irq_chip)
                        mask_ack_irq(desc);
                irq_state_set_disabled(desc);
                if (is_chained)
                        desc->action = NULL;
                desc->depth = 1;
        }
        desc->handle_irq = handle; 
        desc->name = name;

        if (handle != handle_bad_irq && is_chained) {
                irq_settings_set_noprobe(desc);
                irq_settings_set_norequest(desc);
                irq_settings_set_nothread(desc);
                desc->action = &chained_action;
                irq_startup(desc, true);
        }
}



//kernel/irq/chip.c
/**
 *      irq_set_chip_data - set irq chip data for an irq
 *      @irq:   Interrupt number
 *      @data:  Pointer to chip specific data
 *
 *      Set the hardware irq chip data for an irq
 */
int irq_set_chip_data(unsigned int irq, void *data)
{
        unsigned long flags;
        struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);

        if (!desc)
                return -EINVAL;
        desc->irq_data.chip_data = data;  
        irq_put_desc_unlock(desc, flags);
        return 0;
}


//include/linux/irq.h
static inline void irq_set_probe(unsigned int irq)
{
        irq_modify_status(irq, IRQ_NOPROBE, 0); 
}



//kernel/irq/chip.c
void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) 
{
        unsigned long flags;
        struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);

        if (!desc)
                return;
        irq_settings_clr_and_set(desc, clr, set);

        irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU |
                   IRQD_TRIGGER_MASK | IRQD_LEVEL | IRQD_MOVE_PCNTXT);
        if (irq_settings_has_no_balance_set(desc))
                irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
        if (irq_settings_is_per_cpu(desc))
                irqd_set(&desc->irq_data, IRQD_PER_CPU);
        if (irq_settings_can_move_pcntxt(desc))
                irqd_set(&desc->irq_data, IRQD_MOVE_PCNTXT);
        if (irq_settings_is_level(desc))
                irqd_set(&desc->irq_data, IRQD_LEVEL);

        irqd_set(&desc->irq_data, irq_settings_get_trigger_mask(desc));

        irq_put_desc_unlock(desc, flags);
}
EXPORT_SYMBOL_GPL(irq_modify_status);


//kernel/irq/internals.h
static inline bool irqd_has_set(struct irq_data *d, unsigned int mask)
{
        return __irqd_to_state(d) & mask;
}


//include/linux/irq.h
#define __irqd_to_state(d)              ((d)->common->state_use_accessors)

23. 总结

每个中断控制器对应一个struct irq_domain结构体变量,在linux系统中这些struct irq_domain会被链接到irq_domain_list链表中,struct irq_domain中变量hwirq_max中记录该中断控制器可用的最大硬件中断号,revmap_size记录了struct irq_domain中linear_revmap[]数组的大小,在hwirq小于revmap_size时,即linear_revmap[]未填充满时, linear_revmap[]使用hwirq作为索引来保存对应的虚拟中断号virq。当hwirq大于revmap_size时,即linear_revmap[]填充满时,使用revmap_tree基数树保存hwirq对应的irq_data。

每一个硬件中断号hwirq对应一个struct irq_desc结构体变量,struct irq_desc结构体变量中包含一个struct irq_data结构体变量,struct irq_data结构体中会记录对应的硬件中断号hwirq,虚拟中断号virq,同时记录该中断对应的中断控制器的struct irq_domain指针。所有的已使用的虚拟中断号virq都被记录在allocated_irqs的全局位图变量中,所有申请的struct irq_desc结构体变量会通过虚拟中断号virq保存在irq_desc_tree基数树中。struct irq_desc结构体还记录中断处理struct iqraction结构体指针。同时在struct irq_data包含具体的struct irq_chip结构体,该结构体定义具体中断控制器的操作接口。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值