<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      am33xx linux中斷處理流程

      運(yùn)行環(huán)境

        kernel 5.10 

        CPU Ti am33xx

      linux中斷的3個(gè)結(jié)構(gòu)體

      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;
      	struct irqaction	*action;	/* IRQ action list */   // 里面存放了用戶注冊(cè)的中斷服務(wù)代碼
      	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		tot_count;
      	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;
      	const struct cpumask	*percpu_affinity;
      #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
      #ifdef CONFIG_GENERIC_IRQ_DEBUGFS
      	struct dentry		*debugfs_file;
      	const char		*dev_name;
      #endif
      #ifdef CONFIG_SPARSE_IRQ
      	struct rcu_head		rcu;
      	struct kobject		kobj;
      #endif
      	struct mutex		request_mutex;
      	int			parent_irq;
      	struct module		*owner;
      	const char		*name;
      } ____cacheline_internodealigned_in_smp;
      

        

      struct irqaction {
      	irq_handler_t		handler;  // 用戶注冊(cè)的中斷服務(wù)代碼, 中斷發(fā)生時(shí)就會(huì)運(yùn)行這個(gè)中斷處理函數(shù)
      	void			*dev_id;
      	void __percpu		*percpu_dev_id;
      	struct irqaction	*next;
      	irq_handler_t		thread_fn;
      	struct task_struct	*thread;
      	struct irqaction	*secondary;
      	unsigned int		irq;    //hw中斷號(hào),
      	unsigned int		flags;  //中斷標(biāo)志,注冊(cè)時(shí)設(shè)置,比如上升沿中斷,下降沿中斷等
      	unsigned long		thread_flags;
      	unsigned long		thread_mask;
      	const char		*name;  //中斷名稱,產(chǎn)生中斷的硬件的名字
      	struct proc_dir_entry	*dir;  // proc/irq/
      } ____cacheline_internodealigned_in_smp;
      

        

      struct irq_data {
      	u32			mask;
      	unsigned int		irq;
      	unsigned long		hwirq;
      	struct irq_common_data	*common;
      	struct irq_chip		*chip;    // 芯片端的中斷處理函數(shù)
      	struct irq_domain	*domain;    // 代表一個(gè)interrupt controller 設(shè)備樹(shù)中的INTC
      #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
      	struct irq_data		*parent_data;
      #endif
      	void			*chip_data;
      };
      

        整體框圖如下:

       

       

       

      linux硬件中斷處理流程

      1. 外設(shè)產(chǎn)生中斷

      2. CPU觸發(fā)中斷

      3. 跳轉(zhuǎn)到異常向量入口:

        保存現(xiàn)場(chǎng)

        執(zhí)行中斷服務(wù)

        恢復(fù)現(xiàn)場(chǎng)

       -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

      我們主要關(guān)注如何執(zhí)行中斷服務(wù),在arch\arm\kernel\entry-armv.S中,有如下幾條語(yǔ)句

      	.macro	irq_handler
      #ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
      	ldr	r1, =handle_arch_irq
      	mov	r0, sp
      	badr	lr, 9997f
      	ldr	pc, [r1]
      #else
      	arch_irq_handler_default
      #endif
      

      其中 handle_arch_irq 即為CPU處理中斷時(shí)會(huì)調(diào)用到的硬件中斷服務(wù)入口,handle_arch_irq在 kernel/irq/handle.c 中被賦值

      #ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
      #ifndef CONFIG_IRQCHIP_XILINX_INTC_MODULE_SUPPORT_EXPERIMENTAL
      int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
      #else
      int set_handle_irq(void (*handle_irq)(struct pt_regs *))
      #endif
      {
      	if (handle_arch_irq)
      		return -EBUSY;
      
      	handle_arch_irq = handle_irq;
      	return 0;
      }
      #endif
      

       

      set_handle_irq 被誰(shuí)調(diào)用?主要由CPU廠家的BSP工程師調(diào)用,比如TI的AM33xx系列平臺(tái),在 drivers/irqchip/irq-omap-intc.c 中的以下接口調(diào)用

      static int __init intc_of_init(struct device_node *node,
      			     struct device_node *parent)
      {
      	int ret;
      
      	omap_nr_pending = 3;
      	omap_nr_irqs = 96;
      
      	if (WARN_ON(!node))
      		return -ENODEV;
      
      	if (of_device_is_compatible(node, "ti,dm814-intc") ||
      	    of_device_is_compatible(node, "ti,dm816-intc") ||
      	    of_device_is_compatible(node, "ti,am33xx-intc")) {
      		omap_nr_irqs = 128;
      		omap_nr_pending = 4;
      	}
      
      	ret = omap_init_irq(-1, of_node_get(node));
      	if (ret < 0)
      		return ret;
      
      	set_handle_irq(omap_intc_handle_irq);   // 設(shè)置硬中斷服務(wù)函數(shù)入口
      
      	return 0;
      }
      

       

      omap_intc_handle_irq便是am33xx觸發(fā)中斷后,需要響應(yīng)的中斷服務(wù)入口,該入口做了哪些工作?

      static asmlinkage void __exception_irq_entry
      omap_intc_handle_irq(struct pt_regs *regs)
      {
      	extern unsigned long irq_err_count;
      	u32 irqnr;
      
      	irqnr = intc_readl(INTC_SIR);  // 獲取硬件中斷號(hào)
      
      	/*
      	 * A spurious IRQ can result if interrupt that triggered the
      	 * sorting is no longer active during the sorting (10 INTC
      	 * functional clock cycles after interrupt assertion). Or a
      	 * change in interrupt mask affected the result during sorting
      	 * time. There is no special handling required except ignoring
      	 * the SIR register value just read and retrying.
      	 * See section 6.2.5 of AM335x TRM Literature Number: SPRUH73K
      	 *
      	 * Many a times, a spurious interrupt situation has been fixed
      	 * by adding a flush for the posted write acking the IRQ in
      	 * the device driver. Typically, this is going be the device
      	 * driver whose interrupt was handled just before the spurious
      	 * IRQ occurred. Pay attention to those device drivers if you
      	 * run into hitting the spurious IRQ condition below.
      	 */
      	if (unlikely((irqnr & SPURIOUSIRQ_MASK) == SPURIOUSIRQ_MASK)) {
      		pr_err_once("%s: spurious irq!\n", __func__);
      		irq_err_count++;
      		omap_ack_irq(NULL);
      		return;
      	}
      
      	irqnr &= ACTIVEIRQ_MASK;
      	handle_domain_irq(domain, irqnr, regs); // 根據(jù)硬件中斷號(hào),在domain鏈表中查找映射好的IRQ number, 實(shí)現(xiàn)原理是怎樣的?
      }
      

      domain是如何實(shí)現(xiàn)HW 中斷號(hào)和 IRQ number映射的? 在解析設(shè)備樹(shù)的時(shí)候,會(huì)調(diào)用 irq_of_parse_and_map 接口創(chuàng)建映射;詳情參考 linux中斷號(hào)映射

      handle_domain_irq 調(diào)用了 __handle_domain_irq, 在__handle_domain_irq中,開(kāi)始調(diào)用硬中斷服務(wù)函數(shù),也就是中斷上半部

      int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
      			bool lookup, struct pt_regs *regs)
      {
      	struct pt_regs *old_regs = set_irq_regs(regs);
      	unsigned int irq = hwirq;
      	int ret = 0;
      
      	irq_enter();
      
      #ifdef CONFIG_IRQ_DOMAIN
      	if (lookup)
      		irq = irq_find_mapping(domain, hwirq);  // 根據(jù)硬中斷號(hào),在映射表中找到對(duì)應(yīng)的 IRQ number
      #endif
      
      	/*
      	 * Some hardware gives randomly wrong interrupts.  Rather
      	 * than crashing, do something sensible.
      	 */
      	if (unlikely(!irq || irq >= nr_irqs)) {
      		ack_bad_irq(irq);
      		ret = -EINVAL;
      	} else {
      		generic_handle_irq(irq);  // 根據(jù)IRQ number,查找中斷服務(wù),并執(zhí)行
      	}
      
      	irq_exit();  // 退出硬件中斷服務(wù),然后 invoke_softirq(),再 __do_softirq(),喚醒softirq隊(duì)列,進(jìn)入下半部開(kāi)始執(zhí)行
      	set_irq_regs(old_regs);
      	return ret;
      }
      

        他會(huì)調(diào)用  generic_handle_irq(irq) , 以下為 generic_handle_irq 代碼

      int generic_handle_irq(unsigned int irq)
      {
      	struct irq_desc *desc = irq_to_desc(irq); // 根據(jù)IRQ number 找到 中斷描述符
      	struct irq_data *data;
      
      	if (!desc)
      		return -EINVAL;
      
      	data = irq_desc_get_irq_data(desc);
      	if (WARN_ON_ONCE(!in_irq() && handle_enforce_irqctx(data)))
      		return -EPERM;
      
      	generic_handle_irq_desc(desc); // 根據(jù)中斷描述符,找到對(duì)應(yīng)的 handler
      	return 0;
      }
      

      generic_handle_irq_desc 會(huì)調(diào)用 desc結(jié)構(gòu)體中的 hande_irq 成員: desc->handle_irq(desc); 他指向哪 ?

      desc->handle_irq(desc) 最后會(huì)調(diào)用到 irqaction 結(jié)構(gòu)體中的 handler 成員,也就是用戶通過(guò) request_irq (request_irq詳解) 接口注冊(cè)的中斷服務(wù)程序,附上網(wǎng)上找的一份簡(jiǎn)單示意圖(kernel2.6



       

       

       

       

       desc->handle_irq(desc) 是如何關(guān)聯(lián)到 irqaction 結(jié)構(gòu)體中的 handler 成員?

       先從設(shè)備端看:

      拿AM33xx GPIO舉例:

      在 gpio-omap.c 中的 omap_gpio_probe 里面,會(huì)對(duì) irq_chip 結(jié)構(gòu)體的 irq_set_type 賦值 omap_gpio_irq_type,然后將 am33xx芯片端的gpio 中斷處理相關(guān)的函數(shù)注冊(cè)到linux kernel,kernel調(diào)用 desc->handle_irq(desc) 時(shí),即可調(diào)用到 am33xx xinp端的 gpio 中斷處理函數(shù)(具體細(xì)節(jié)接下來(lái)分析);

      omap_gpio_irq_type 函數(shù)如下:

      static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
      {
      	struct gpio_bank *bank = omap_irq_data_get_bank(d);
      	int retval;
      	unsigned long flags;
      	unsigned offset = d->hwirq;
      
      	if (type & ~IRQ_TYPE_SENSE_MASK)
      		return -EINVAL;
      
      	if (!bank->regs->leveldetect0 &&
      		(type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
      		return -EINVAL;
      
      	raw_spin_lock_irqsave(&bank->lock, flags);
      	retval = omap_set_gpio_triggering(bank, offset, type);
      	if (retval) {
      		raw_spin_unlock_irqrestore(&bank->lock, flags);
      		goto error;
      	}
      	omap_gpio_init_irq(bank, offset);
      	if (!omap_gpio_is_input(bank, offset)) {
      		raw_spin_unlock_irqrestore(&bank->lock, flags);
      		retval = -EINVAL;
      		goto error;
      	}
      	raw_spin_unlock_irqrestore(&bank->lock, flags);
      
      	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
      		irq_set_handler_locked(d, handle_level_irq);    // 電平觸發(fā)中斷
      	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
      		/*
      		 * Edge IRQs are already cleared/acked in irq_handler and
      		 * not need to be masked, as result handle_edge_irq()
      		 * logic is excessed here and may cause lose of interrupts.
      		 * So just use handle_simple_irq.
      		 */
      		irq_set_handler_locked(d, handle_simple_irq);   // 邊沿觸發(fā)中斷
      
      	return 0;
      
      error:
      	return retval;
      }    
      

        該函數(shù)主要是設(shè)置了 gpio 電平觸發(fā), 邊沿觸發(fā)的中斷處理服務(wù),先拿邊沿觸發(fā)分析,看看 handle_simple_irq 函數(shù)是如何一步步調(diào)用到 irqaction 結(jié)構(gòu)體中的 handler 成員的(也就是用戶通過(guò)request_irq 申請(qǐng)的中斷服務(wù)):

      void handle_simple_irq(struct irq_desc *desc)
      {
      	raw_spin_lock(&desc->lock);
      
      	if (!irq_may_run(desc))
      		goto out_unlock;
      
      	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
      
      	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
      		desc->istate |= IRQS_PENDING;
      		goto out_unlock;
      	}
      
      	kstat_incr_irqs_this_cpu(desc);  // 設(shè)置處理標(biāo)記,避免多次重復(fù)進(jìn)入中斷處理
      	handle_irq_event(desc);    // 中斷服務(wù)相關(guān)
      
      out_unlock:
      	raw_spin_unlock(&desc->lock);
      }
      

        再來(lái)看看 handle_irq_event(desc) 做了啥 irqreturn_t handle_irq_event(struct irq_desc *desc)

      {
      	irqreturn_t ret;
      
      	desc->istate &= ~IRQS_PENDING;
      	irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
      	raw_spin_unlock(&desc->lock);
      
      	ret = handle_irq_event_percpu(desc);  // 進(jìn)入中斷服務(wù)
      
      	raw_spin_lock(&desc->lock);
      	irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
      	return ret;
      }
      
      
      irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
      {
      	irqreturn_t retval;
      	unsigned int flags = 0;
      
      	retval = __handle_irq_event_percpu(desc, &flags);   // 進(jìn)入中斷服務(wù)
      
      	add_interrupt_randomness(desc->irq_data.irq);
      
      	if (!noirqdebug)
      		note_interrupt(desc, retval);
      	return retval;
      }

        接下來(lái)就真正調(diào)用到了 irqaction 結(jié)構(gòu)體中的 handler 成員

      irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
      {
      	irqreturn_t retval = IRQ_NONE;
      	unsigned int irq = desc->irq_data.irq;
      	struct irqaction *action;
      
      	record_irq_time(desc);
      
      	for_each_action_of_desc(desc, action) {   // 輪詢?cè)撝袛嗝枋龇锩娴乃?action
      		irqreturn_t res;
      
      		/*
      		 * If this IRQ would be threaded under force_irqthreads, mark it so.
      		 */
      		if (irq_settings_can_thread(desc) &&
      		    !(action->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT)))
      			lockdep_hardirq_threaded();
      
      		trace_irq_handler_entry(irq, action);
      		res = action->handler(irq, action->dev_id);  // 調(diào)用用戶注冊(cè)的中斷服務(wù)
      		trace_irq_handler_exit(irq, action, res);
      
      		if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pS enabled interrupts\n",
      			      irq, action->handler))
      			local_irq_disable();
      
      		switch (res) {
      		case IRQ_WAKE_THREAD:
      			/*
      			 * Catch drivers which return WAKE_THREAD but
      			 * did not set up a thread function
      			 */
      			if (unlikely(!action->thread_fn)) {
      				warn_no_thread(irq, action);
      				break;
      			}
      
      			__irq_wake_thread(desc, action);
      
      			fallthrough;	/* to add to randomness */
      		case IRQ_HANDLED:
      			*flags |= action->flags;
      			break;
      
      		default:
      			break;
      		}
      
      		retval |= res;
      	}
      
      	return retval;
      }
      

        omap_gpio_probe 是如何將 irq_chip 關(guān)聯(lián)到 irq_desc 結(jié)構(gòu)體的, 也就是說(shuō)  kernel調(diào)用 desc->handle_irq(desc) 時(shí),怎么就能調(diào)用到 handle_simple_irq?后續(xù)繼續(xù)分析!

        

      posted @ 2024-02-20 15:33  迷途小菜鳥(niǎo)  閱讀(79)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 粉嫩少妇内射浓精videos| 亚洲人成网站18禁止| 中文字幕日韩一区二区不卡| 国产成人综合网亚洲第一| 久久天天躁狠狠躁夜夜婷 | 本溪市| 99久久国产福利自产拍| 久久亚洲国产成人精品性色| 亚洲AV日韩AV综合在线观看| 成全高清在线播放电视剧| 精品亚洲无人区一区二区| 干老熟女干老穴干老女人| 2022最新国产在线不卡a| 久久综合九色综合久桃花| 国产精品久久香蕉免费播放| 2021国产精品视频网站| 国产中文三级全黄| 亚洲色欲色欱WWW在线| 思南县| 亚洲精品无amm毛片| 国产99久一区二区三区a片| 亚洲一区二区三区自拍高清 | 亚洲国产成人综合自在线| 国产做无码视频在线观看浪潮| 成人看的污污超级黄网站免费| av午夜福利一片免费看久久| 中文字幕人妻丝袜美腿乱 | 视频二区中文字幕在线| 欧美激情 亚洲 在线| 国产性生大片免费观看性| 丰满少妇高潮无套内谢| 中文字幕国产精品第一页| 日韩精品 中文字幕 视频在线| 成人福利一区二区视频在线 | 国产成AV人片久青草影院| 翁牛特旗| 无遮挡高潮国产免费观看| 博乐市| 秋霞电影院午夜无码免费视频| 亚洲国产在一区二区三区| 中文字幕结果国产精品|