This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DMTimer Start and Stop Issue (Two DMTimer)

Hello,

Greetings,

I tried to use two DMTimer (TIMER 4 and TIMER 7) on my AM335x Evm,
I configured Timer 4 to generate 180 Microseconds PWM and Timer 7 to100 Microseconds.
when I run both timer continuously, its working well, Timer 4 generates 180 Microseconds pulse width and Timer 7 generates 100 Microseconds pulse width.

Please refer the attached Logic Analyzer Pic1.


However, when I try to start the Timer 7 inside Timer 4 ISR Handler, its not working well, the Timer 7 pulse width is changed,

please refer the attached Logic Analyzer Pic2.

My requirement is, to start the Timer 7 Whenever Timer 4 interrupt occurs. DMTimer Start and Stop then start is not working as expected, please guide me to clear this issue.
Here is my sample source code.

Two_DMTimer_Test.c
#include <linux/module.h>		
#include <linux/kernel.h>		
#include <linux/init.h>			
#include <linux/clk.h>		
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/types.h>			
#include <linux/platform_device.h>
#include <linux/platform_data/dmtimer-omap.h>
#include <linux/sched_clock.h>
#include </opt/work/trunk/BSP/AM335X/Linux/linux-3.14.26-g2489c02/arch/arm/plat-omap/include/plat/counter-32k.h>
#include </opt/work/trunk/BSP/AM335X/Linux/linux-3.14.26-g2489c02/arch/arm/plat-omap/include/plat/dmtimer.h>
#include <linux/gpio.h>

#define DRIVER_NAME "test_dev"
#define TIMER_MAX 0xFFFFFFFF
static struct omap_dm_timer *timer_ptr = NULL;
static struct omap_dm_timer *timer_ptr1 = NULL;
static int32_t timer_irq;
static int32_t timer_irq1;
static uint32_t timer_rate;
u32 irq_count =0;


static irqreturn_t timer_irq_handler( int irq, void * dev_id)
{
	unsigned int retval;

	retval = omap_dm_timer_read_status(timer_ptr);
	if (!retval)
		return IRQ_NONE;

	if (retval & ~OMAP_TIMER_INT_MATCH)
		printk("Unexpected interrupt source: %x\n", retval);

	omap_dm_timer_write_status(timer_ptr,
				OMAP_TIMER_INT_MATCH	|
				OMAP_TIMER_INT_OVERFLOW	|
				OMAP_TIMER_INT_CAPTURE);

	omap_dm_timer_read_status(timer_ptr);
	
	irq_count++;
	omap_dm_timer_start(timer_ptr1);
	if(irq_count > 100)
	{
	
		omap_dm_timer_stop(timer_ptr);		
		omap_dm_timer_stop(timer_ptr1);
		printk("________________DM Timer Count  %d\n", irq_count );
		irq_count =0;
	}

	return IRQ_HANDLED;
}

static irqreturn_t timer_irq_handler1( int irq, void * dev_id)
{
	unsigned int retval;


	retval = omap_dm_timer_read_status(timer_ptr1);
	if (!retval)
		return IRQ_NONE;

	if (retval & ~OMAP_TIMER_INT_MATCH)
		printk("Unexpected interrupt source: %x\n", retval);

	omap_dm_timer_write_status(timer_ptr1,
				OMAP_TIMER_INT_MATCH	|
				OMAP_TIMER_INT_OVERFLOW	|
				OMAP_TIMER_INT_CAPTURE);

	omap_dm_timer_read_status(timer_ptr1);

	omap_dm_timer_stop(timer_ptr1);

	return IRQ_HANDLED;
}

