您的位置:首页 > 运维架构 > Linux

Linux中断子系统-中断初始化

2013-12-25 11:36 441 查看
前面学习了一下linux中断子系统的中断处理过程,看到中断最终由desc->handle_irq(irq, desc)处理。linux系统最早调用与中断相关的函数是early_trap_init(),他主要工作是把我们定义好的中断向量表拷贝到CPU的中断地址。然后linux调用early_irq_init()初始化linux中断系统的核心数据,下面我们就从中断模型的核心数据开始来继续学习linux的中断系统。
linux中断机制的核心数据结构 irq_desc, 它完整地描述了一条中断线 (或称为 “中断通道” )。其中irq_desc 结构在 include/linux/irqdesc.h 中定义:

struct irq_desc {
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;
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_PROC_FS
struct proc_dir_entry	*dir;
#endif
struct module		*owner;
const char		*name;
} ____cacheline_internodealigned_in_smp;


其中irq_data 结构在 include/linux/irq.h 中定义:

struct irq_data {
unsigned int		irq;
unsigned long		hwirq;
unsigned int		node;
unsigned int		state_use_accessors;
struct irq_chip		*chip; /* 低层次的硬件操作 */
struct irq_domain	*domain;
void			*handler_data;/* chip 方法使用的数据*/
void			*chip_data;    /* chip 私有数据 */
struct msi_desc		*msi_desc;
cpumask_var_t		affinity;
};


其中irq_desc 结构在 include/linux/irqdesc.h 中定义:

struct irqaction {
irq_handler_t		handler;//中断处理函数,注册时提供
void			*dev_id;//设备id
void __percpu		*percpu_dev_id;
struct irqaction	*next;//如果有中断共享,则继续执行,
irq_handler_t		thread_fn;
struct task_struct	*thread;
unsigned int		irq;//中断号,注册时提供
unsigned int		flags;
unsigned long		thread_flags;//中断标志,注册时提供
unsigned long		thread_mask;//中断掩码
const char		*name;//中断名称
struct proc_dir_entry	*dir;//指向IRQn相关的/proc/irq/n目录的描述符
} ____cacheline_internodealigned_in_smp;

在注册中断号为irq的中断服务程序时,系统会根据注册参数封装相应的irqaction结构体。并把中断号为irq的irqaction结构体写入irq_desc
[irq]->action。这样就把设备的中断请求号与该设备的中断服务例程irqaction联系在一起了。当CPU接收到中断请求后,就可以根据中断号通过irq_desc
[]找到该设备的中断服务程序。 它的定义在kernel/irq/irqdesc.c:

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq	= handle_bad_irq,
.depth		= 1,
.lock		= __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};

start_kernel()函数调用early_irq_init()和init_IRQ()两个函数来初始化中断管理系统,其实就是初始化irq_desc的结构。

在start_kernel()函数中调用了early_irq_init()函数,这个函数在kernel/irq/irqdesc.c文件中定义。这个函数将用于管理中断的irq_desc[NR_IRQS]数组的每个元素的部分字段设置为确定的状态,它设置每一个成员的中断号

int __init early_irq_init(void)
{
int count, i, node = first_online_node;
struct irq_desc *desc;

init_irq_default_affinity();

printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);

desc = irq_desc;
count = ARRAY_SIZE(irq_desc);

for (i = 0; i < count; i++) {
desc[i].kstat_irqs = alloc_percpu(unsigned int);
alloc_masks(&desc[i], GFP_KERNEL, node);
raw_spin_lock_init(&desc[i].lock);
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
desc_set_defaults(i, &desc[i], node, NULL);
}
return arch_early_irq_init();
}

early_irq_init属于与硬件和平台无关的通用逻辑层,它完成irq_desc结构的内存申请,为它们其中某些字段填充默认值,完成后调用体系相关的arch_early_irq_init函数完成进一步的初始化工作,不过ARM体系没有实现arch_early_irq_init。

接着,start_kernel调用定义在arch\arm\kernel\irq.c的init_IRQ()函数,它会直接调用所属板子machine_desc结构体中的init_irq回调。machine_desc通常在板子的特定代码中,使用MACHINE_START和MACHINE_END宏进行定义。

void __init init_IRQ(void)
{
machine_desc->init_irq();
}

