TIMER REFERENCE
1. Overview¶
The public version provides 3 timers, which can be configured as a single trigger or a cyclic trigger.
They are disabled by default and can decide whether to enable or not.
2. Timer Control¶
2.1. Set Single/Cycle Trigger¶
#define TIMER_ENABLE (0x1) /*Set cycle trigger*/ #define TIMER_TRIG (0x2) /*Set single trigger*/ //Choose one of two WRITE_WORD(reg_base, TIMER_ENABLE|TIMER_INTERRUPT); /*Set cycle trigger*/ WRITE_WORD(reg_base, TIMER_TRIG|TIMER_INTERRUPT); /*Set single trigger*/
2.2. Enable Timer¶
//Set the following 3 timers to 2 seconds, the unit is us.
_set_timer(2000000, REG_BASE_TIMER0); mdelay(50); //Add delay when enable two or more timers together. _set_timer(2000000, REG_BASE_TIMER1); mdelay(50); //Add delay when enable two or more timers together. _set_timer(2000000, REG_BASE_TIMER2);
2.3. Disable Timer¶
_reset_timer(REG_BASE_TIMER0); _reset_timer(REG_BASE_TIMER1); _reset_timer(REG_BASE_TIMER2);
2.4. Interruption¶
irqreturn_t interrupt_handle(int irq,void *dev_id) { printk("====> hit %d \n", irq); return IRQ_HANDLED; }
2.5. Demo¶
#include <linux/module.h> #include <linux/random.h> #include <linux/kthread.h> #include <linux/delay.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> //struct irq_domain #include <linux/of.h> //of_find_compatible_node #include <linux/irq.h> //IRQ_TYPE_LEVEL_HIGH #include <irqs.h> //INT_FIQ_TIMER_0 #define REG_BASE_TIMER0 (0xDE000000+0x1f000000+0x3020*2) #define REG_BASE_TIMER1 (0xDE000000+0x1f000000+0x3040*2) #define REG_BASE_TIMER2 (0xDE000000+0x1f000000+0x3060*2) #define ADDR_TIMER_CTL (0x0<<2) #define TIMER_ENABLE (0x1) /*Set cycle trigger*/ #define TIMER_TRIG (0x2) /*Set single trigger*/ #define TIMER_INTERRUPT (0x100) /*Enable interruption*/ #define ADDR_TIMER_HIT (0x1<<2) #define ADDR_TIMER_MAX_LOW (0x2<<2) #define ADDR_TIMER_MAX_HIGH (0x3<<2) #define ADDR_TIMER_DIV (0x6<<2) #define WRITE_WORD(_reg, _val) {(*((volatile unsigned short*)(_reg))) = (unsigned short)(_val); } static int vIrqNum0=0; static int vIrqNum1=0; static int vIrqNum2=0; irqreturn_t interrupt_handle(int irq,void *dev_id) { printk("====> hit %d \n", irq); return IRQ_HANDLED; } void _set_timer(unsigned int usec, unsigned int reg_base) { unsigned int interval; interval = usec*12; //set timer_clk to 12M for I2m/P3, (mcu_432m/12m)-1=0x23 WRITE_WORD(reg_base+ ADDR_TIMER_DIV, 0x23); WRITE_WORD(reg_base+ ADDR_TIMER_MAX_LOW, (interval &0xffff)); WRITE_WORD(reg_base+ ADDR_TIMER_MAX_HIGH, (interval >>16)); WRITE_WORD(reg_base, 0); WRITE_WORD(reg_base, TIMER_ENABLE|TIMER_INTERRUPT); /*Set cycle trigger*/ //WRITE_WORD(reg_base, TIMER_TRIG|TIMER_INTERRUPT); /*Set single trigger*/ } void _reset_timer(unsigned int reg_base) { WRITE_WORD(reg_base, 0); } static __init int mdrv_init(void) { printk(KERN_ALERT "Timer mdrv_init! \n"); /*timer0*/ { struct device_node *intr_node; struct irq_domain *intr_domain; struct irq_fwspec fwspec; intr_node = of_find_compatible_node(NULL, NULL, "sstar,main-intc"); intr_domain = irq_find_host(intr_node); if(!intr_domain) return -ENXIO; fwspec.param_count = 3; //refer to #interrupt-cells fwspec.param[0] = 0; //GIC_SPI fwspec.param[1] = INT_FIQ_TIMER_0; /* irqs.h */ fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; fwspec.fwnode = of_node_to_fwnode(intr_node); vIrqNum0 = irq_create_fwspec_mapping(&fwspec); if (request_irq(vIrqNum0, interrupt_handle, 0, "timer0", (void *)REG_BASE_TIMER0)) return -EBUSY; } #if 1 /*timer1*/ { struct device_node *intr_node; struct irq_domain *intr_domain; struct irq_fwspec fwspec; intr_node = of_find_compatible_node(NULL, NULL, "sstar,main-intc"); intr_domain = irq_find_host(intr_node); if(!intr_domain) return -ENXIO; fwspec.param_count = 3; //refer to #interrupt-cells fwspec.param[0] = 0; //GIC_SPI fwspec.param[1] = INT_FIQ_TIMER_1; /* irqs.h */ fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; fwspec.fwnode = of_node_to_fwnode(intr_node); vIrqNum1 = irq_create_fwspec_mapping(&fwspec); if (request_irq(vIrqNum1, interrupt_handle, 0, "timer1", (void *)REG_BASE_TIMER1)) return -EBUSY; } #endif #if 2 /*timer2*/ { struct device_node *intr_node; struct irq_domain *intr_domain; struct irq_fwspec fwspec; intr_node = of_find_compatible_node(NULL, NULL, "sstar,main-intc"); intr_domain = irq_find_host(intr_node); if(!intr_domain) return -ENXIO; fwspec.param_count = 3; //refer to #interrupt-cells fwspec.param[0] = 0; //GIC_SPI fwspec.param[1] = INT_FIQ_TIMER_2; /* irqs.h */ fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; fwspec.fwnode = of_node_to_fwnode(intr_node); vIrqNum2 = irq_create_fwspec_mapping(&fwspec); if (request_irq(vIrqNum2, interrupt_handle, 0, "timer2", (void *)REG_BASE_TIMER2)) return -EBUSY; } #endif //以下3个timer定时设为2秒,参数单位为us _set_timer(2000000, REG_BASE_TIMER0); mdelay(50); //Add delay when enable two or more timers together. _set_timer(2000000, REG_BASE_TIMER1); mdelay(50); //Add delay when enable two or more timers together. _set_timer(2000000, REG_BASE_TIMER2); return 0; } static __exit void mdrv_exit(void) { printk(KERN_ALERT " mdrv_exit!\n"); _reset_timer(REG_BASE_TIMER0); _reset_timer(REG_BASE_TIMER1); _reset_timer(REG_BASE_TIMER2); free_irq (vIrqNum0, (void *)REG_BASE_TIMER0); free_irq (vIrqNum1, (void *)REG_BASE_TIMER1); free_irq (vIrqNum2, (void *)REG_BASE_TIMER2); } module_init(mdrv_init); module_exit(mdrv_exit); MODULE_AUTHOR("SSTAR"); MODULE_DESCRIPTION("ms cpu load driver"); MODULE_LICENSE("GPL");
2.6. Makefile¶
KERNEL_PATH = /home/shaojun.ke/work/Pioneer3/kernel MODULE_NAME = $(shell basename ${PWD}) obj-m := $(MODULE_NAME).o $(MODULE_NAME)-y := mdrv_timer.o EXTRA_CFLAGS += -Idrivers/sstar/include EXTRA_CFLAGS += -Idrivers/sstar/include/pioneer3 all: make -C $(KERNEL_PATH) M=$(PWD) modules clean: make -C $(KERNEL_PATH) M=$(PWD) clean