static int init_timing(struct omap_dm_timer *timer,unsigned int on_time,unsigned int off_time)
{
		
	u32 load, match;
	load = timer_rate * (on_time + off_time) / 1000;
	match = timer_rate * on_time / 1000;
	
	omap_dm_timer_set_load(timer, 1, -load);
	omap_dm_timer_set_match(timer, 1, -match); 
	omap_dm_timer_write_counter(timer, - 2);
	omap_dm_timer_set_pwm(timer, 1, 1, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
	omap_dm_timer_set_int_enable(timer, OMAP_TIMER_INT_MATCH);
	return 0;
}

static int test_module_init(struct platform_device *pdev)
{
	int ret = 1,i;
	struct clk *timer_clk;  

	struct device_node *timer_device_node; 
	struct device_node *timer_device_node1; 
	printk(KERN_INFO " INIT  DM Timer\n");   
	
	timer_device_node = of_parse_phandle(pdev->dev.of_node, "DMtimer", 0); 

	timer_ptr = omap_dm_timer_request_by_node(timer_device_node);
	if(timer_ptr == NULL)
	{
		/* no timers available */
		printk(KERN_INFO "Can't request specified DM Timer 4\n");
		return -1;
	}   

	timer_device_node1 = of_parse_phandle(pdev->dev.of_node, "DMtimer1", 0); 

	timer_ptr1 = omap_dm_timer_request_by_node(timer_device_node1);
	if(timer_ptr1 == NULL)
	{
		/* no timers available */
		printk(KERN_INFO "Can't request specified DM Timer 7\n");
		return -1;
	}   


	/* Set the Clock source to the System Clock */
	ret = omap_dm_timer_set_source(timer_ptr, OMAP_TIMER_SRC_SYS_CLK);
	ret = omap_dm_timer_set_source(timer_ptr1, OMAP_TIMER_SRC_SYS_CLK);
	// set prescalar to 1:1
	//omap_dm_timer_set_prescaler(timer_ptr, 0); 

	omap_dm_timer_enable(timer_ptr);
	omap_dm_timer_enable(timer_ptr1);

	/* Determine what IRQ the timer triggers */
	timer_irq = omap_dm_timer_get_irq(timer_ptr);
	timer_irq1 = omap_dm_timer_get_irq(timer_ptr1);
	/* Setup the IRQ handler */
	ret = request_irq(timer_irq, timer_irq_handler, IRQF_TIMER, "DMTimer 4", NULL);
	/* Setup the IRQ handler */
	ret = request_irq(timer_irq1, timer_irq_handler1, IRQF_TIMER, "DMTimer 7", NULL);

	/* Get the Clock rate in Hz */
	timer_clk = omap_dm_timer_get_fclk(timer_ptr);
	timer_rate = clk_get_rate(timer_clk)/1000;

	init_timing(timer_ptr,180,5);  //180 microsec
	init_timing(timer_ptr1,100,5);   // 100 microsec


	/* Start the timer */
	omap_dm_timer_start(timer_ptr);
	//omap_dm_timer_start(timer_ptr1);
	
	return 0;
}


static int test_module_exit(struct platform_device *pdev)
{
	int ret = 1;

	/* stop the timer */
	ret = omap_dm_timer_stop(timer_ptr);
	printk(KERN_INFO "Timer Stop ret = %d\n", ret); 
	ret = omap_dm_timer_stop(timer_ptr1);
	printk(KERN_INFO "Timer Stop ret = %d\n", ret); 
	/* Release the IRQ handler */
	free_irq(timer_irq, NULL);
	free_irq(timer_irq1, NULL);
	printk(KERN_INFO "Free IRQ Done\n");    

	/* Release the timer */
	ret = omap_dm_timer_free(timer_ptr);
	printk(KERN_INFO "Timer Free ret = %d\n", ret); 
	ret = omap_dm_timer_free(timer_ptr1);
	printk(KERN_INFO "Timer Free ret = %d\n", ret); 

	return 0;
}

static const struct of_device_id test_module_timer_of_match[] = {
    { .compatible   ="ti,am335x-timer" },
    {},
};

static struct platform_driver test_module_timer_driver = {
    .driver = {
        .name   = DRIVER_NAME,
        .of_match_table = test_module_timer_of_match,
    },
    .probe  = test_module_init,
    .remove = test_module_exit,
};

module_platform_driver(test_module_timer_driver);

MODULE_LICENSE("GPL");            
MODULE_AUTHOR("Sahaya Darcius");    
MODULE_DESCRIPTION("Sample DMTimer Test Driver");  
MODULE_VERSION("0.1"); 

Thank you.

  • Hi,

    I will forward this to the software team, but please note that feedback may be delayed due to the holidays.
  • Thank you and i am using SDK 8.

  • Hi Peter,

    I think, that somehow in the code timer_ptr & timer_ptr1 mess up & point to the same settings (as I see in the Logic Analyzer Pic2 the period of both timers is approx 180us).  

    Could you specify in Logic Analyzer Pic1, when you enable both timer4 & timer7 to run continuously, do you call timer7 inside timer4 ISR handler, or this call to enable timer7 is commented?

    Is it possible to track register values of timer7 and timer4, when you start timer7 inside timer4 ISR handler? For this you can dump the register values either within dmtimer.c omap_dm_timer_enable(), or by using Jtag (if you have one).  I recommend reading the registers within the kernel driver. 

    Also can you share/verify your dts nodes for timer4 & timer7? You should have in mind that when you add these timers within your dts, dmtimer.c initializes both timers, and by calling them in your custom driver you overwrite the initial settings. 

    Best Regards, 
    Yordan

  •  

    Hi Yordan,

       Thank you for your response,

    When you enable both timer4 & timer7 to run continuously, do you call timer7 inside timer4 ISR handler, or this call to enable timer7 is commented?

       Enable both timers inside the module init function, yes the commented enable timer 7 is correct,

    Is it possible to track register values of timer7 and timer4, when you start timer7 inside timer4 ISR handler?

           I keep track the Load Register, Match Register values, nothing wrong with the register values. It seems like perfect.

    My DTS File settings

    am33xx-test_module_timer {

            compatible = "ti,am335x-timer ";

            status = "okay";

            DMtimer = <&timer4>;

            DMtimer1 = <&timer7>;

     

                };

     

    Timer4_pins_default: Timer4_pins_default {

                   pinctrl-single,pins = <

                           0x1b0 ( PIN_OUTPUT_PULLDOWN | MUX_MODE2 ) /* (A15) xdma_event_intr0.timer4 */

                   >;

            };

     

            /* Optional sleep pin settings. Must manually enter values in the below skeleton. */

            Timer4_pins_sleep: Timer4_pins_sleep {

                   pinctrl-single,pins = <

                           0x1b0 (PIN_OUTPUT_PULLUP | MUX_MODE2 ) /* (A15) xdma_event_intr0.timer4 */

                   >;

                };

    Timer7_pins_default: Timer7_pins_default {

                   pinctrl-single,pins = <

                           0x1b4 ( PIN_OUTPUT_PULLDOWN | MUX_MODE4 ) /* (D14) xdma_event_intr1.timer7 */

                   >;

            };

     

            /* Optional sleep pin settings. Must manually enter values in the below skeleton. */

            Timer7_pins_sleep: Timer7_pins_sleep {

                   pinctrl-single,pins = <

                           0x1b4 (PIN_OUTPUT_PULLUP | MUX_MODE4 ) /* (D14) xdma_event_intr1.timer7 */

                   >;

                };

     

    &timer4 {

            status = "okay";

            pinctrl-names = "default";

            pinctrl-0 = <&timer4_pins_default>;

            pinctrl-1 = <& timer4_pins_sleep>;

            status = "okay";

     

                };

    &timer7 {

            status = "okay";

            pinctrl-names = "default";

            pinctrl-0 = <& timer7_pins_default>;

            pinctrl-1 = <& timer7_pins_sleep>;

            status = "okay";

                };

    In my current source code, Timer7 is used to generate 100 microseconds  pulse with the match value is about 5 microseconds,

    I.e the load register values is 100microsec =  0xFFFFF69F

    Match value for 5 microsec  (100 + 5)=  FFFFF717

    If I change the  match value (95% of load value )  =  0xFFFFFF87 this time I got the expected output however  some random delay occurs.

    Do we need to give long match values to get the expected pulse?

    Here is my original usage,

    Actually, I use Timer4 for motor control, its continuous running, i.e I set the motor timer register settings only one time.

    I use Timer7 for TPH Strobe control, here I used to set the register values inside the Timer7 ISR, every time the Timer7 load and match register values are changing. I keep track the timer7 register values, it seems perfect,  but sometimes the current register setting  has not taken place and previous register setting were used, it seems like writing to load and match register takes some delay.

    For example, I  want to generate the TPH pulse, 30, 60,70 100(in microsecs) repeatedly , but I got 30,60,70,100,30,60,70,100,30,30,60,70,100, 30.60,70,100 like that, if you look at it , 30 is repeated twice.

    Why this is happening? Is updating load and match register setting inside ISR will cause some delay?

    Please guide me.

    Thank you,

    With Regards,

    Sahaya  P

  • Hi,

    As you describe the problem, it seems that indeed, you need a longer match values to get the pulses right..

    Have you tried increasing the timers clock frequecy? As I see in the kernel & your custom driver you set them to work with:
    OMAP_TIMER_SRC_SYS_CLK
    which in dmtimer.c is mapped to
    timer_sys_ck
    and then in clk-33xx.c & ti.h & am335x-clocks.dtsi it is mapped to:
    sys_clkin_ck

    Can you try setting the clock to its maximum: 25Mhz & see if you'll get improvement in your use case?

    Best Regards,
    Yordan
  • Just a suggestion (I'm not a software expert), but could this have something to do with posted read/write (AM335X TRM Rev. L sections 20.1.3.9 to 20.1.3.11)?
  • Hi Yordan,

        Thank you for your suggestion, i changed the clock frequency from 24MHz to 25MHz in the am335x-clocks.dtsi file and i checked the clock rate inside my custom driver its 25MHz, however the result is still same no improvement, same random delay, i tried direct memory map method as well but still the result is same, 

    Please refer the follow picture,

    if you look into the picture, you can notify the pulse width 65 repeated 2 times.

    Here is a my sample code - direct memory mapped 

    DMTimer_Test_latest.c
    #include <linux/module.h>		
    #include <linux/kernel.h>	
    #include <linux/kthread.h> 	
    #include <linux/init.h>			
    #include <linux/clk.h>		
    #include <linux/irq.h>
    #include <linux/interrupt.h>
    #include <asm/io.h>
    #include <linux/types.h>			
    #include <linux/platform_device.h>
    #include <linux/platform_data/dmtimer-omap.h>
    #include <linux/sched_clock.h>
    #include </opt/work/trunk/BSP/AM335X/Linux/linux-3.14.26-g2489c02/arch/arm/plat-omap/include/plat/counter-32k.h>
    #include </opt/work/trunk/BSP/AM335X/Linux/linux-3.14.26-g2489c02/arch/arm/plat-omap/include/plat/dmtimer.h>
    #include <linux/gpio.h>
    #include "DMTimer.h"
    
    
    #define DRIVER_NAME "test_dev"
    #define TIMER_MAX 0xFFFFFFFF
    static struct omap_dm_timer *timer_ptr = NULL;
    static struct omap_dm_timer *timer_ptr1 = NULL;
    static int32_t timer_irq;
    static int32_t timer_irq1;
    static uint32_t timer_rate;
    u32 irq_count =0;
    
    /*Memory Mapped handlers*/
    volatile void __iomem *dmtimer_4_addr = NULL;
    volatile void __iomem *dmtimer_7_addr = NULL;
    volatile void __iomem *sys_int_addr = NULL;
    volatile void __iomem *cm_per_addr;
    
    static unsigned long int TPH_PW_1=0;
    static unsigned long int TPH_PW_2=0;
    static unsigned long int TPH_PW_3=0;
    static unsigned long int TPH_PW_4=0;
    
    static void DMTimer4SetUp(unsigned int ms,unsigned int pd)
    {
    	
    	unsigned long int mot_counter_val;
    	unsigned long int mot_match_val;
    	unsigned int tph_pulse_total_width;
    	
    	mot_counter_val = (TIMER_MAX - (25 * ms));
    	mot_match_val = mot_counter_val + (3 * 25);
    	printk("mot_counter_val = %x mot_match_val = %x\n",mot_counter_val,mot_match_val);
    	
    	tph_pulse_total_width = ((2 * ms * pd)/100);
    	
    	TPH_PW_1 = ((tph_pulse_total_width * 40)/100);
    	TPH_PW_1 = (TIMER_MAX - (25 * TPH_PW_1));
    	
    	TPH_PW_2 = ((tph_pulse_total_width * 35)/100);
    	TPH_PW_2 = (TIMER_MAX - (25 * TPH_PW_2));
    	
    	TPH_PW_3 = ((tph_pulse_total_width * 25)/100);
    	TPH_PW_3 = (TIMER_MAX - (25 * TPH_PW_3));
    	
    	TPH_PW_4 = ((2 * ms) - tph_pulse_total_width);
    	TPH_PW_4 = (TIMER_MAX - (25 * TPH_PW_4));
    	
    	DMTimerPreScalerClkDisable(dmtimer_4_addr);
    	DMTimerReset(dmtimer_4_addr);
    	
    	//DMTimerPWMEnable(dmtimer_4_addr,DMTIMER_PWM_TOGGLE_MODE_NEGATIVE,DMTIMER_PWM_TRIGON_OVRFLW_MATCH);
    	*((volatile unsigned int* )(dmtimer_4_addr + DMTIMER_TCLR)) = 0x1800;
    	DMTimerCounterSet(dmtimer_4_addr,mot_counter_val);
    	DMTimerReloadSet(dmtimer_4_addr, mot_counter_val);
    	DMTimerCompareSet(dmtimer_4_addr,TIMER_MAX-75);
    	DMTimerModeConfigure(dmtimer_4_addr,0x00018c2);//DMTIMER_AUTORLD_CMP_ENABLE);//
    	DMTimerPostedModeConfigure(dmtimer_4_addr,DMTIMER_POSTED_ACTIVE);
    	DMTimerIntEnable(dmtimer_4_addr, DMTIMER_INT_MAT_EN_FLAG);
    }
    
    static void DMTimer7SetUp(void)
    {
    	DMTimerPreScalerClkDisable(dmtimer_7_addr);
    	DMTimerReset(dmtimer_7_addr);
    	DMTimerPWMEnable(dmtimer_7_addr,DMTIMER_PWM_TOGGLE_MODE_NEGATIVE,DMTIMER_PWM_TRIGON_OVRFLW_MATCH);
    	//*((volatile unsigned int* )(dmtimer_7_addr + DMTIMER_TCLR)) = 0x1800;
    	DMTimerCounterSet(dmtimer_7_addr,TPH_PW_1); //180microsec
    	DMTimerReloadSet(dmtimer_7_addr, TPH_PW_1);
    	DMTimerCompareSet(dmtimer_7_addr,TIMER_MAX-25);	
    	DMTimerModeConfigure(dmtimer_7_addr,DMTIMER_AUTORLD_CMP_ENABLE);//0x00018c2);//
    	DMTimerPostedModeConfigure(dmtimer_7_addr,DMTIMER_POSTED_ACTIVE);
    	DMTimerIntEnable(dmtimer_7_addr, DMTIMER_INT_MAT_EN_FLAG);
    }
    
    
    static irqreturn_t timer_irq_handler( int irq, void * dev_id)
    {
    	DMTimerIntDisable(dmtimer_4_addr, DMTIMER_INT_MAT_EN_FLAG);
    	DMTimerIntStatusGet(dmtimer_4_addr);
    	DMTimerIntStatusClear(dmtimer_4_addr, DMTIMER_INT_MAT_IT_FLAG);	
    		
    	irq_count++;
    	if(irq_count > 100)
    	{
    		DMTimerPWMDisable(dmtimer_7_addr);
    		DMTimerDisable(dmtimer_7_addr);
    		DMTimerPWMDisable(dmtimer_4_addr);
    		DMTimerDisable(dmtimer_4_addr);	
    		printk("________________DM Timer Count  %d\n", irq_count );
    		irq_count =0;
    		return IRQ_HANDLED;
    	}
    	
    	DMTimerIntEnable(dmtimer_4_addr, DMTIMER_INT_MAT_EN_FLAG);	
    	return IRQ_HANDLED;
    }
    
    static irqreturn_t timer_irq_handler1( int irq, void * dev_id)
    {
    	static int count = -1;
    	
    	DMTimerIntDisable(dmtimer_7_addr, DMTIMER_INT_MAT_EN_FLAG);
    	DMTimerIntStatusGet(dmtimer_7_addr);
    	DMTimerIntStatusClear(dmtimer_7_addr, DMTIMER_INT_MAT_IT_FLAG);	
    		
    	count++;
    	if(count >3) count =0;	
    	if(count  == 0 )
    	{
    		DMTimerReloadSet(dmtimer_7_addr, TPH_PW_1);
    		DMTimerCompareSet(dmtimer_7_addr,TIMER_MAX-25);
    	}
    	else if(count  == 1)
    	{
    		DMTimerReloadSet(dmtimer_7_addr, TPH_PW_2);
    		DMTimerCompareSet(dmtimer_7_addr,TIMER_MAX-25);
    	}
    	else if(count == 2)
    	{
    		DMTimerReloadSet(dmtimer_7_addr, TPH_PW_3);
    		DMTimerCompareSet(dmtimer_7_addr,TIMER_MAX-25);
    			
    	}
    	else if(count == 3)
    	{
    		DMTimerReloadSet(dmtimer_7_addr, TPH_PW_4);
    		DMTimerCompareSet(dmtimer_7_addr,TIMER_MAX-25);
    	}
    	
    	DMTimerIntEnable(dmtimer_7_addr, DMTIMER_INT_MAT_EN_FLAG);	
    	return IRQ_HANDLED;
    }
    
    
    static int test_module_init(struct platform_device *pdev)
    {
    	struct device_node *timer_device_node; 
    	struct device_node *timer_device_node1; 
    	int ret;
    	
    	sys_int_addr = ioremap(SOC_AINTC_REGS,SOC_AINTC_REGS_LEN);
    	printk("sys_int_addr = 0x%x\n",sys_int_addr);
    	
    	cm_per_addr = ioremap(SOC_CM_PER_REGS,SOC_CM_PER_REGS_LEN);
    	printk("cm_per_addre=0x%x\n",cm_per_addr);
    	
    	dmtimer_4_addr = ioremap(SOC_DMTIMER_4_REGS,SOC_DMTIMER_REGS_LEN);
    	printk("dmtimer_4_addr = 0x%x\n",dmtimer_4_addr);
    	
    	
    	dmtimer_7_addr = ioremap(SOC_DMTIMER_7_REGS,SOC_DMTIMER_REGS_LEN);
    	printk("dmtimer_7_addr = 0x%x\n",dmtimer_7_addr); 
    	
    		
    	printk(KERN_INFO " INIT  DM Timer\n");   
    	
    	timer_device_node = of_parse_phandle(pdev->dev.of_node, "DMtimer", 0); 
    
    	timer_ptr = omap_dm_timer_request_by_node(timer_device_node);
    	if(timer_ptr == NULL)
    	{
    		/* no timers available */
    		printk(KERN_INFO "Can't request specified DM Timer 4\n");
    		return -1;
    	}   
    
    	timer_device_node1 = of_parse_phandle(pdev->dev.of_node, "DMtimer1", 0); 
    
    	timer_ptr1 = omap_dm_timer_request_by_node(timer_device_node1);
    	if(timer_ptr1 == NULL)
    	{
    		/* no timers available */
    		printk(KERN_INFO "Can't request specified DM Timer 7\n");
    		return -1;
    	}  
    	
    	DMTimer4ModuleClkConfig(cm_per_addr);
    	timer_irq = omap_dm_timer_get_irq(timer_ptr);
    	
    	DMTimer7ModuleClkConfig(cm_per_addr);
    	timer_irq1 = omap_dm_timer_get_irq(timer_ptr1);
    	
    	request_irq(timer_irq, timer_irq_handler, IRQF_TIMER, "DMTimer 4", NULL);
    	IntPrioritySet(sys_int_addr,SYS_INT_TINT4, 0, AINTC_HOSTINT_ROUTE_IRQ);
    	
    	request_irq(timer_irq1, timer_irq_handler1, IRQF_TIMER, "DMTimer 7", NULL);
    	IntPrioritySet(sys_int_addr,SYS_INT_TINT7, 0, AINTC_HOSTINT_ROUTE_IRQ);
    
    	printk(KERN_INFO "SGTChar: DMTimer7 Request_irq Success\n");
    	DMTimer4SetUp(180,70);
    	DMTimer7SetUp();
    	DMTimerEnable(dmtimer_4_addr);
    	DMTimerEnable(dmtimer_7_addr);	
    	return 0;
    	
    }
    
    static int test_module_exit(struct platform_device *pdev)
    {
    	int ret = 1;
    
    	/* stop the timer */
       	
    	DMTimerDisable(dmtimer_4_addr);
    	free_irq(timer_irq, NULL);
    	
    	DMTimerDisable(dmtimer_7_addr);
    	free_irq(timer_irq1, NULL);
    	printk(KERN_INFO "Free IRQ Done\n");
    
    	/* Release the timer */
    	ret = omap_dm_timer_free(timer_ptr);
    	ret = omap_dm_timer_free(timer_ptr1);
    	printk(KERN_INFO "Timer Free ret = %d\n", ret);
    	
    	iounmap(sys_int_addr );
    	iounmap(cm_per_addr);
    	iounmap(dmtimer_4_addr);
    	iounmap(dmtimer_7_addr);
    	 
    	return 0;
    }
    
    static const struct of_device_id test_module_timer_of_match[] = {
        { .compatible   ="ti,am335x-timer" },
        {},
    };
    
    static struct platform_driver test_module_timer_driver = {
        .driver = {
            .name   = DRIVER_NAME,
            .of_match_table = test_module_timer_of_match,
        },
        .probe  = test_module_init,
        .remove = test_module_exit,
    };
    
    module_platform_driver(test_module_timer_driver);
    
    MODULE_LICENSE("GPL");            
    MODULE_AUTHOR("Sahaya Darcius");    
    MODULE_DESCRIPTION("Sample DMTimer Test Driver");  
    MODULE_VERSION("0.1"); 
    

    We are using 24MHz  crystal, do we need to change it? or its a issue of DMTimer register setting ? 

    Thank you.

    With Regards,

    Sahaya P

  • Hi,

    No I don't think the 24MHz crystal is the problem.

    One more thing came to mind.

    If what you said is correct and your timer register settings are persistent upon timer start/stop & in the ISR.
    Can you try tweaking the values of PTV & PRE upon starting timer7, which is equal to changing the prescaler -> hence the counting rate of the problematic timer7. See Section 20.1.3.6 Timer Counting Rate, Section 20.1.3.5 Pulse-Width Modulation & Section 20.1.3.4 Prescaler Functionality in AM335x TRM.

    Hope this helps.

    Best Regards,
    Yordan
  • Hi  Yordan,

         Thank you for your suggestion,

    I checked the PTV and PRE values its always 0, if you look at my source code (i mean the one attached yesterday), i already disabled Prescaler functionality using DMTimerPreScalerClkDisable() API. Please try to reproduce this issue on your side and guide me to clear this issue.

    Thank you,

    With Regards,

    Sahaya P

  • Hi Yordan,

        After analyzing this issue,I  found out, it's happened due to some random delay while calling the ISR, As far as i know, the ISR should be called immediately whenever the Overflow and Match Interrupt occurs, but in this case, the ISR is not called immediately there was some random delay, its about 60 microseconds,  In my sample source code, used Auto Reload and overflow match mode that’s why, whenever the overflow and match interrupts are occurred the PWM is generated using the current register values, since the ISR is not called immediately the new value is not not taking effect the old values were used for the next PWM.that’s why the 65 microseconds repeated two times,

    How do we fix this random delay issue in calling ISR? In my Sample Source, already set highest priority(0) for the timer 7 ISR,  I think it's very critical one, please guide me!

    Thank you

    With Regards,

    Sahaya P

  • I can try to reproduce the issue and will post what I find.

    Steve K.

  • Hello Steve K,

         Thank you.

    May i know the current status of this issue?, Have you reproduced the issue or not? 

    Thank you,

    With Regards,

    Sahaya P

  • Not yet. But I'm wondering if this is just the Linux scheduler.

    Steve K.

  • Your latest code includes DMTimer.h. Can you attach that?

    Steve K.
  • Hello Steve K,

       Thank you for your response,

    Here is my  DMTimer.h.file

    DMTimer.h

    With Regards,

    Sahaya P

  • Hello Steve K,

       I reproduced this issue with TI Evm board version 1.6 A, SDK 8 BSP,

    What you mean by Linux Scheduler Issue? you mean the CUP Scheduling cause this issue? if so, how do we fix?

    With Regards,

    Sahaya P 

  • Peter, we are still a little confused on how you want the timers to operate. Can you provide a timing diagram of how you want both signals to look?

    Thanks,
    James
  • Hello James,

    I hope you keep track the history of this issue and gone through our sample source code as well, if so, I am pretty sure you will be well aware of our requirement. I am really sorry to say this, This issue has been reported on 22nd of December, 2015 however, we still yet to get a solution from TI, its really embarrassing, due to this issue our current project has been delayed almost 2 months. Hope TI will solve this issue ASAP.

    I will explain our requirements one more time,

    We want to use Two DMTimer, Timer4 for Motor Control and Timer7 for TPH Srtobe Control.

    We want to control the TPH inside the Motor ISR,  for every two TIMER4 Pulse, the TIMER4 ISR starts the TIMER7. The TIMER7 has to generate 4 pulses with different width and stop the Timer7 i.e for every 2 Motor steps 4 TPH pulses should be generated.

    For example,

    1 motor step = 180microseconds

    TPH Pulses = 110, 90,60 and 100 microseconds  

     

    Motor Pulse:  180    ,      180,       180    ,      180,      180    ,      180,

    TPH Pulse   : 110,90,60,100,      110,90,60,100,      110,90,60,100

     

    Please refer to this picture,

     e2e.ti.com/.../MOT_5F00_TPH.jpg

    We want to generate this kind of pulse,

    Hope you understand our requirement, Please let me know if you have any concerns. 

    Thank you,

    With Regards,

    Sahaya P

  • James and I were working with your first driver, Two_DMTimer_Test.c and could re-create the issue. Then we started to modify the code and could not get it to work. So I then went to your other driver, DMTimer_Test_latest.c. It looks like a mix of Linux and StarterWare. That is something we cannot support.

    I suggest you go back to using the dmtimer APIs. Try something like setting up Timer 4 for 180 microseconds and auto-reload. Do not install an interrupt handler. Set up Timer 7 with an interrupt handler. Set it for 110 microseconds. Start both in your driver init routine. Then in the Timer 7 interrupt handler, cycle through the 90, 60, 100 values.

    Steve K.

  • Hello Steve,

    Thank you for your suggestion, however it doesn't change anything, still get the same random delay,

    To make it simple, i used only one timer, TIMER7 and set it as a Auto Reload mode and inside the ISR used the cycle values 110,90,60,100.

    Here is my sample code

    DMTimer7_Test.c
    #include <linux/module.h>		
    #include <linux/kernel.h>		
    #include <linux/init.h>			
    #include <linux/clk.h>		
    #include <linux/irq.h>
    #include <linux/interrupt.h>
    #include <asm/io.h>
    #include <linux/types.h>			
    #include <linux/platform_device.h>
    #include <linux/platform_data/dmtimer-omap.h>
    #include <linux/sched_clock.h>
    #include </opt/work/trunk/BSP/AM335X/Linux/linux-3.14.26-g2489c02/arch/arm/plat-omap/include/plat/counter-32k.h>
    #include </opt/work/trunk/BSP/AM335X/Linux/linux-3.14.26-g2489c02/arch/arm/plat-omap/include/plat/dmtimer.h>
    #include <linux/gpio.h>
    
    #define DRIVER_NAME "test_dev"
    #define TIMER_MAX 0xFFFFFFFF
    
    #define ISRCALLTIME 
    
    static struct omap_dm_timer *timer_ptr = NULL;
    static struct omap_dm_timer *timer_ptr1 = NULL;
    static int32_t timer_irq;
    static int32_t timer_irq1;
    static uint32_t timer_rate;
    u32 irq_count =0;
    unsigned long int TimerEntry[10000];
    static long int  timerEntryindex =0;
    
    static irqreturn_t timer_irq_handler1( int irq, void * dev_id)
    {
    	unsigned int retval,i;
    	static unsigned int count = 0;
    	u32 load, match;
    #ifdef ISRCALLTIME
    	static struct timespec begin, end, diff;
    	getnstimeofday(&end);
    	diff = timespec_sub(end, begin);
    	TimerEntry[timerEntryindex++] = diff.tv_nsec;
    #endif 	
    	count++;
    	retval = omap_dm_timer_read_status(timer_ptr1);
    	if (!retval)
    		return IRQ_NONE;
    
    	if (retval & ~OMAP_TIMER_INT_MATCH)
    		printk("Unexpected interrupt source: %x\n", retval);
    
    	omap_dm_timer_write_status(timer_ptr1,
    				OMAP_TIMER_INT_MATCH	|
    				OMAP_TIMER_INT_OVERFLOW	|
    				OMAP_TIMER_INT_CAPTURE);
    	if(count > 4) count =1;
    	if(count == 1)
    	{
    		load = timer_rate * (110) / 1000;
    		omap_dm_timer_set_load(timer_ptr1, 1, -load);
    		omap_dm_timer_set_match(timer_ptr1, 1, TIMER_MAX -72); 	
    	}
    	else if(count == 2)
    	{
    		load = timer_rate * (90) / 1000;
    		omap_dm_timer_set_load(timer_ptr1, 1, -load);
    		omap_dm_timer_set_match(timer_ptr1, 1, TIMER_MAX -72); 	
    	}
    	else if(count == 3)
    	{
    		load = timer_rate * (60) / 1000;
    		omap_dm_timer_set_load(timer_ptr1, 1, -load);
    		omap_dm_timer_set_match(timer_ptr1, 1, TIMER_MAX -72); 	
    	}
    	if(count == 4)
    	{
    		load = timer_rate * (100) / 1000;
    		omap_dm_timer_set_load(timer_ptr1, 1, -load);
    		omap_dm_timer_set_match(timer_ptr1, 1, TIMER_MAX -72); 	
    	}
    	
    	irq_count++;
    	
        if(irq_count > 500)
    	{
    	
    		omap_dm_timer_stop(timer_ptr1);
    		printk("________________DM Timer Count  %d\n", irq_count );
    #ifdef ISRCALLTIME
    		for(i =0;i<timerEntryindex;i++)
    		{
    			printk("ISR_Entry_Time[%3d] = %3lu  Microsec\n", i,TimerEntry[i]/1000 );
    		}
    #endif
    		irq_count =0;
    		return IRQ_HANDLED;
    	}
    	
    	omap_dm_timer_read_status(timer_ptr1);
    #ifdef ISRCALLTIME
    	getnstimeofday(&begin);	
    #endif
    
    	return IRQ_HANDLED;
    }
    
    static int init_timing(struct omap_dm_timer *timer,unsigned int on_time,unsigned int off_time)
    {
    		
    	u32 load, match;
    	load = timer_rate * (on_time ) / 1000;
    	match = timer_rate * (off_time) / 1000;
    	
    	omap_dm_timer_set_load(timer, 1, -load);
    	omap_dm_timer_set_match(timer, 1, TIMER_MAX -72); 
    	omap_dm_timer_set_pwm(timer, 1, 1, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
    	omap_dm_timer_set_int_enable(timer, OMAP_TIMER_INT_MATCH);
    	return 0;
    }
    
    static int test_module_init(struct platform_device *pdev)
    {
    	int ret = 1,i;
    	struct clk *timer_clk;  
    
    	struct device_node *timer_device_node; 
    	struct device_node *timer_device_node1; 
    	printk(KERN_INFO " INIT  DM Timer\n");   
    
    	timer_device_node1 = of_parse_phandle(pdev->dev.of_node, "DMtimer1", 0); 
    
    	timer_ptr1 = omap_dm_timer_request_by_node(timer_device_node1);
    	if(timer_ptr1 == NULL)
    	{
    		/* no timers available */
    		printk(KERN_INFO "Can't request specified DM Timer 7\n");
    		return -1;
    	}   
    	/* Set the Clock source to the System Clock */
    	ret = omap_dm_timer_set_source(timer_ptr1, OMAP_TIMER_SRC_SYS_CLK);
    	omap_dm_timer_enable(timer_ptr1);
    
    	/* Determine what IRQ the timer triggers */
    	timer_irq1 = omap_dm_timer_get_irq(timer_ptr1);
    	
    	/* Setup the IRQ handler */
    	ret = request_irq(timer_irq1, timer_irq_handler1, IRQF_TIMER, "DMTimer 7", NULL);
    
    	/* Get the Clock rate in Hz */
    	timer_clk = omap_dm_timer_get_fclk(timer_ptr1);
    	timer_rate = clk_get_rate(timer_clk)/1000;
    	printk(KERN_INFO "timer_rate = %d\n",timer_rate);
    	init_timing(timer_ptr1,110,3);   // 100 microsec
    
    	/* Start the timer */	
    	omap_dm_timer_start(timer_ptr1);
    	
    	return 0;
    }
    
    
    static int test_module_exit(struct platform_device *pdev)
    {
    	int ret = 1;
    
    	ret = omap_dm_timer_stop(timer_ptr1);
    	printk(KERN_INFO "Timer Stop ret = %d\n", ret); 
    	/* Release the IRQ handler */
    	free_irq(timer_irq1, NULL);
    	printk(KERN_INFO "Free IRQ Done\n");    
    
    	/* Release the timer */
    	ret = omap_dm_timer_free(timer_ptr1);
    	printk(KERN_INFO "Timer Free ret = %d\n", ret); 
    
    	return 0;
    }
    
    static const struct of_device_id test_module_timer_of_match[] = {
        { .compatible   ="ti,am335x-timer" },
        {},
    };
    
    static struct platform_driver test_module_timer_driver = {
        .driver = {
            .name   = DRIVER_NAME,
            .of_match_table = test_module_timer_of_match,
        },
        .probe  = test_module_init,
        .remove = test_module_exit,
    };
    
    module_platform_driver(test_module_timer_driver);
    
    MODULE_LICENSE("GPL");            
    MODULE_AUTHOR("Sahaya Darcius");    
    MODULE_DESCRIPTION("Sample DMTimer Test Driver");  
    MODULE_VERSION("0.1"); 
    

    Here is the Logical Analyzer Output

    In my Opinion it's happened due to some random delay while calling the ISR, i already mention this on my old post( Jan 25,2016), whenever the Overflow and Match Interrupt occur, sometimes the ISR is not called immediately there was some random delay, its about 60 microseconds (check the M3 to M4 on the above picture it suppose to be 110 but its 168 ).

    Here is my capture for the ISR Entry Timing,

    If you look at the above picture, you can clearly say that sometimes there is a delay in calling ISR. In my attached source code i used to a macro ISRCALLTIME to find the Entry Time.

    I have  some concerns over your previous post as well, 

      Two_DMTimer_Test.c and could re-create the issue. Then we started to modify the code and could not get it to work. 

    In my understanding, you successfully recreate this issue on your side correct? then you modified my source but you fail to get the expected output correct? i am bit confused, please clear me. 

    Try something like setting up Timer 4 for 180 microseconds and auto-reload. Do not install an interrupt handler.

    Here, you mean we should not use Timer4 ISR, if so, we need to use only Timer7 ISR? in my previous post i clearly mention, we want to control the TPH Strobe Control(Timer7) inside the Motor ISR, not only TPH Control but also some other functions also we need to control whenever Timer4(Motor) Interrupt occurs, I am worried, whether you fully understand our requirements or not, please clear me.

    I strongly believe this issue is related to ISR Calling  Timing, correct me if i am wrong.

    I hope you guys are expert in DMTimer Driver development, If you think, my current source is not correct, then please write a sample code according to our requirement and send it to me. i will debug here and update you.

    Thank you,

    With Regards,

    Sahaya P