linux中断底层硬件操作方法…

来源:互联网 发布:java获取12时间戳 编辑:程序博客网 时间:2024/06/04 23:18
原文地址:linux中断底层硬件操作方法(转)作者:joee33

每一条中断线都有一个底层硬件操作函数集structirq_chip 。大多数控制方法都是重复的 ,基本上只要有中断响应 、 中断屏蔽 、 中断开启 、中断触发类型设置等方法就可以满足要求了。其他各种方法基本上和这些相同。

这些操作方法的实现在文件linux/arch/arm/plat-s3c24xx/irq.c中。

例如外部中断IRQ_EINT0 ~ IRQ_EINT3都用以下操作函数集:

static structirq_chip s3c_irq_eint0t4 = {
 .name  ="s3c-ext0",
 .ack  =s3c_irq_ack,
 .mask  =s3c_irq_mask,
 .unmask  =s3c_irq_unmask,
 .set_wake = s3c_irq_wake,
 .set_type =s3c_irqext_type,
};

 

static inlinevoid
s3c_irq_ack(unsigned int irqno)
{
 unsigned long bitval = 1UL<< (irqno - IRQ_EINT0);

 __raw_writel(bitval,S3C2410_SRCPND);
 __raw_writel(bitval, S3C2410_INTPND);
}

 

staticvoid
s3c_irq_mask(unsigned int irqno)
{
 unsigned long mask;

 irqno -=IRQ_EINT0;

 mask =__raw_readl(S3C2410_INTMSK);
 mask |= 1UL <<irqno;
 __raw_writel(mask, S3C2410_INTMSK);
}

 

staticvoid
s3c_irq_unmask(unsigned int irqno)
{
 unsigned long mask;

 if (irqno!= IRQ_TIMER4 && irqno !=IRQ_EINT8t23)
  irqdbf2("s3c_irq_unmask %dn",irqno);

 irqno -=IRQ_EINT0;

 mask =__raw_readl(S3C2410_INTMSK);
 mask &= ~(1UL<< irqno);
 __raw_writel(mask, S3C2410_INTMSK);
}

 

 int
s3c_irqext_type(unsigned int irq, unsigned int type)
{
 void __iomem *extint_reg;
 void __iomem *gpcon_reg;
 unsigned long gpcon_offset, extint_offset;
 unsigned long newvalue = 0, value;

 if ((irq >= IRQ_EINT0)&& (irq <=IRQ_EINT3))
 {
  gpcon_reg =S3C2410_GPFCON;
  extint_reg =S3C24XX_EXTINT0;
  gpcon_offset = (irq -IRQ_EINT0) * 2;
  extint_offset = (irq -IRQ_EINT0) * 4;
 }
。。。。。。

 __raw_writel(value,gpcon_reg);  //将对应管脚配置成中断功能


 switch (type)
 {
  case IRQ_TYPE_NONE:
   printk(KERN_WARNING"No edge setting!n");
   break;

  caseIRQ_TYPE_EDGE_RISING:
   newvalue =S3C2410_EXTINT_RISEEDGE;
   break;

  caseIRQ_TYPE_EDGE_FALLING:
   newvalue =S3C2410_EXTINT_FALLEDGE;
   break;

  caseIRQ_TYPE_EDGE_BOTH:
   newvalue =S3C2410_EXTINT_BOTHEDGE;
   break;

  caseIRQ_TYPE_LEVEL_LOW:
   newvalue =S3C2410_EXTINT_LOWLEV;
   break;

  caseIRQ_TYPE_LEVEL_HIGH:
   newvalue =S3C2410_EXTINT_HILEV;
   break;

  default:
   printk(KERN_ERR"No such irq type %d", type);
   return-1;
 }

 value =__raw_readl(extint_reg);  
 value = (value & ~(7<< extint_offset)) | (newvalue<< extint_offset);
 __raw_writel(value,extint_reg); //设置中断的触发方式。

 return 0;
}

中断申请函数

//中断申请函数request_irq()只是函数request_threaded_irq()的包装而已
request_irq(unsigned int irq, irq_handler_t handler, unsigned longflags,
    const char *name, void *dev)
{
 return request_threaded_irq(irq, handler, NULL,flags, name, dev);
}

 

int request_threaded_irq(unsigned int irq, irq_handler_thandler,
   irq_handler_t thread_fn, unsigned long irqflags,
    const char*devname, void *dev_id)
{
 struct irqaction *action;
 struct irq_desc *desc;
 int retval;

//中断类型标识IRQF_SHARED和IRQF_DISABLED不应当被同时设置。
 if ((irqflags &(IRQF_SHARED|IRQF_DISABLED)) ==
     (IRQF_SHARED|IRQF_DISABLED)){
  pr_warning(
    "IRQ %d/%s:IRQF_DISABLED is not guaranteed on shared IRQsn",
   irq,devname);
 }

#ifdef CONFIG_LOCKDEP
 
 irqflags |= IRQF_DISABLED;
#endif
 
 if ((irqflags & IRQF_SHARED)&& !dev_id)
  return -EINVAL;

 desc =irq_to_desc(irq);  //根据中断号获取中断线描述符结构体
 if (!desc)
  return -EINVAL;

 if (desc->status& IRQ_NOREQUEST)
  return -EINVAL;
 if (!handler)
  return -EINVAL;

//分配一个中断服务例程结构体action并初始化它的各字段。

 action = kzalloc(sizeof(struct irqaction),GFP_KERNEL); 
 if (!action)
  return -ENOMEM;

 action->handler =handler;
 action->thread_fn = thread_fn;//为NULL
 action->flags = irqflags;
 action->name = devname;

 //对于共享中断, 此特定值用来区分各中断,以便从共享中断线的诸多中断处理程序中删除指定的那一个。
 action->dev_id = dev_id;

//将该例程添加到单向链表desc->action上,并启动该例程。

 retval = __setup_irq(irq, desc, action);
 if (retval)
  kfree(action);

。。。。。。
 return retval;
}

 

static int
__setup_irq(unsigned int irq, struct irq_desc *desc, structirqaction *new)
{
 struct irqaction *old, **old_ptr;
 const char *old_name = NULL;
 unsigned long flags;
 int shared = 0;
 int ret;

。。。。。。


 old_ptr =&desc->action; //获取中断处理例程单向链表上的第一个例程,如果它为NULL说明该中断线是第一次被触发。
 old = *old_ptr;
 if (old) { //


。。。。。。

//如果该中断线上存在中断服务例程则让old_ptr指向该例程链表的尾部,以便加入新的服务例程
  do {
   old_ptr =&old->next;
   old =*old_ptr;
  } while (old);
  shared = 1;
 }

 if (!shared) {

//将操作函数集desc->chip的一些未设置的字段设为默认值。
  irq_chip_set_defaults(desc->chip);

  init_waitqueue_head(&desc->wait_for_threads);


#defineIRQ_TYPE_EDGE_RISING 0x00000001 
#defineIRQ_TYPE_EDGE_FALLING 0x00000002 
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING |IRQ_TYPE_EDGE_RISING)
#defineIRQ_TYPE_LEVEL_HIGH 0x00000004 
#defineIRQ_TYPE_LEVEL_LOW 0x00000008 
#defineIRQ_TYPE_SENSE_MASK 0x0000000f 

IRQF_TRIGGER_MASK在文件interrupt.h中定义

#defineIRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH |IRQF_TRIGGER_LOW |
    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)


可以看出只要外部中断设置了触发方式函数__irq_set_trigger()就会执行。

*/
  if (new->flags& IRQF_TRIGGER_MASK) 
   ret =__irq_set_trigger(desc,irq, new->flags &IRQF_TRIGGER_MASK);

   if(ret)
    gotoout_thread;
  } else
   compat_irq_chip_set_default_handler(desc);

//desc->status的标志IRQ_NOAUTOEN 在中断初始化函数s3c24xx_init_irq()中调用函数set_irq_flags()设置。

  if(!(desc->status & IRQ_NOAUTOEN)){
   desc->depth= 0;
   desc->status&= ~IRQ_DISABLED;
   desc->chip->startup(irq); //开启中断线
  } else
   desc->depth= 1;

。。。。。。
  }

 *old_ptr= new; //将新加入的中断处理例程添加到链表中

。。。。。。

//在proc文件系统中创建目录。

 new->irq = irq;
 register_irq_proc(irq, desc);
 new->dir = NULL;
 register_handler_proc(irq, new);

。。。。。。
 return ret;
}

 

中断卸载函数free_irq().。

如果指定的中断线不是共享的 , 那么 ,该函数删除处理程序的同时将禁用这条中断线 。 如果
中断线是共享的,则仅删除 dev_id 所对应的处理程序,而这条中断线本身只有在删除了最
后一个处理程序时才会被禁用。由此可以看出为什么惟一的 dev_ id 如此重要。对于共享的
中断线,需要一个惟一的信息来区分其上面的多个处理程序,并让 free_irq() 仅仅删除指定
的处理程序。如果 dev_id 非空,它都必须与需要删除的处理程序相匹配。非共享中断,该
域可以为空,但需要和注册时使用的指针一致。

 

static struct irqaction *__free_irq(unsigned int irq, void*dev_id)
{
 struct irq_desc *desc = irq_to_desc(irq);
 struct irqaction *action, **action_ptr;
 struct task_struct *irqthread;
 unsigned long flags;

 

 if (!desc)
  return NULL;


 action_ptr =&desc->action;
 for (;;) {
  action = *action_ptr;

 

  if(action->dev_id == dev_id)
   break;
  action_ptr =&action->next;
 }

*action_ptr = action->next;

。。。。。。
 if (!desc->action) { //无其他中断使用该中断线则禁止
  desc->status |=IRQ_DISABLED;
  if(desc->chip->shutdown)
   desc->chip->shutdown(irq);
  else
   desc->chip->disable(irq);
 }

。。。。。。。


 return action; //返回中断处理例程结构体
}
//在函数free_irq中将函数__free_irq返回的中断处理例程结构体释放掉。

void free_irq(unsigned int irq, void *dev_id)
{
 kfree(__free_irq(irq, dev_id));
0 0
原创粉丝点击