关于mx27中断

转载

关于MX27中断系统:
这个文档没有涉及到中断向量初始化的内容,而是这之后的纯软件上的架构分析,至于中断向量初始化及系统从用户模式或系统模式下进入中断的一系列流程将会在另外一篇文档中讲述,如果有可能的话我会将这两部分合成一个文档。
初始化流程:
首先,在arch/arm/mach-mx27/mx27mdk27v0.c中有关于machine的描述:
/*
 * The following uses standard kernel macros define in arch.h in order to
 * initialize __mach_desc_MX27ADS data structure.
 */
/* *INDENT-OFF* */
MACHINE_START(MX27_MDK27V0, "Morninghan i.MX27 MDK27V0")
        /* maintainer: Freescale Semiconductor, Inc. */
#ifdef CONFIG_SERIAL_8250_CONSOLE
        .phys_io = CS5_BASE_ADDR,
        .io_pg_offst = ((CS5_BASE_ADDR_VIRT) >> 18) & 0xfffc,
#else
        .phys_io = AIPI_BASE_ADDR,
        .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
#endif
        .boot_params = PHYS_OFFSET + 0x100,
        .fixup = fixup_mxc_board,
        .map_io = mxc_map_io,
        .init_irq = mxc_init_irq,
        .init_machine = mxc_board_init,
        .timer = &mxc_timer,
MACHINE_END
其中有一个mxc_init_irq函数,地址赋给init_irq指针,这个函数就是在异常向量表建立好之后初始化中断处理的时候调用的。我们来看下init_irq指针的去向:
在init/main.c的大名鼎鼎的start_kernel函数中有调用setup_arch(&command_line)(函数原型在arch/arm/kernel/setup.c中),然后setup_arch中有这么一行init_arch_irq = mdesc->init_irq;而这个init_arch_irq为一个全局的指向函数的指针变量,然后在start_kernel函数后面有调用init_IRQ,这是在trap_init(),rcu_init()之后,没错,这个trap_init就是进行传说中的代码(异常向量)大挪移的,回到init_IRQ函数中,它最后有一行init_arch_irq,看到没,终于调用了吧。总结一下:
start_kernel-->setup_arch(指针赋值)返回
-->init_IRQ-->init_arch_irq(即是mxc_init_irq)
函数原型(在arch/arm/plat-mxc/irq.c中):
/*!
 * This function initializes the AVIC hardware and disables all the
 * interrupts. It registers the interrupt enable and disable functions
 * to the kernel for each interrupt source.
 */
void __init mxc_init_irq(void)
{
    int i;
    u32 reg;
    /* put the AVIC into the reset value with
     * all interrupts disabled
     */
    __raw_writel(0, AVIC_INTCNTL);
    __raw_writel(0x1f, AVIC_NIMASK);
    /* disable all interrupts */
    __raw_writel(0, AVIC_INTENABLEH);
    __raw_writel(0, AVIC_INTENABLEL);
    /* all IRQ no FIQ */
    __raw_writel(0, AVIC_INTTYPEH);
    __raw_writel(0, AVIC_INTTYPEL);
    for (i = 0; i < MXC_MAX_INT_LINES; i++) {
#ifdef EDIO_BASE_ADDR
        if (irq_to_edio(i) != -1) {
            mxc_irq_set_edio(i, 0, 0, 0);
            set_irq_chip(i, &mxc_edio_chip);
        } else
#endif
        {
            set_irq_chip(i, &mxc_avic_chip);
        }
        set_irq_handler(i, do_level_IRQ);
        set_irq_flags(i, IRQF_VALID);
    }
    /* Set WDOG2's interrupt the highest priority level (bit 28-31) */
    reg = __raw_readl(AVIC_NIPRIORITY6);
    reg |= (0xF << 28);
    __raw_writel(reg, AVIC_NIPRIORITY6);
    printk(KERN_INFO "MXC IRQ initialized\n");
}
其实在解释这个函数之前有必要先贴一些关于IRQ的数据结构出来:
在include/linux/irq.h中有
/**
 * struct irq_chip - hardware interrupt chip descriptor
 *
 * @name:        name for /proc/interrupts
 * @startup:        start up the interrupt (defaults to ->enable if NULL)
 * @shutdown:        shut down the interrupt (defaults to ->disable if NULL)
 * @enable:        enable the interrupt (defaults to chip->unmask if NULL)
 * @disable:        disable the interrupt (defaults to chip->mask if NULL)
 * @ack:        start of a new interrupt
 * @mask:        mask an interrupt source
 * @mask_ack:        ack and mask an interrupt source
 * @unmask:        unmask an interrupt source
 * @eoi:        end of interrupt - chip level
 * @end:        end of interrupt - flow level
 * @set_affinity:    set the CPU affinity on SMP machines
 * @retrigger:        resend an IRQ to the CPU
 * @set_type:        set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
 * @set_wake:        enable/disable power-management wake-on of an IRQ
 *
 * @release:        release function solely used by UML
 * @typename:        obsoleted by name, kept as migration helper
 */
