最近由于工作的原因,开始接触了Atheros AR9344无线通信芯片,对硬件关注的比较少。Atheros AR9344是mips为内核的处理芯片。之前主要关注的是ARM内核,对mips内核的相关知识,还是比较欠缺。今天就看看Atheros AR9344中的中断处理流程。
首先站在系统的最高层来看看,系统中申请了那些中断。
/proc # cat interrupts
CPU0
2: 0 dummy_cctv wifi0
3: 537 MIPS ehci_hcd:usb1
4: 534 MIPS eth0
6: 0 MIPS cascade_cctv
7: 27152 MIPS timer
18: 0 ath_misc_cctv_suiyuan cascade_cctv
19: 1189 ath_misc_cctv_suiyuan serial
64: 0 ath_pci_cctv wifi1
第一列显示的是中断号,
第二列显示的是中断的次数,
第三列为:此中断对应的struct irq_chip结构中的name,因为每一个irq于一个特定的struct irq_chip相联系。
第四列为:申请此中断时的名称。
重要的一个数据结构,硬件中断描述符。
/**
* 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);
int (*set_affinity)(unsigned int irq,
const struct cpumask *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;
};
Handle.c (z:\wlan\src\linux\kernels\mips-linux-2.6.31\kernel\irq)
Irq.c (z:\wlan\src\linux\kernels\mips-linux-2.6.31\arch\mips\kernel)
asmlinkage void __init start_kernel(void)
{
early_irq_init();
init_IRQ();//用来初始化中断相关的代码。
}
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),
}
};
static unsigned int kstat_irqs_all[NR_IRQS][NR_CPUS];
int __init early_irq_init(void)
{
struct irq_desc *desc;
int count;
int i;
init_irq_default_affinity();
printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS); //NR_IRQS:128
desc = irq_desc;
count = ARRAY_SIZE(irq_desc);
for (i = 0; i < count; i++) {
desc[i].irq = i;
alloc_desc_masks(&desc[i], 0, true);
init_desc_masks(&desc[i]);
desc[i].kstat_irqs = kstat_irqs_all[i];
}
return arch_early_irq_init();
}
void __init init_IRQ(void)
{
int i;
#ifdef CONFIG_KGDB
if (kgdb_early_setup)
return;
#endif
for (i = 0; i < NR_IRQS; i++){
set_irq_noprobe(i);
}
printk("cctv: %s: line:%d\n", __FUNCTION__,__LINE__);
arch_init_irq();
#ifdef CONFIG_KGDB
if (!kgdb_early_setup)
kgdb_early_setup = 1;
#endif
}
Irq.c (z:\wlan\src\linux\kernels\mips-linux-2.6.31\arch\mips\atheros)
void __init arch_init_irq(void) //主要对AR9344平台的中断进行初始化。
{
/*
* initialize our interrupt controllers
*/
printk("cctv: %s: line:%d\n", __FUNCTION__,__LINE__);
mips_cpu_irq_init();
ath_misc_irq_init(ATH_MISC_IRQ_BASE);
ath_gpio_irq_init(ATH_GPIO_IRQ_BASE);
#ifdef CONFIG_PCI
ath_pci_irq_init(ATH_PCI_IRQ_BASE);
#endif
/*
* enable cascades
*/
setup_irq(ATH_CPU_IRQ_MISC, &cascade);
setup_irq(ATH_MISC_IRQ_GPIO, &cascade);
#ifdef CONFIG_PCI
setup_irq(ATH_CPU_IRQ_PCI, &cascade);
#endif
ath_arch_init_irq();
set_c0_status(ST0_IM);
}
//先看看下面这个函数:给特定的中断号建立起一个特定的中断处理函数
Void set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle)
{//下面这俩个函数主要就是对irq对应的struct irq_desc的成员进行赋值。
set_irq_chip(irq, chip);
__set_irq_handler(irq, handle, 0, NULL);
}
//下面这个函数是将一个特定的中断号于数据结构struct irq_chip建立其联系,我们可以从函数的俩个参数看出来。
/**
* set_irq_chip - set the irq chip for an irq
* @irq: irq number
* @chip: pointer to irq chip description structure
*/
int set_irq_chip(unsigned int irq, struct irq_chip *chip)
{
struct irq_desc *desc = irq_to_desc(irq);//根据irq号,取出irq_desc。
unsigned long flags;
if (!desc) {
WARN(1, KERN_ERR "Trying to install chip for IRQ%d\n", irq);
return -EINVAL;
}
if (!chip)
chip = &no_irq_chip;
spin_lock_irqsave(&desc->lock, flags);
irq_chip_set_defaults(chip);
desc->chip = chip; //将参数赋值给特定中断对应的chip。
spin_unlock_irqrestore(&desc->lock, flags);
return 0;
}
/*
* Fixup enable/disable function pointers
*/
void irq_chip_set_defaults(struct irq_chip *chip)
{//给struct irq_chip结构成员赋初始值。
if (!chip->enable)
chip->enable = default_enable;
if (!chip->disable)
chip->disable = default_disable;
if (!chip->startup)
chip->startup = default_startup;
/*
* We use chip->disable, when the user provided its own. When
* we have default_disable set for chip->disable, then we need
* to use default_shutdown, otherwise the irq line is not
* disabled on free_irq():
*/
if (!chip->shutdown)
chip->shutdown = chip->disable != default_disable ?
chip->disable : default_shutdown;
if (!chip->name)
chip->name = chip->typename;
if (!chip->end)
chip->end = dummy_irq_chip.end;
}
/*
* Determine interrupt source among interrupts that use IP6
ath_misc_irq_init(ATH_MISC_IRQ_BASE);
*/
/*
* IRQ Map.
* There are 4 conceptual ICs in the system. We generally give a block of 16
* irqs to each IC.
* CPU : 0 - 0xf
* MISC: 0x10 - 0x1f
* GPIO: 0x20 - 0x3f
* PCI : 0x40 - 0x4f
*
*/
#define ATH_CPU_IRQ_BASE 0x00
#define ATH_MISC_IRQ_BASE 0x10
#define ATH_GPIO_IRQ_BASE 0x20
#define ATH_MISC_IRQ_COUNT 14
下面的函数主要也就是对特定的中断号关联特定的handler。
static void ath_misc_irq_init(int irq_base)
{
int i;
for (i = irq_base; i < irq_base + ATH_MISC_IRQ_COUNT; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = NULL;
irq_desc[i].depth = 1;
set_irq_chip_and_handler(i, &ath_misc_irq_controller,
handle_percpu_irq);
}
}
/**
* setup_irq - setup an interrupt
* @irq: Interrupt line to setup
* @act: irqaction for the interrupt
*
* Used to statically setup interrupts in the early boot process.
*/
int setup_irq(unsigned int irq, struct irqaction *act)
{
struct irq_desc *desc = irq_to_desc(irq);
return __setup_irq(irq, desc, act);
}
///////////////////////////////////////////////////////////////////////////////////////
下面来看看驱动中对注册到系统的中断是如何申请和处理的。
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{//中断申请函数。
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
ret = request_irq(ATH_GPIO_IRQn(BUTTON_GPIO), ar9344buttons_interrupt, 0,"ar9344 button", NULL);
函数中的主要完成的事情为:
desc = irq_to_desc(irq);和retval = __setup_irq(irq, desc, action);
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id){
struct irqaction *action;
struct irq_desc *desc;
int retval;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (desc->status & IRQ_NOREQUEST)
return -EINVAL;
if (!handler)
return -EINVAL;
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
retval = __setup_irq(irq, desc, action);
}