新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM Linux中斷機制之中斷的初始化

        ARM Linux中斷機制之中斷的初始化

        作者: 時間:2016-11-10 來源:網絡 收藏
        一,認識幾個重要結構體:

        1.中斷描述符

        本文引用地址:http://www.104case.com/article/201611/317087.htm

        對于每一條中斷線都由一個irq_desc結構來描述。

        //在include/linux/irq.h中

        struct irq_desc {
        unsigned intirq;//中斷號
        struct timer_rand_state *timer_rand_state;
        unsigned int *kstat_irqs;
        #ifdef CONFIG_INTR_REMAP
        struct irq_2_iommu *irq_2_iommu;
        #endif

        /*

        在kernel/irq/chip.c中實現了5個函數:handle_simple_irq(),handle_level_irq(),
        handle_edge_irq(),handle_fasteoi_irq()以及handle_percpu_irq()。 handle_irq指針可
        以指向這5個函數中的一個, 選擇一種中斷事件處理策略, 這是通過函數set_irq_handler()
        完成的

        */
        irq_flow_handler_thandle_irq;//上層中斷處理函數,
        struct irq_chip*chip;//底層硬件操作
        struct msi_desc*msi_desc;
        void*handler_data;//附加參數,用于handle_irq
        void*chip_data;//平臺相關附加參數,用于chip
        struct irqaction*action;/* IRQ action list*///中斷服務例程鏈表
        unsigned intstatus;/* IRQ status *///中斷當前的狀態

        //中斷關閉打開層數調用一次disable_irq( ) ,depth加1;調用一次enable_irq( )該值減1,

        //如果depth等于0就開啟這條中斷線,如果depth大于0就關閉中斷線。

        unsigned intdepth;
        unsigned intwake_depth;////* 喚醒次數 */
        unsigned intirq_count;/* 發生的中斷次數 */
        unsigned longlast_unhandled;/* Aging timer for unhandled count */
        unsigned intirqs_unhandled;
        spinlock_tlock;
        #ifdef CONFIG_SMP
        cpumask_var_taffinity;
        unsigned intcpu;
        #ifdef CONFIG_GENERIC_PENDING_IRQ
        cpumask_var_tpending_mask;
        #endif
        #endif
        atomic_tthreads_active;
        wait_queue_head_t wait_for_threads;
        #ifdef CONFIG_PROC_FS
        struct proc_dir_entry*dir;///proc/irq/ 入口
        #endif
        const char*name;///proc/interrupts 中顯示的中斷名稱
        } ____cacheline_internodealigned_in_smp;


        /*在kernel/irq/handle.c中有個全局irq_desc數組,描述了系統中所有的中斷線:

        struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
        [0 ... NR_IRQS-1] = {
        .status = IRQ_DISABLED,
        .chip = &no_irq_chip,
        .handle_irq = handle_bad_irq,
        .depth = 1,
        .lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
        }
        };

        NR_IRQS為最大中斷數對于s3c2410芯片,在文件arch/arm/mach-s3c2410/include/mach/irqs.h中定義如下:

        #ifdef CONFIG_CPU_S3C2443
        #define NR_IRQS (IRQ_S3C2443_AC97+1)
        #else
        #define NR_IRQS (IRQ_S3C2440_AC97+1)//每一個中斷源都對應一個irq_desc結構體。
        #endif

        */

        2. 中斷硬件操作函數集

        //在include/linux/irq.h中定義

        //該結構體中各函數在文件linux/arch/arm/plat-s3c24xx/irq.c中實現

        struct irq_chip {
        const char*name;//用于 /proc/interrupts
        unsigned int(*startup)(unsigned int irq);//默認為 enable 如果為NULL
        void(*shutdown)(unsigned int irq);//默認為 disable 如果為NULL
        void(*enable)(unsigned int irq);//允許中斷,默認為 unmask 如果為NULL
        void(*disable)(unsigned int irq);//禁止中斷,默認為 mask如果為 NULL

        void(*ack)(unsigned int irq);//響應一個中斷,清除中斷標志
        void(*mask)(unsigned int irq);//mask 一個中斷源,通常是關閉中斷
        void(*mask_ack)(unsigned int irq);//響應并 mask 中斷源
        void(*unmask)(unsigned int irq);//unmask 中斷源
        void(*eoi)(unsigned int irq);

        void(*end)(unsigned int irq);
        void(*set_affinity)(unsigned int irq,
        const struct cpumask *dest);
        int(*retrigger)(unsigned int irq);
        int(*set_type)(unsigned int irq, unsigned int flow_type);//設置中斷觸發方式 IRQ_TYPE_LEVEL
        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;
        };

        3.中斷處理例程描述符

        //在include/linux/interrupt.h中

        struct irqaction {
        irq_handler_t handler;/* 具體的中斷處理程序 */
        unsigned long flags;//用一組標志描述中斷線與 I/O 設備之間的關系。
        cpumask_t mask;
        const char *name;/* 名稱,會顯示在/proc/interreupts中 */
        void *dev_id;/* 設備ID,用于區分共享一條中斷線的多個處理程序 ,以便從共享中斷線的諸多中斷處理程序中刪除指定的那一個*/
        struct irqaction *next;/* 指向下一個irq_action結構 */
        int irq;/* 中斷通道號 */
        struct proc_dir_entry *dir; /* procfs目錄 */
        irq_handler_t thread_fn;
        struct task_struct *thread;
        unsigned long thread_flags;
        };

        這三個結構體間的關系表示如下

        二,中斷初始化過程
        中斷機制的初始化通過 兩個函數完成:early_trap_init()和init_IRQ(),在此我們先討論函數init_IRQ()。
        //函數init_IRQ在文件linux/arch/arm/kernel/irq.c中實現。
        void __init init_IRQ(void)
        {
        int irq;
        /* 設置 irq_desc 數組的 status 為 IRQ_NOREQUEST | IRQ_NOPROBE(沒有請求,沒有檢測) */
        for (irq = 0; irq < NR_IRQS; irq++)
        irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
        #ifdef CONFIG_SMP
        cpumask_setall(bad_irq_desc.affinity);
        bad_irq_desc.cpu = smp_processor_id();
        #endif
        /*
        init_arch_irq在文件linux/arch/arm/kernel/irq.c中定義如下
        void (*init_arch_irq)(void) __initdata = NULL;
        該函數指針在 setup_arch()中被賦值,
        init_arch_irq = mdesc->init_irq;
        指向 machine_desc 中定義的 init_irq 函數。
        在平臺smdk2440中,該函數在文件linux/arch/arm/plat-s3c24xx/irq.c中實現。
        */
        init_arch_irq();
        }
        //函數s3c24xx_init_irq在文件linux/arch/arm/plat-s3c24xx/irq.c中實現
        void __init s3c24xx_init_irq(void)
        {
        unsigned long pend;
        unsigned long last;
        int irqno;
        int i;
        irqdbf("s3c2410_init_irq: clearing interrupt status flagsn");
        /* first, clear all interrupts pending... */
        last = 0;
        for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C24XX_EINTPEND);
        if (pend == 0 || pend == last)
        break;
        __raw_writel(pend, S3C24XX_EINTPEND);//清除外部中斷寄存器EINTPEND中的請求標志,
        printk("irq: clearing pending ext status %08xn", (int)pend);
        last = pend;
        }
        last = 0;

        。。。。。。
        //設置各中斷的底層硬件操作函數集desc->chip,中斷上層處理函數desc->handle_irq
        for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
        /* set all the s3c2410 internal irqs */
        switch (irqno) {
        /* deal with the special IRQs (cascaded) */
        case IRQ_EINT4t7:
        case IRQ_EINT8t23:
        case IRQ_UART0:
        case IRQ_UART1:
        case IRQ_UART2:
        case IRQ_ADCPARENT:
        set_irq_chip(irqno, &s3c_irq_level_chip);
        set_irq_handler(irqno, handle_level_irq);
        break;
        case IRQ_RESERVED6:
        case IRQ_RESERVED24:
        /* no IRQ here */
        break;
        default:
        //irqdbf("registering irq %d (s3c irq)n", irqno);
        set_irq_chip(irqno, &s3c_irq_chip);
        set_irq_handler(irqno, handle_edge_irq);
        set_irq_flags(irqno, IRQF_VALID);
        }
        }
        //以下幾個中斷都有多個中斷源,每一個中斷源也都有各自的中斷號,它們的多個中斷源中任意一個產生中斷
        //該中斷都會被觸發,而不是直接出發子中斷。這幾個中斷并不處理中斷函數,它們的中作是計算子中斷的中斷號,
        //并根據子中斷的中斷號在數組irq_desc[NR_IRQS]中去找出該中斷號對應的irq_desc結構,并調用該結構中的中斷處理函數。
        set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
        set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
        set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
        set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
        set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
        set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
        。。。。。。
        for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
        irqdbf("registering irq %d (extended s3c irq)n", irqno);
        set_irq_chip(irqno, &s3c_irqext_chip);//設置子中斷的硬件操作函數集
        set_irq_handler(irqno, handle_edge_irq);//設置子中斷的上層處理函數
        set_irq_flags(irqno, IRQF_VALID);
        }
        。。。。。。

        }

        比如此時外部中斷10產生了中斷,中斷號為IRQ_EINT8t23的中斷被觸發,執行函數s3c_irq_demux_extint8()。

        static void
        s3c_irq_demux_extint8(unsigned int irq,
        struct irq_desc *desc)
        {
        unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);
        unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);

        eintpnd &= ~eintmsk;
        eintpnd &= ~0xff;/* ignore lower irqs */

        /* we may as well handle all the pending IRQs here */

        while (eintpnd) {
        irq = __ffs(eintpnd);//計算該中斷在外部中斷寄存器EINTPEND中的偏移量
        eintpnd &= ~(1<

        irq += (IRQ_EINT4 - 4);//根據這個偏移量重新計算中斷號
        generic_handle_irq(irq);//根據重新計算的中斷號獲取對應的結構體irq_desc,并調用它的上層中斷處理函數。
        }

        }

        static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
        {
        #ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
        desc->handle_irq(irq, desc);//直接調用上層中斷處理函數
        #else
        if (likely(desc->handle_irq))
        desc->handle_irq(irq, desc);
        else
        __do_IRQ(irq);//通用中斷處理函數,該函數最終調用desc->handle_irq(irq, desc);
        #endif
        }

        上層中斷處理函數

        上層中斷處理函數有5個分別為:handle_simple_irq(),handle_level_irq(),
        handle_edge_irq(),handle_fasteoi_irq()以及handle_percpu_irq()。

        這幾個函數在文件kernel/irq/chip.c中實現。常用的有兩個handle_level_irq(),和handle_edge_irq()。

        這5個上層中斷處理函數都是通過調用函數handle_IRQ_event()來做進一步處理。

        irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
        {
        irqreturn_t ret, retval = IRQ_NONE;
        unsigned int status = 0;

        if (!(action->flags & IRQF_DISABLED))
        local_irq_enable_in_hardirq();

        do {


        。。。。。。


        ret = action->handler(irq, action->dev_id);//執行中斷處理函數。


        。。。。。。

        retval |= ret;
        action = action->next;
        } while (action);//調用該中斷線上的所有例程

        if (status & IRQF_SAMPLE_RANDOM)
        add_interrupt_randomness(irq);
        local_irq_disable();

        return retval;
        }

        void
        handle_level_irq(unsigned int irq, struct irq_desc *desc)
        {
        struct irqaction *action;
        irqreturn_t action_ret;

        。。。。。。


        desc = irq_remap_to_desc(irq, desc);

        。。。。。。


        action = desc->action;

        action_ret = handle_IRQ_event(irq, action);


        。。。。。。


        }

        void
        handle_edge_irq(unsigned int irq, struct irq_desc *desc)
        {
        spin_lock(&desc->lock);

        。。。。。。
        desc = irq_remap_to_desc(irq, desc);
        。。。。。。
        desc->status |= IRQ_INPROGRESS;

        do {
        struct irqaction *action = desc->action;
        。。。。。。

        desc->status &= ~IRQ_PENDING;
        spin_unlock(&desc->lock);
        action_ret = handle_IRQ_event(irq, action);
        if (!noirqdebug)
        note_interrupt(irq, desc, action_ret);
        spin_lock(&desc->lock);

        //該函數與函數handle_level_irq不太一樣的是,該函數多了一個循環。即如果在本次中斷

        //的處理過程中該中斷線上又有中斷產生,則再次執行該中斷線上的處理例程

        /*

        以下是5個常用的中斷線狀態。

        #define IRQ_INPROGRESS 1 /* 正在執行這個 IRQ 的一個處理程序 */
        #define IRQ_DISABLED 2 /* 由設備驅動程序已經禁用了這條 IRQ 中斷線 */

        #define IRQ_PENDING 4 /* 一個 IRQ 已經出現在中斷線上,且被應答,但還沒有
        為它提供服務 */
        #define IRQ_REPLAY 8 /* 當 Linux 重新發送一個已被刪除的 IRQ 時 */
        #define IRQ_WAITING 32 /* 當對硬件設備進行探測時,設置這個狀態以標記正在被
        測試的 irq */

        */

        } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);

        desc->status &= ~IRQ_INPROGRESS;
        out_unlock:
        spin_unlock(&desc->lock);
        }



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 马龙县| 大兴区| 兖州市| 临西县| 正安县| 新郑市| 重庆市| 和政县| 新龙县| 遂昌县| 高雄县| 福清市| 江安县| 鹤庆县| 灌云县| 达拉特旗| 察隅县| 民和| 青河县| 沙田区| 台湾省| 思茅市| 南岸区| 清徐县| 汉沽区| 长垣县| 大兴区| 祁阳县| 峨边| 广安市| 于田县| 江安县| 裕民县| 金门县| 望城县| 炉霍县| 布拖县| 宾川县| 盐源县| 浪卡子县| 长汀县|