struct irq_chip {
    const char    *name;
    unsigned int    (*startup)(unsigned int irq);
    void        (*shutdown)(unsigned int irq);
    void        (*enable)(unsigned int irq);
    void        (*disable)(unsigned int irq);
    void        (*ack)(unsigned int irq);
    void        (*mask)(unsigned int irq);
    void        (*mask_ack)(unsigned int irq);
    void        (*unmask)(unsigned int irq);
    void        (*eoi)(unsigned int irq);
    void        (*end)(unsigned int irq);
    void        (*set_affinity)(unsigned int irq, cpumask_t dest);
    int        (*retrigger)(unsigned int irq);
    int        (*set_type)(unsigned int irq, unsigned int flow_type);
    int        (*set_wake)(unsigned int irq, unsigned int on);
    /* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
    void        (*release)(unsigned int irq, void *dev_id);
#endif
    /*
     * For compatibility, ->typename is copied into ->name.
     * Will disappear.
     */
    const char    *typename;
};
这个chip的描述我的理解是从PC时代延用下来的,当时是抽象出来描述8255这种扩展中断芯片的,而到了我们现在用的各种ARM核的SOC中的话就演变成对一个中断controller的描述,比如在MX27系统中就有
    static struct irqchip mxc_avic_chip = {
        .ack = mxc_mask_irq,
        .mask = mxc_mask_irq,
        .unmask = mxc_unmask_irq,
    };
    这是与ARM端对接的一个CHIP描述,当然还有
    static struct irq_chip gpio_irq_chip = {
        .ack = gpio_ack_irq,
        .mask = gpio_mask_irq,
        .unmask = gpio_unmask_irq,
        .set_type = gpio_set_irq_type,
    };
    这是一个与INT_GPIO中断对接的一个扩展中断CHIP的描述,这些在后面都会有讲述。
从上面的两个例子也可以看到,这个CHIP结构描述虽然抽象出有很多可用的方法,但我们比较常用的也就只有ack,mask,mask_ack和set_type等等,看到这里,也许你会有这样的疑问,为什么mxc_avic_chip中的ack方法用mask来实现呢?这个问题下面会有讲述,还有,为什么mxc_avic_chip没有set_type方法?这个嘛,因为这个CHIP所管的中断只支持电平触发,就这么简单。
/**
 * struct irq_desc - interrupt descriptor
 *
 * @handle_irq:        highlevel irq-events handler [if NULL, __do_IRQ()]
 * @chip:        low level interrupt hardware access
 * @handler_data:    per-IRQ data for the irq_chip methods
 * @chip_data:        platform-specific per-chip private data for the chip
 *            methods, to allow shared chip implementations
 * @action:        the irq action chain
 * @status:        status information
 * @depth:        disable-depth, for nested irq_disable() calls
 * @wake_depth:        enable depth, for multiple set_irq_wake() callers
 * @irq_count:        stats field to detect stalled irqs
 * @irqs_unhandled:    stats field for spurious unhandled interrupts
 * @lock:        locking for SMP
 * @affinity:        IRQ affinity on SMP
 * @cpu:        cpu index useful for balancing
 * @pending_mask:    pending rebalanced interrupts
 * @dir:        /proc/irq/ procfs entry
 * @affinity_entry:    /proc/irq/smp_affinity procfs entry on SMP
 * @name:        flow handler name for /proc/interrupts output
 *
 * Pad this out to 32 bytes for cache and indexing reasons.
 */
struct irq_desc {
    irq_flow_handler_t    handle_irq;
    struct irq_chip            *chip;
    void                                *handler_data;
    void                                *chip_data;
    struct irqaction        *action;    /* IRQ action list */
    unsigned int                status;        /* IRQ status */
    unsigned int                depth;        /* nested irq disables */
    unsigned int                wake_depth;    /* nested wake enables */
    unsigned int                irq_count;    /* For detecting broken IRQs */
    unsigned int                irqs_unhandled;
    spinlock_t                    lock;
#ifdef CONFIG_SMP
    cpumask_t                        affinity;
    unsigned int                cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
    cpumask_t                        pending_mask;
#endif
#ifdef CONFIG_PROC_FS
    struct proc_dir_entry    *dir;
#endif
    const char                    *name;
} ____cacheline_aligned;
每个硬件中断都对应着有这个irq_desc的描述,在/kernel/irq/handle.c中有定义:
/*
 * Linux has a controller-independent interrupt architecture.
 * Every controller has a 'controller-template', that is used
 * by the main code to do the right thing. Each driver-visible
 * interrupt source is transparently wired to the appropriate
 * controller. Thus drivers need not be aware of the
 * interrupt-controller.
 *
 * The code is designed to be easily extended with new/different
 * interrupt controllers, without having to do assembly magic or
 * having to touch the generic code.
 *
 * Controller mappings for all interrupt sources:
 */
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned = {
    [0 ... NR_IRQS-1] = {
        .status = IRQ_DISABLED,
        .chip = &no_irq_chip,
        .handle_irq = handle_bad_irq,
        .depth = 1,
        .lock = SPIN_LOCK_UNLOCKED,
#ifdef CONFIG_SMP
        .affinity = CPU_MASK_ALL
#endif
    }
};
这个NR_IRQS决定了系统中最多可用的中断数,看一下这个在MX27中的定义:
#define NR_IRQS (MXC_MAX_INTS) /*asm/arch-mxc/irqs.h*/
#define MXC_MAX_INTS (MXC_MAX_INT_LINES + \
                                MXC_MAX_GPIO_LINES + \
                                MXC_MAX_EXP_IO_LINES + \
                                MXC_MAX_VIRTUAL_INTS)
  /*=64 + 32*6 + 16(0?) +16 *//*/asm-arm/arch-mxc/hardware.h*/
在init_IRQ中,有对这个irq_desc[]数组初始化的操作:
void __init init_IRQ(void)
{
    int irq;
    for (irq = 0; irq < NR_IRQS; irq++)
        irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_DELAYED_DISABLE | IRQ_NOPROBE;
#ifdef CONFIG_SMP
    bad_irq_desc.affinity = CPU_MASK_ALL;
    bad_irq_desc.cpu = smp_processor_id();
#endif
    init_arch_irq();
}
将所有的status置为IRQ_NOREQUEST | IRQ_DELAYED_DISABLE | IRQ_NOPROBE然后调用上面文档中的init_arch_irq:
void __init mxc_init_irq(void)
{
    int i;
    u32 reg;
    /* put the AVIC into the reset value with
     * all interrupts disabled
     */
    __raw_writel(0, AVIC_INTCNTL);
    __raw_writel(0x1f, AVIC_NIMASK);
    /* disable all interrupts */
    __raw_writel(0, AVIC_INTENABLEH);
    __raw_writel(0, AVIC_INTENABLEL);
    /* all IRQ no FIQ */
    __raw_writel(0, AVIC_INTTYPEH);
    __raw_writel(0, AVIC_INTTYPEL);
    for (i = 0; i < MXC_MAX_INT_LINES; i++) {
#ifdef EDIO_BASE_ADDR
        if (irq_to_edio(i) != -1) {
            mxc_irq_set_edio(i, 0, 0, 0);
            set_irq_chip(i, &mxc_edio_chip);
        } else
#endif
        {
            set_irq_chip(i, &mxc_avic_chip);
        }
        set_irq_handler(i, do_level_IRQ);
        set_irq_flags(i, IRQF_VALID);
    }
    /* Set WDOG2's interrupt the highest priority level (bit 28-31) */
    reg = __raw_readl(AVIC_NIPRIORITY6);
    reg |= (0xF << 28);
    __raw_writel(reg, AVIC_NIPRIORITY6);
    printk(KERN_INFO "MXC IRQ initialized\n");
}
在MX27中支持将64个系统中断配置成FIQ或IRQ模式,并且还支持优先级设置,当然这些都只是设置寄存器,具体中断发生时由硬件去实现判别,set_irq_chip就是将对应的中断irq_desc中的chip方法初始化,上面贴出的mxc_avic_chip中只有ack,mask,unmask这几个方法,而在set_irq_chip中会调用irq_chip_set_defaults初始化enable,disable,startup,shutdown等,这些无非都是用mask和unmask方法填充。
接下来有个set_irq_handler,并且设置的还是do_level_IRQ
/*
 * Set a highlevel flow handler for a given IRQ:
 */
static inline void
set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
    __set_irq_handler(irq, handle, 0, NULL);
}
函数调用__set_irq_handler,并且传入的第三个变量为0,表示不是chain方式,这个chain为1的话表明这个中断号所对应的中断接着一个中断扩展,这样的话当然这个中断不能用request_irq来申请了。
    void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
             const char *name)
    {
        struct irq_desc *desc;
        unsigned long flags;
    
        if (irq >= NR_IRQS) {
            printk(KERN_ERR
             "Trying to install type control for IRQ%d\n", irq);
            return;
        }
    
        desc = irq_desc + irq;
    
        if (!handle)
            handle = handle_bad_irq;
    
        if (desc->chip == &no_irq_chip) {
            printk(KERN_WARNING "Trying to install %sinterrupt handler "
             "for IRQ%d\n", is_chained ? "chained " : " ", irq);
            /*
             * Some ARM implementations install a handler for really dumb
             * interrupt hardware without setting an irq_chip. This worked
             * with the ARM no_irq_chip but the check in setup_irq would
             * prevent us to setup the interrupt at all. Switch it to
             * dummy_irq_chip for easy transition.
             */
            desc->chip = &dummy_irq_chip;
        }
    
        spin_lock_irqsave(&desc->lock, flags);
    
        /* Uninstall? */
        if (handle == handle_bad_irq) {
            if (desc->chip != &no_irq_chip) {
                desc->chip->mask(irq);
                desc->chip->ack(irq);
            }
            desc->status |= IRQ_DISABLED;
            desc->depth = 1;
        }
        desc->handle_irq = handle;
        desc->name = name;
    
        if (handle != handle_bad_irq && is_chained) {
            desc->status &= ~IRQ_DISABLED;
            /*表明这个中断线上扩展了中断,因此不能用request_irq来申请*/
            desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
            desc->depth = 0;
            desc->chip->unmask(irq);
        }
        spin_unlock_irqrestore(&desc->lock, flags);
    }
    返回
接下来调用set_irq_flags,并传了一个IRQF_VALID下去。
void set_irq_flags(unsigned int irq, unsigned int iflags)
{
    struct irqdesc *desc;
    unsigned long flags;
    if (irq >= NR_IRQS) {
        printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
        return;
    }
    desc = irq_desc + irq;
    spin_lock_irqsave(&desc->lock, flags);
    desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
    if (iflags & IRQF_VALID)/*驱动中可以用request_irq来申请该中断了*/
        desc->status &= ~IRQ_NOREQUEST;
    if (iflags & IRQF_PROBE)
        desc->status &= ~IRQ_NOPROBE;
    if (!(iflags & IRQF_NOAUTOEN))
        desc->status &= ~IRQ_NOAUTOEN;
    spin_unlock_irqrestore(&desc->lock, flags);
}
最后返回到mxc_init_irq中,看到最后有两行
/* Set WDOG2's interrupt the highest priority level (bit 28-31) */
    reg = __raw_readl(AVIC_NIPRIORITY6);
    reg |= (0xF << 28);
    __raw_writel(reg, AVIC_NIPRIORITY6);
这里不知道什么意思,按注释来说是设置watchdog的优先级为16,但写入的是AVIC_NIPRIORITY6这个寄存器,这个寄存器在手册中对应的应该是中断号为55的中断,也就是INT_USB2所对应的中断号,迷惑??
下面看一下GPIO中断是怎么初始化的:
static int __init _mxc_gpio_init(void)
{
    int i;
    struct gpio_port *port;
    initialized = 1;
    printk(KERN_INFO "MXC GPIO hardware\n");
    for (i = 0; i < GPIO_PORT_NUM; i++) {
        int j, gpio_count = GPIO_NUM_PIN;
        port = &gpio_port[i];
        port->base = mxc_gpio_ports[i].base;
        port->num = mxc_gpio_ports[i].num;
        port->irq = mxc_gpio_ports[i].irq;
        port->virtual_irq_start = mxc_gpio_ports[i].virtual_irq_start;
        port->reserved_map = 0;
        spin_lock_init(&port->lock);
        /* disable the interrupt and clear the status */
        __raw_writel(0, port->base + GPIO_IMR);
        __raw_writel(0xFFFFFFFF, port->base + GPIO_ISR);
        for (j = port->virtual_irq_start;
         j < port->virtual_irq_start + gpio_count; j++) {
            set_irq_chip(j, &gpio_irq_chip);
            set_irq_handler(j, do_edge_IRQ);
            set_irq_flags(j, IRQF_VALID);
        }
#ifndef MXC_MUX_GPIO_INTERRUPTS
        set_irq_chained_handler(port->irq, mxc_gpio_irq_handler);
        set_irq_data(port->irq, port);
#endif
    }
#ifdef MXC_MUX_GPIO_INTERRUPTS
    set_irq_chained_handler(port->irq, mxc_gpio_mux_irq_handler);
    set_irq_data(mxc_gpio_ports[0].irq, gpio_port);
#endif
    return 0;
}
这里MXC_MUX_GPIO_INTERRUPTS是define为1的,看到没,有一个for循环去初始化chip,handler和flags,最后循环出来后,你说这个port->irq是多少?当然是INT_GPIO了, 所以这里调用set_irq_chained_handler,将这个INT_GPIO中断号设置为扩展中断,后面set_irq_data是初始化desc->handler_data = data。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值