machine_desc在setup_arch()函数中被初始化

mdesc = setup_machine_fdt(__atags_pointer);
if (!mdesc)
mdesc = setup_machine_tags(machine_arch_type);
machine_desc = mdesc;


struct machine_desc类型的结构,mach_desc里定义了一些关键的体系架构相关的信息。比如我手上这个板子的源码:

DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)")
/* Maintainer: Atmel */
.timer		= &at91sam926x_timer,
.map_io		= at91_map_io,
.handle_irq	= at91_aic5_handle_irq,
.init_early	= at91_dt_initialize,
.init_irq	= at91_dt_init_irq,
.init_machine	= at91_dt_device_init,
.dt_compat	= sama5_dt_board_compat,
MACHINE_END

static const char *at91_dt_board_compat[] __initdata = {
"atmel,at91sam9",
NULL
};


MACHINE_START或DT_MACHINE_START宏的作用是对mach_desc结构体进行初始化。然后setup_arch去为相应的芯片找到相应的结构体。我们跟踪代码看看:

static void __init at91_dt_init_irq(void)
{
of_irq_init(irq_of_match);
}
of_irq_init扫描和初始匹配中断控制器,扫描设备树匹配中断控制器节点,并调用其初始化函数,

void __init of_irq_init(const struct of_device_id *matches)
{
struct device_node *np, *parent = NULL;
struct intc_desc *desc, *temp_desc;
struct list_head intc_desc_list, intc_parent_list;

INIT_LIST_HEAD(&intc_desc_list);
INIT_LIST_HEAD(&intc_parent_list);

for_each_matching_node(np, matches) {
if (!of_find_property(np, "interrupt-controller", NULL))
continue;
/*
* Here, we allocate and populate an intc_desc with the node
* pointer, interrupt-parent device_node etc.
*/
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (WARN_ON(!desc))
goto err;

desc->dev = np;
desc->interrupt_parent = of_irq_find_parent(np);
if (desc->interrupt_parent == np)
desc->interrupt_parent = NULL;
list_add_tail(&desc->list, &intc_desc_list);
}

/*
* The root irq controller is the one without an interrupt-parent.
* That one goes first, followed by the controllers that reference it,
* followed by the ones that reference the 2nd level controllers, etc.
*/
while (!list_empty(&intc_desc_list)) {
/*
* Process all controllers with the current 'parent'.
* First pass will be looking for NULL as the parent.
* The assumption is that NULL parent means a root controller.
*/
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
const struct of_device_id *match;
int ret;
of_irq_init_cb_t irq_init_cb;

if (desc->interrupt_parent != parent)
continue;

list_del(&desc->list);
match = of_match_node(matches, desc->dev);
if (WARN(!match->data,
"of_irq_init: no init function for %s\n",
match->compatible)) {
kfree(desc);
continue;
}

pr_debug("of_irq_init: init %s @ %p, parent %p\n",
match->compatible,
desc->dev, desc->interrupt_parent);
irq_init_cb = match->data;
ret = irq_init_cb(desc->dev, desc->interrupt_parent);
if (ret) {
kfree(desc);
continue;
}

/*
* This one is now set up; add it to the parent list so
* its children can get processed in a subsequent pass.
*/
list_add_tail(&desc->list, &intc_parent_list);
}

/* Get the next pending parent that might have children */
desc = list_first_entry(&intc_parent_list, typeof(*desc), list);
if (list_empty(&intc_parent_list) || !desc) {
pr_err("of_irq_init: children remain, but no parents\n");
break;
}
list_del(&desc->list);
parent = desc->dev;
kfree(desc);
}

list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
list_del(&desc->list);
kfree(desc);
}
err:
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
list_del(&desc->list);
kfree(desc);
}
}


这个函数完成对于中断控制器的初始化,并且设置中断描述符的相应的函数指针的值,以在中断发生时发生时,调用这些函数来完成芯片级的处理。为每个irq_desc结构安装irq_chip指针,使他指向正确的中断控制器所对应的irq_chip结构的实例,同时,如果该平台中的中断线有多路复用(多个中断公用一个irq中断线)的情况,还应该初始化irq_desc中相应的字段和标志,以便实现中断控制器的级联。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: