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

Marvell-linux研究—irq.c源代码分析

2007-11-10 22:15 302 查看
导读:
  Marvell-linux研究—irq.c源代码分析
  
  转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd
  作者联系方式:李先静
  更新时间:2007-7-13
  
  轮询、中断和DMA是外设与CPU通信的三种基本方式。在PXA3xx中,中断分为基本中断源(Primary Sources of interrupts)和二级中断源(Secondary Sources of interrupts)。多个二级中断源共享一个基本中断源(Primary Sources of interrupts),比如32路DMA通道共享一个基本中断源,发生中断时,光知道是DMA中断还不够,需要查询DMA相关寄存器,以确定具体的中断源。
  
  通过编程可以控制基本中断源(Primary Sources of interrupts)的优先级和类型(IRQ/FIQ),二级中断源主要是软件上的概念,CPU不需要关心这些,甚至不知道所谓二级中断源的存在,所以不能设置二级中断源的优先级和类型(IRQ/FIQ)。
  
  下面我们来看mach-pxa/irq.c中的代码:
  35 static void pxa_mask_low_irq(unsigned int irq)
  36 {
  37 #ifdef CONFIG_CPU_MONAHANS
  38 unsigned int temp = ~(1 <<(irq + PXA_IRQ_SKIP));
  39 __asm__ __volatile__ ("/n/
  40 mrc p6, 0, r1, c1, c0, 0 @ Read out ICMR/n/
  41 and r1, r1, %0 /n/
  42 mcr p6, 0, r1, c1, c0, 0 @ Write back"
  43 :
  44 :"r"(temp)
  45 :"r1");
  46 #else
  47 ICMR &= ~(1 <<(irq + PXA_IRQ_SKIP));
  48 #endif
  49 }
  50
  51 static void pxa_unmask_low_irq(unsigned int irq)
  52 {
  53 #ifdef CONFIG_CPU_MONAHANS
  54 unsigned int temp = (1 <<(irq + PXA_IRQ_SKIP));
  55 __asm__ __volatile__ ("/n/
  56 mrc p6, 0, r1, c1, c0, 0 @ Read out ICMR/n/
  57 orr r1, r1, %0 /n/
  58 mcr p6, 0, r1, c1, c0, 0 @ Write back"
  59 :
  60 :"r"(temp)
  61 :"r1");
  62 #else
  63 ICMR |= (1 <<(irq + PXA_IRQ_SKIP));
  64 #endif
  65 }
  66
  67 static struct irqchip pxa_internal_chip_low = {
  68 .ack = pxa_mask_low_irq,
  69 .mask = pxa_mask_low_irq,
  70 .unmask = pxa_unmask_low_irq,
  71 };
  
  79 static void pxa_mask_high_irq(unsigned int irq)
  80 {
  81 #ifdef CONFIG_CPU_MONAHANS
  82 unsigned int temp = ~(1 <<(irq - 32 + PXA_IRQ_SKIP));
  83 __asm__ __volatile__ ("/n/
  84 mrc p6, 0, r1, c7, c0, 0 @ Read out ICMR2/n/
  85 and r1, r1, %0 /n/
  86 mcr p6, 0, r1, c7, c0, 0 @ Write back"
  87 :
  88 :"r"(temp)
  89 :"r1");
  90 #else
  91 ICMR2 &= ~(1 <<(irq - 32 + PXA_IRQ_SKIP));
  92 #endif
  93 }
  94
  95 static void pxa_unmask_high_irq(unsigned int irq)
  96 {
  97 #ifdef CONFIG_CPU_MONAHANS
  98 unsigned int temp = (1 <<(irq - 32 + PXA_IRQ_SKIP));
  99 __asm__ __volatile__ ("/n/
  100 mrc p6, 0, r1, c7, c0, 0 @ Read out ICMR2/n/
  101 orr r1, r1, %0 /n/
  102 mcr p6, 0, r1, c7, c0, 0 @ Write back"
  103 :
  104 :"r"(temp)
  105 :"r1");
  106
  107 #else
  108 ICMR2 |= (1 <<(irq - 32 + PXA_IRQ_SKIP));
  109 #endif
  110 }
  111
  112 static struct irqchip pxa_internal_chip_high = {
  113 .ack = pxa_mask_high_irq,
  114 .mask = pxa_mask_high_irq,
  115 .unmask = pxa_unmask_high_irq,
  116 };
  117
  118 #endif
  
  ICMR是中断屏蔽寄存器,它的位域决定哪些中断被禁止,哪些中断被启用。在PXA3xx中,中断屏蔽寄存器由两个32位的寄存器组成,最多允许64个基本中断源(实际上没有那么多,有几个位保留给将来用)。这里low是指低32位的中断,high是指高32位的中断,它们与中断优先级无关。ICMR控制低位中断,而ICMR2控制高位中断。
  
  操作寄存器的方式有两种,一种是通过协处理器指令读写,读取用mrc指令,写入用mcr指令。操作中断相关寄存器使用6号协处理,中断寄存器与协处理寄存器对应关系可以参考表12-1。上面的汇编代码就是通过协处理器操作中断寄存器的。
  
  另外一种方式是通过内存映射的I/O寄存器,它的形式和读取普通内存相同,使用方法简单直观,但与协处理器方式相比,它的速度稍慢。上面的else宏中的语句就属于这种方式。
  
  130 static int pxa_gpio_irq_type(unsigned int irq, unsigned int type)
  131 {
  132 int gpio, idx;
  133
  134 gpio = IRQ_TO_GPIO(irq);
  135 idx = gpio >>5
  136
  137 if (type == IRQT_PROBE) {
  138 /* Don't mess with enabled GPIOs using preconfigured edges or
  139 GPIOs set to alternate function during probe */
  140 if ((GPIO_IRQ_rising_edge[idx] | GPIO_IRQ_falling_edge[idx]) &
  141 GPIO_bit(gpio))
  142 return 0
  143 #ifndef CONFIG_CPU_MONAHANS
  144 if (GAFR(gpio) &(0x3 <<(((gpio) &0xf)*2)))
  145 return 0
  146 #endif
  147 type = __IRQT_RISEDGE | __IRQT_FALEDGE;
  148 }
  149
  150 /* printk(KERN_DEBUG "IRQ%d (GPIO%d): ", irq, gpio); */
  151
  152 #ifndef CONFIG_CPU_MONAHANS
  153 pxa_gpio_mode(gpio | GPIO_IN);
  154 #else
  155 G CDR(gpio) &= (1 <<(gpio &0x1f)); /* Set Direction Input */
  156 #endif
  157
  158 if (type &__IRQT_RISEDGE) {
  159 __set_bit (gpio, GPIO_IRQ_rising_edge);
  160 }
  161 else {
  162 __clear_bit (gpio, GPIO_IRQ_rising_edge);
  163 }
  164
  165 if (type &__IRQT_FALEDGE) {
  166 __set_bit (gpio, GPIO_IRQ_falling_edge);
  167 }
  168 else {
  169 __clear_bit (gpio, GPIO_IRQ_falling_edge);
  170 }
  171
  172 GRER(gpio) = GPIO_IRQ_rising_edge[idx] &GPIO_IRQ_mask[idx];
  173 GFER(gpio) = GPIO_IRQ_falling_edge[idx] &GPIO_IRQ_mask[idx];
  174 return 0
  175 }
  
  这个函数用来设置GPIO中断的类型,也就是触发中断的条件,上升沿触发、下降沿触发、两者同时触发、或者禁止触发,这些设置通过写GRER和GFER两个寄存器生效,同时会保存到GPIO_IRQ_rising_edge和GPIO_IRQ_falling_edge两个变量里。
  
  181 static void pxa_ack_low_gpio(unsigned int irq)
  182 {
  183 GEDR0 = (1 <<(irq - IRQ_GPIO0));
  184 }
  185
  186 static struct irqchip pxa_low_gpio_chip = {
  187 .ack = pxa_ack_low_gpio,
  188 .mask = pxa_mask_low_irq,
  189 .unmask = pxa_unmask_low_irq,
  190 .set_type = pxa_gpio_irq_type,
  191 };
  
  GPIO不都一样吗?为什么还来个low_gpio呢?原来GPIO0和GPIO1是独自占用基本中断源(Primary Sources of interrupts)的,而其它GPIO则共享10号中断。所以把GPIO0和GPIO1称为low_gpio,而把其它GPIO称为muxed_gpio。
  
  Irqchip中的ack函数,用来确认中断被处理了,通常用来清除二级中断的状态,这里清除GEDR中相应的位。
  
  197 static void pxa_gpio_demux_handler(unsigned int irq, struct irqdesc *desc,
  198 struct pt_regs *regs)
  199 {
  200 unsigned int mask;
  201 int loop;
  202
  203 do {
  204 loop = 0
  205
  206 mask = GEDR0 &~3
  207 if (mask) {
  208 GEDR0 = mask;
  209 irq = IRQ_GPIO(2);
  210 desc = irq_desc + irq;
  211 mask >>= 2
  212 do {
  213 if (mask &1)
  214 desc_handle_irq(irq, desc, regs);
  215 irq++;
  216 desc++;
  217 mask >>= 1
  218 } while (mask);
  219 loop = 1
  220 }
  221
  222 mask = GEDR1;
  223 if (mask) {
  224 GEDR1 = mask;
  225 irq = IRQ_GPIO(32);
  226 desc = irq_desc + irq;
  227 do {
  228 if (mask &1)
  229 desc_handle_irq(irq, desc, regs);
  230 irq++;
  231 desc++;
  232 mask >>= 1
  233 } while (mask);
  234 loop = 1
  235 }
  236
  237 mask = GEDR2;
  238 if (mask) {
  239 GEDR2 = mask;
  240 irq = IRQ_GPIO(64);
  241 desc = irq_desc + irq;
  242 do {
  243 if (mask &1)
  244 desc_handle_irq(irq, desc, regs);
  245 irq++;
  246 desc++;
  247 mask >>= 1
  248 } while (mask);
  249 loop = 1
  250 }
  251
  252 #if PXA_LAST_GPIO >= 96
  253 mask = GEDR3;
  254 if (mask) {
  255 GEDR3 = mask;
  256 irq = IRQ_GPIO(96);
  257 desc = irq_desc + irq;
  258 do {
  259 if (mask &1)
  260 desc_handle_irq(irq, desc, regs);
  261 irq++;
  262 desc++;
  263 mask >>= 1
  264 } while (mask);
  265 loop = 1
  266 }
  267 #endif
  268 } while (loop);
  269 }
  
  这函数用来调用所有GPIO(>=2)的中断处理函数,总共有4个GEDR寄存器,它循环检查所有状态,直到处理全部GPIO为止。外层循环看似有点奇怪,其实它的目的是保证,把最近发生的中断也处理掉,即使这个中断发生在当前函数执行过程之中。
  
  279 static void pxa_mask_muxed_gpio(unsigned int irq)
  280 {
  281 int gpio = irq - IRQ_GPIO(2) + 2
  282
  283 u32 tem=0
  284 if ( GRER(gpio) &GPIO_bit(gpio)) {
  285 __set_bit (gpio, GPIO_IRQ_rising_edge);
  286 } else{
  287 __clear_bit (gpio, GPIO_IRQ_rising_edge);
  288 }
  289
  290 if (GFER(gpio) &GPIO_bit(gpio)) {
  291 __set_bit (gpio, GPIO_IRQ_falling_edge);
  292 } else{
  293 __clear_bit (gpio, GPIO_IRQ_falling_edge);
  294 }
  295
  296 __clear_bit(gpio, GPIO_IRQ_mask);
  297 GRER(gpio) &= ~GPIO_bit(gpio);
  298 GFER(gpio) &= ~GPIO_bit(gpio);
  299
  300 tem = GRER(gpio);
  301 tem = GFER(gpio);
  302 }
  303
  304 static void pxa_unmask_muxed_gpio(unsigned int irq)
  305 {
  306 int gpio = irq - IRQ_GPIO(2) + 2
  307 int idx = gpio >>5
  308 u32 tem=0
  309 __set_bit(gpio, GPIO_IRQ_mask);
  310 GRER(gpio) = GPIO_IRQ_rising_edge[idx] &GPIO_IRQ_mask[idx];
  311 GFER(gpio) = GPIO_IRQ_falling_edge[idx] &GPIO_IRQ_mask[idx];
  312 tem = GRER(gpio);
  313 }
  314
  315 static struct irqchip pxa_muxed_gpio_chip = {
  316 .ack = pxa_ack_muxed_gpio,
  317 .mask = pxa_mask_muxed_gpio,
  318 .unmask = pxa_unmask_muxed_gpio,
  319 .set_type = pxa_gpio_irq_type,
  320 };
  
  pxa_mask_muxed_gpio保存GPIO当前中断设置,然后屏蔽相应的中断。pxa_unmask_muxed_gpio允许GPIO中断,至于中断方式,还要由GPIO_IRQ_rising_edge和GPIO_IRQ_falling_edge中的值决定。
  
  323 void __init pxa_init_irq(void)
  324 {
  325 int irq;
  326
  327 #ifdef CONFIG_CPU_MONAHANS
  328 __asm__ __volatile__ ("/n/
  329 mov r0, #0 /n/
  330 mcr p6, 0, r0, c1, c0, 0 @ ICMR=0 /n/
  331 mcr p6, 0, r0, c2, c0, 0 @ ICLR=0"
  332 :
  333 :
  334 : "r0");
  335 #else
  336 /* disable all IRQs */
  337 ICMR = 0
  338
  339 /* all IRQs are IRQ, not FIQ */
  340 ICLR = 0
  341 #endif
  342 /* clear all GPIO edge detects */
  343 GFER0 = 0
  344 GFER1 = 0
  345 GFER2 = 0
  346 GRER0 = 0
  347 GRER1 = 0
  348 GRER2 = 0
  349 GEDR0 = GEDR0;
  350 GEDR1 = GEDR1;
  351 GEDR2 = GEDR2;
  352
  353 #if defined(CONFIG_PXA27x) || defined(CONFIG_CPU_MONAHANS)
  354 #ifdef CONFIG_CPU_MONAHANS
  355 __asm__ __volatile__ ("/n/
  356 mov r0, #0 /n/
  357 mcr p6, 0, r0, c7, c0, 0 @ ICMR2=0 /n/
  358 mcr p6, 0, r0, c8, c0, 0 @ ICLR2=0"
  359 :
  360 :
  361 : "r0");
  362 #else
  363 /* And similarly for the extra regs on the PXA27x */
  364 ICMR2 = 0
  365 ICLR2 = 0
  366 #endif
  367
  368 GFER3 = 0
  369 GRER3 = 0
  370 GEDR3 = GEDR3;
  371 #endif /*#if defined(CONFIG_PXA27x) || defined(CONFIG_CPU_MONAHANS) */
  372
  373 /* only unmasked interrupts kick us out of idle */
  374 ICCR = 1
  375
  376 /* GPIO 0 and 1 must have their mask bit always set */
  377 GPIO_IRQ_mask[0] = 3
  378
  379 for (irq = PXA_IRQ(PXA_IRQ_SKIP); irq <= PXA_IRQ(31); irq++) {
  380 set_irq_chip(irq, &pxa_internal_chip_low);
  381 set_irq_handler(irq, do_level_IRQ);
  382 set_irq_flags(irq, IRQF_VALID);
  383 }
  384
  385 #if PXA_INTERNAL_IRQS >32
  386 for (irq = PXA_IRQ(32); irq =2 edge detect interrupts */
  406 set_irq_chip(IRQ_GPIO_2_x, &pxa_internal_chip_low);
  407 set_irq_chained_handler(IRQ_GPIO_2_x, pxa_gpio_demux_handler);
  408 }
  
  323-341 它把ICMR清零,即屏蔽所有低位中断。把ICLR清零,即把所有低位中断设置为IRQ。353-366以同样的方式去设置高位中断,不知道为什么不和低位设置放在一起,这里好像与时序也没有什么关系啊。
  
  343-351 清除前96个GPIO的中断触发条件。
  
  368-370 清除后32个GPIO的中断触发条件。
  
  对GEDR的操作,这里的代码可能有些让人不解。其实很简单,原因是对GEDR的写操作有点特别,写1是把它清零,写0对它没有影响,把GEDR的值读出来,再写回去,这样原先为1的被清零,为0的保持不变。
  
  374 把ICCR中的DIM置1,在低功耗模式下,它只允许未屏蔽的中断唤醒CPU。
  
  377 前面一行的注释说了等于没说,为什么要把GPIO0和GPIO1屏蔽掉呢?原因很简单,它们使用基本中断源,如果不屏蔽,基本中断源和二级中断源同时发生,会造成不必要的混乱。
  
  379-408 设置中断处理函数。
  
  ~~end~~
  Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1689344
本文转自
http://blog.csdn.net/absurd/archive/2007/07/13/1689344.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: