imx6q 定时器设置

时间:2021-03-26 00:15:56
IMX6Q提供了一个General Purpose Timer (GPT)和两个Enhanced Periodic Interrupt Timer (EPIT),共三个定时器中断,但是GPT已经用作系统的时钟中断了。
如果我们要用到其他的时钟中断,就只能用两个EPIT。
可是,在IMX6Q的BSP里面没有提供EPIT的中断,下面就介绍下如何实现EPIT中断。
1 在dts文件里面添加对EPIT的支持.
  在imx6q.dtsi中添加下面的代码
  epit1: epit@020d0000 { /* EPIT1 */
                  compatible = "fsl,imx6q-epit1"; 
                reg = <0x020d0000 0x4000>;
                interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
  };


  epit2: epit@020d4000 { /* EPIT2 */
                compatible = "fsl,imx6q-epit2"; 
                reg = <0x020d4000 0x4000>;
                interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>;
  };
2 在 include/dt-bindings/clock/imx6qdl-clock.h
  增加
  #define IMX6QDL_CLK_EPIT1                        264
  #define IMX6QDL_CLK_EPIT2                        265
  这两个宏的数值根据实际情况定义了,也许不是这两个数字,反正跟在原来定义的宏后面添加就好了。
  修改arch/arm/mach-imx/clk-imx6q.c
  在imx6q_clocks_init函数中添加
  clk[IMX6QDL_CLK_EPIT1]        = imx_clk_gate2("epit1",        "perclk",            base + 0x6c, 12);
  clk[IMX6QDL_CLK_EPIT2]        = imx_clk_gate2("epit2",        "perclk",            base + 0x6c, 14);
  和
  clk_register_clkdev(clk[IMX6QDL_CLK_EPIT1], "per", "imx-epit.1");
  clk_register_clkdev(clk[IMX6QDL_CLK_EPIT2], "per", "imx-epit.2");
  这里一定不能漏了,否则在驱动中clk_get_sys会失败的。
  
3 在驱动中初始化定时器中断
  #define COUNT_TO_NS                15
  epit_reg *reg;
  int init_epit( )
  {
                struct device_node *node;
                u32 val;
                struct clk *timer_clk;
                node = of_find_compatible_node(NULL, NULL,"fsl,imx6q-epit2");
                if(node)
                {
                        reg = of_iomap(node, 0);
                        irq = irq_of_parse_and_map(node, 0);
                }
                else
                {
                        return -1;
                }
                writel(0,&(reg->EPITCR) );
                timer_clk = clk_get_sys("imx-epit.2", "per");
                if (IS_ERR(timer_clk)) {
                        return -1;
                }
                clk_prepare_enable(timer_clk);
                writel(0x0, &(reg->EPITCMPR));
        
                val = EPITCR_CLKSRC_REF_HIGH|EPITCR_IOVW|EPITCR_ENMOD|EPITCR_WAITEN|EPITCR_STOPEN;
         
                val |= EPITCR_RLD;
         
        
                writel(val ,&(reg->EPITCR));
                writel(0, &(reg->EPITLR));
                request_irq(irq, epit2_irq, 0, DEV_NAME, reg);
                return 0;
  }
  这里需要注意的是如果希望在6Q进入wait或者stop状态的时候定时器也能工作,一定要或上EPITCR_WAITEN和EPITCR_STOPEN。
  否则在wait或者stop状态时,定时器就停止计数了。
  
  
  设置定时器的超时时间
  void epit_set_count(epit_reg *reg,int delayus)
  {
                u32 val;
         
                val = delayus*1000/COUNT_TO_NS;
                writel(val, &(reg->EPITLR)); 
  }
  
  
  使能中断,在init_epit是没有打开中断的
  void epit_enable(epit_reg *reg,int isOn)
  {
                u32 val;
                val = readl(&(reg->EPITCR));
                if(isOn)
                {
                        val |= EPITCR_EN|EPITCR_OCIEN;
                }
                else
                {
                        val &= ~(EPITCR_EN|EPITCR_OCIEN);
                }
                writel(val, &(reg->EPITCR));
  }
  
  清中断标志位,这里如果产生中断后,一定要清中断标志,否则会不断产生中断。
  void clearint(epit_reg *reg)
  {
                writel(0x1, &(reg->EPITSR));
  }
  
  定义中断处理函数
  static irqreturn_t epit2_irq(int irq, void *dev_id)
  {
                clearint((epit_reg *)dev_id);
                epit_enable((epit_reg *)dev_id,0); 
         
         
                。。。。处理中断。。。
                return IRQ_HANDLED;
  }
  OK!大功告成!
  上面的代码设置了epit2,如果是epit1处理流程是一样的。