tiny4412 设备树之定时器中断(四)

开发板:tiny4412(1611)
内核:linux4.4
编译器: arm-none-linux-gnueabi-gcc (gcc version 4.8.3 20140320)

这里写图片描述
Exynos 4412 has five 32-bit Pulse Width Modulation (PWM) timers. These timers generate internal interrupts for the ARM subsystem. Additionally, timers 0, 1, 2, and 3 include a PWM function that drives an external I/O signal. The PWM in timer 0 has an optional dead-zone generator capability to support a large current device. Timer 4 is an internal timer without output pins.

我们使用定时器3做实验。

Timer3相关寄存器

REG: 0x139D_0000

Register Offset Function
TCFG0 0x0000 分频
TCFG1 0x0004 分频
TCON 0x0008 控制
TCNTB3 0x0030 向下计数,重装
TCNTO3 0x0038 观察寄存器
TINT_CSTAT 0x0044 定时器中断控制

If the timer reaches 0, then TCNTBn and TCMPBn registers are loaded into TCNTn and TCMPn. If TCNTn reaches 0, then the interrupt request occurs if it enables the interrupt (TCNTn and TCMPn are the names of the internal registers. It reads the TCNTn register from the TCNTOn register).
TCNTn 不可见,只能通过读TCNTO3来获取TCNTn的值。

GIC中断控制器

Generic Interrupt Controller (GIC) is a centralized resource that supports and manages interrupts in a system.

Supports three interrupt types:
 Software Generated Interrupt (SGI)
 Private Peripheral Interrupt (PPI)
 Shared Peripheral Interrupt (SPI)

PPI: This is a peripheral interrupt that is specific to a single processor.
SPI: This is a peripheral interrupt that the Distributor can route to any combination of processors

这里写图片描述

这里写图片描述


devicetree

        timer3demo@139D0000{
                compatible = "tiny4412,timer3demo";
                reg = <0x139D0000 0x48>;
                interrupts = <0 40 0>;
                clocks = <&clock CLK_PWM>;
                clock-names = "timers";
        };

关于clock可以看这个http://blog.csdn.net/qq_33160790/article/details/79360548
在说节点interrupts属性之前,先提一下中断控制器,它包括interrupt-cells和
interrupt-controller两个属性。
exynos4.dtsi:
这里写图片描述
exynos4x12-pinctrl.dtsi上:
这里写图片描述
节点可以使用interrupt-parent来指定使用的中断控制器,如果未指定则继承父节点。
exynos4.dtsi中可以看到根节点指定interrupt-parent为gic。
这里写图片描述
节点中interrupts属性指定用到的中断号、触发方法,这个属性有多少个cell由依附的中断控制器的interrupt-cells决定。具体每个的含义,由驱动决定,会在文档中说明。对于gic,在
Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt中如下说明:
这里写图片描述
搜索exynos平台的设备树时发现第三个cell都为0,这可能是soc实现的差异吧。
Timer3使用的是SPI No.40,所以interrupts = <0 40 0>;


驱动:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/export.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/sched.h>

#define timer_debug printk("TCFG0:      %08x\n",base->TCFG0);   \
            printk("TCFG1:      %08x\n",base->TCFG1);   \
            printk("TCON:       %08x\n",base->TCON);    \
            printk("TCNTB3:     %08x\n",base->TCNTB3);  \
            printk("TCMPB3:     %08x\n",base->TCMPB3);  \
            printk("TCNTO3:     %08x\n",base->TCNTO3);  \
            printk("TINT_CSTAT: %08x\n",base->TINT_CSTAT)

typedef struct TIMER{  
    unsigned int TCFG0; 
    unsigned int TCFG1; 
    unsigned int TCON;
    unsigned int TCNTB0;
    unsigned int TCMPB0;
    unsigned int TCNTO0;
    unsigned int TCNTB1;
    unsigned int TCMPB1;
    unsigned int TCNTO1;
    unsigned int TCNTB2;
    unsigned int TCMPB2;
    unsigned int TCNTO2;
    unsigned int TCNTB3;
    unsigned int TCMPB3;
    unsigned int TCNTO3;
    unsigned int TCNTB4;
    unsigned int TCNTO4;
    unsigned int TINT_CSTAT;
}timer;

volatile timer *base=NULL;
struct clk *clk=NULL;

static irqreturn_t timer3_interrupt(int irq, void *dev_id){

    base->TINT_CSTAT=0x100;
    printk("%s enter.\n", __func__);    

    return IRQ_HANDLED;
}
static int timer3_probe(struct platform_device *pdev) {  
    struct resource *res=NULL,*irq=NULL;
    struct device *dev = &pdev->dev;  
    int ret;

    printk("%s enter.\n", __func__);

        res=platform_get_resource(pdev,IORESOURCE_MEM,0);
        if(!res){
                dev_err(dev, "failed to get timer base mem resource\n");
            return -ENOMEM;
        }

        base=devm_ioremap_resource(dev,res);
        if(base<0){
                dev_err(dev, "failed to get timer base mem\n");
            return -ENOMEM;
        }

    clk=devm_clk_get(dev,"timers");
        if(IS_ERR(clk)){
            dev_err(dev, "failed to get timer base clk\n");
            return PTR_ERR(clk);
        }
    //ret = clk_prepare_enable(clk);
    clk_prepare(clk);
    ret=clk_enable(clk);
    if (ret < 0){
        dev_err(dev, "failed to enable base clock\n");
            return ret;
    }
    printk("rate:%d\n",clk_get_rate(clk));
#if 1
    irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (irq == NULL){
                printk("platform_get_resource irq error\n");
            return -EINVAL;
        }
    ret=devm_request_irq(dev,irq->start,timer3_interrupt,IRQF_TIMER,"timer3",NULL);
#endif
#if 1
    //timer_debug;  

    base->TCNTB3=0xfff;
        // init tranfer and start timer
    base->TCON=1<<17;
    base->TCON=0b1001<<16;
    base->TINT_CSTAT=0x108;

    timer_debug;
#endif

    return 0;
}

static int timer3_remove(struct platform_device *pdev) {  
    printk("%s enter.\n", __func__);
    base->TINT_CSTAT=0x100;
    clk_disable(clk);
    clk_unprepare(clk);

    return 0;   
}

static const struct of_device_id dt_ids[] = {  
        { .compatible = "tiny4412,timer3demo", },  
        {},  
};  

MODULE_DEVICE_TABLE(of, dt_ids);  

static struct platform_driver timer3_driver = {  
    .driver        = {  
         .name    = "timer3",  
         .of_match_table    = of_match_ptr(dt_ids),  
    },  
     .probe         = timer3_probe,  
     .remove        = timer3_remove,  
};  

static int timer_init(void){
    int ret;  

        ret = platform_driver_register(&timer3_driver);  
        if (ret)  
            printk(KERN_ERR "int demo: probe failed: %d\n", ret);  

        return ret;
}

static void timer_exit(void){
    platform_driver_unregister(&timer3_driver);  
}

module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");

效果:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_33160790/article/details/79363586
今日推荐