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.

DM Timer Beaglebone Black questions

I'm the process of investigating using one of the DM Timers on the Beaglebone Black to generate a signal for a specified period. I'm currently using TI's Linux SDK v.8.

I found this example code for setting up  a DM Timer and having it generate an interrupt every 3 seconds.

Unfortunately I'm not seeing my IRQ handler being called after I install my module, and to confirm /proc/interrupts indicates that no interrupts have occurred.

Is there something else I need to do to use the DM Timer, since this example code was written for an older 2.6 kernel?

Also how can I specify the specific DM Timer I want? The function omap_dm_timer_request_by_id no longer functions since with SDK v.8 the device tree is now being used. I'm assuming I need to make some changes to my device tree, but I'm sure what changes to make.

Ideally I want to use DM Timer 4, to set the I/O for a fixed time of 100 to 400 microseconds, and to get an interrupt when this is complete.

Thanks,

Sean Moffatt

  • Hi Sean,

    I will forward this to the SW team.

  • I have been able to get the DM Timer module to generate a signal change for an exact time period. I've included my code below and changes to my device tree.

    C Code

    #include <linux/module.h>		/* Needed by all Linux Kernel Modules */
    #include <linux/platform_device.h>	/* Needed to query the plaform configuration for addresses, interrupts,etc */
    #include <linux/kernel.h>		/* Needed for KERN_INFO */
    #include <linux/init.h>			/* Needed for the module_ macros */
    
    /* ARM DM Timer Header */
    #include <plat/dmtimer.h>
    
    /* Needed for clk functions */
    #include <linux/clk.h>	
    
    /* Interrupt Header */
    #include <linux/interrupt.h>
    
    #define DRIVER_AUTHOR 	"Sean Moffatt"
    #define DRIVER_DESC	"Test DM Timer"		
    #define DRIVER_NAME	"test_module_timer"
    
    static struct omap_dm_timer *timer_ptr = NULL;
    
    static int32_t timer_irq;
    static uint32_t timer_rate;
    
    
    static irqreturn_t timer_irq_handler( int irq, void * dev_id)
    {
    	int status = 0;
    
    	/* Read the current Status */
    	status = omap_dm_timer_read_status(timer_ptr);
    
    	/* Clear the timer interrupt */
    	if (status == OMAP_TIMER_INT_MATCH)
    	{
    		omap_dm_timer_write_status(timer_ptr,  OMAP_TIMER_INT_MATCH);
    	}	
    
    	/* Stop the Timer */
    	omap_dm_timer_stop(timer_ptr);
    
    	/* Indicate the Interrupt was handled */
    	return IRQ_HANDLED;
    }
    
    static int test_module_init(struct platform_device *pdev)
    {
    	int ret = 1;
    	struct clk *timer_clk;	
    
    	/* Need to request OMAP_TIMER_HAS_PWM to ensure we have an I/O Pin to toggle */
    	/* Still need to determine how to request a specific Timer*/
    	timer_ptr = omap_dm_timer_request_by_cap(OMAP_TIMER_HAS_PWM);		
    	if(timer_ptr == NULL){
    		/* no timers available */
    		printk(KERN_INFO "No more DM timers available!!\n");
    		return -1;
    	}
    
    	/* Set the Clock source to the System Clock */
    	ret = omap_dm_timer_set_source(timer_ptr, OMAP_TIMER_SRC_SYS_CLK);
    
    	/* Determine what IRQ the timer triggers */
    	timer_irq = omap_dm_timer_get_irq(timer_ptr);
    
    	/* Setup the IRQ handler */
    	ret = request_irq(timer_irq, timer_irq_handler, IRQF_TIMER, DRIVER_NAME, NULL);
    
    	/* Setup the timer to trigger the IRQ on the match event */
    	omap_dm_timer_set_int_enable(timer_ptr, OMAP_TIMER_INT_MATCH);
    	
    	/* Get the Clock rate in Hz */
    	timer_clk = omap_dm_timer_get_fclk(timer_ptr);
    	timer_rate = clk_get_rate(timer_clk);
    	
    	/* Setup the Output for the Timer pin */
    	/* Setup to toggle on the overflow and the compare match event */	
    	omap_dm_timer_set_pwm(timer_ptr, 0, 1, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
    
    	/* Enable the Timer */
    	/* Needs to be done before we can write to the counter */
    	omap_dm_timer_enable(timer_ptr);
    
    	/* Set the initial Count */
    	/* According to section 20.1.3.5 Pulse-Width Modulation, an overflow or match be used to toggle when a compare condition occurs*/
    	/* Therefore it we will trigger the overflow event almost immediately to ensure our toggle will be generated quickly */
    	omap_dm_timer_write_counter(timer_ptr, (0xFFFFFFFF - 5));
    
    	/* Setup the Load Register */
    	/* Setup as autoload to set the the counter back to 0 on an overflow */
    	omap_dm_timer_set_load(timer_ptr, 1, 0);
    
    	/* Set the the compare register to 1 second */
    	/* This will ensure the signal toggle of 1 second after the overflow event*/
    	omap_dm_timer_set_match(timer_ptr, 1, timer_rate);
    
    	/* Start the timer */
    	ret = omap_dm_timer_start(timer_ptr);
    	printk(KERN_INFO "Timer Start ret %d\n", ret);
    
    	/* 
    	* A non 0 returns means the call to init_modules failed; module can't be loaded
    	*/
    	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);	
    
    	/* Release the IRQ handler */
    	free_irq(timer_irq, 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);	
    
    	return 0;
    }
    
    
    static const struct of_device_id test_module_timer_of_match[] = {
    	{ .compatible	= "test,am33xx-test_module_timer" },
    	{},
    };
    
    static struct platform_driver test_module_timer_driver = {
    	.driver	= {
    		.name	= DRIVER_NAME,
    		.owner	= THIS_MODULE,
    		.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(DRIVER_AUTHOR);		/* Who wrote this module */
    MODULE_DESCRIPTION(DRIVER_DESC);	/* What is this module */

    Device Tree

    In the &am33x_pinmux{

    timer4_pins: timer4_pins {
    pinctrl-single,pins = <
    0x090 (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* TIMER4 P8_07 */
    >;
    };

    };

    am33xx-test_module_timer {
    compatible = "test,am33xx-test_module_timer";
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&timer4_pins>;
    };

    The only question I still have is how do I ensure my driver gets a specific DM Timer when call the omap_dm_timer driver? I assume this is some setting in the device tree.

    Thanks,

    Sean

  • I have determined how to request a specific timer, I just had to add the exact timer's device node to my device tree, and then parse to retrieve it via of_parse_phandle then call omap_dm_timer_request_by_node instead.

     Below is my code for my Kernel Module. Following it is the change I made to the device tree.

    #include <linux/module.h>     /* Needed by all Linux Kernel Modules */
    #include <linux/platform_device.h>    /* Needed to query the plaform configuration for addresses, interrupts,etc */
    #include <linux/kernel.h>     /* Needed for KERN_INFO */
    #include <linux/init.h>           /* Needed for the module_ macros */
    
    /* ARM DM Timer Header */
    #include <plat/dmtimer.h>
    
    /* Needed for clk functions */
    #include <linux/clk.h>    
    
    /* Interrupt Header */
    #include <linux/interrupt.h>
    
    #define DRIVER_AUTHOR   "Sean Moffatt"
    #define DRIVER_DESC "Test DM Timer"     
    #define DRIVER_NAME "test_module_timer"
    
    static struct omap_dm_timer *timer_ptr = NULL;
    
    static int32_t timer_irq;
    static uint32_t timer_rate;
    
    
    static irqreturn_t timer_irq_handler( int irq, void * dev_id)
    {
        int status = 0;
    
        /* Read the current Status */
        status = omap_dm_timer_read_status(timer_ptr);
    
        /* Clear the timer interrupt */
        if (status == OMAP_TIMER_INT_MATCH)
        {
            omap_dm_timer_write_status(timer_ptr,  OMAP_TIMER_INT_MATCH);
        }   
    
        /* Stop the Timer */
        omap_dm_timer_stop(timer_ptr);
    
        /* Indicate the Interrupt was handled */
        return IRQ_HANDLED;
    }
    
    static int test_module_init(struct platform_device *pdev)
    {
        int ret = 1;
        struct clk *timer_clk;  
    
        struct device_node *timer_device_node; 
    	
       /* Attempt to request a timer based on the device node */
       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\n");
    	return -1;
        }	
    
        /* Set the Clock source to the System Clock */
        ret = omap_dm_timer_set_source(timer_ptr, OMAP_TIMER_SRC_SYS_CLK);
    
        /* Determine what IRQ the timer triggers */
        timer_irq = omap_dm_timer_get_irq(timer_ptr);
    
        /* Setup the IRQ handler */
        ret = request_irq(timer_irq, timer_irq_handler, IRQF_TIMER, DRIVER_NAME, NULL);
    
        /* Setup the timer to trigger the IRQ on the match event */
        omap_dm_timer_set_int_enable(timer_ptr, OMAP_TIMER_INT_MATCH);
        
        /* Get the Clock rate in Hz */
        timer_clk = omap_dm_timer_get_fclk(timer_ptr);
        timer_rate = clk_get_rate(timer_clk);
        
        /* Setup the Output for the Timer pin */
        /* Setup to toggle on the overflow and the compare match event */   
        omap_dm_timer_set_pwm(timer_ptr, 0, 1, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
    
        /* Enable the Timer */
        /* Needs to be done before we can write to the counter */
        omap_dm_timer_enable(timer_ptr);
    
        /* Set the initial Count */
        /* According to section 20.1.3.5 Pulse-Width Modulation, an overflow or match be used to toggle when a compare condition occurs*/
        /* Therefore it we will trigger the overflow event almost immediately to ensure our toggle will be generated quickly */
        omap_dm_timer_write_counter(timer_ptr, (0xFFFFFFFF - 5));
    
        /* Setup the Load Register */
        /* Setup as autoload to set the the counter back to 0 on an overflow */
        omap_dm_timer_set_load(timer_ptr, 1, 0);
    
        /* Set the the compare register to 1 second */
        /* This will ensure the signal toggle of 1 second after the overflow event*/
        omap_dm_timer_set_match(timer_ptr, 1, timer_rate);
    
        /* Start the timer */
        ret = omap_dm_timer_start(timer_ptr);
        printk(KERN_INFO "Timer Start ret %d\n", ret);
    
        /* 
        * A non 0 returns means the call to init_modules failed; module can't be loaded
        */
        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); 
    
        /* Release the IRQ handler */
        free_irq(timer_irq, 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); 
    
        return 0;
    }
    
    
    static const struct of_device_id test_module_timer_of_match[] = {
        { .compatible   = "test,am33xx-test_module_timer" },
        {},
    };
    
    static struct platform_driver test_module_timer_driver = {
        .driver = {
            .name   = DRIVER_NAME,
            .owner  = THIS_MODULE,
            .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(DRIVER_AUTHOR);       /* Who wrote this module */
    MODULE_DESCRIPTION(DRIVER_DESC);    /* What is this module */

    Device Tree

    In the &am33x_pinmux{

    timer4_pins: timer4_pins {
    pinctrl-single,pins = <
    0x090 (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* TIMER4 P8_07 */ 
    >;
    };

    };

    am33xx-test_module_timer {
    compatible = "test,am33xx-test_module_timer";
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&timer4_pins>;

    DMtimer = <&timer4>;

    };

  • Hi Sean,

    The above details regarding using the DMTimer are great! I am trying to perform a similar task with slight modifications to the use of the dmtimer itself and your post has been invaluable.

    Do you mind briefly outlining all the steps necessary to get your 'example' running? (I believe I am quite close - but I can't get my pinmux to 'stick' so I'm not sure if I'm missing a build step or what...)

    Thanks!

    Best Regards,

    Mark-

  • Hi Mark,

    I'm running TI SDK v.8 OS on a Beaglebone black.

    I modified the am335x-boneblack.dts in the SDK's board-support/linux-3.14.26-g2489c02/arch/arm/boot/dts/ to add the following to the am33x_pinmux section.

    timer4_pins: timer4_pins {
    		pinctrl-single,pins = <
    			0x090 (PIN_OUTPUT_PULLDOWN | MUX_MODE2)	/* TIMER4  P8_07 */			
    		>;
    	};

    Then the following to the final section.

    am33xx-test_module_timer {
            compatible = "test,am33xx-test_module_timer";
            status = "okay";
            pinctrl-names = "default";
            pinctrl-0 = <&timer4_pins>;
    };

    Then to compile the device tree I called the following in the board-support/linux-3.14.26-g2489c02/ directory.

    export PATH=<TI SDK v.8 Install PATH>/ti-sdk-am335x-evm-08.00.00.00/linux-devkit/sysroots/i686-arago-linux/usr/bin/:$PATH

    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- am335x-boneblack.dtb

    Where <TI SDK v.8 Install PATH> is the location of where the SDK was installed in the system.

    Then I copied  board-support/linux-3.14.26-g2489c02/arch/arm/boot/dts/am335x-boneblack.dtb to the Target's /boot/ directory.

    To compile the code I used the following Makefile.

    MODULES := test_module_timer.o
    
    ARCH := arm
    
    
    ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/arch/arm/plat-omap/include
    
    CROSS_COMPILE := arm-linux-gnueabihf-
    obj-m := $(MODULES)
    
    #Path to the ARM compiled kernel
    ROOTDIR := ../linux-3.14.26-g2489c02
    
    
    
    MAKEARCH := $(MAKE) ARCH=$(ARCH) -WALL CROSS_COMPILE=$(CROSS_COMPILE)
    
    all: modules
    modules:
    	$(MAKEARCH) -C $(ROOTDIR) M=${shell pwd} modules
    
    clean:
    	$(MAKEARCH) -C $(ROOTDIR) M=${shell pwd} clean

    My environment was setup with the test directory containing kernel module source in test_module_timer.c at the same level as the linux source.

    ~/ti-sdk-am335x-evm-08.00.00.00/board-support $ ls
    extra-drivers  linux-3.14.26-g2489c02  prebuilt-images  test  u-boot-2014.07-g7e537bf
    

    In the test directory I called make to compile the kernel module.

    Then on the target I loaded the test_module_timer.ko, and could verify the pinmuxing by the following command.

    cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pinmux-pins | grep -i timer
    pin 36 (44e10890.0): am33xx-test_module_timer.11 (GPIO UNCLAIMED) function timer4_pins group timer4_pins
    

    I can also verify that the Timer interrupt by checking /proc/interrupts.

    Before loading test_module_timer.ko

    cat /proc/interrupts 
               CPU0       
     28:       3025      INTC  12  edma
     30:         13      INTC  14  edma_error
     33:          0      INTC  17  47400000.dma-controller
     34:          0      INTC  18  musb-hdrc.0.auto
     35:          1      INTC  19  musb-hdrc.1.auto
     44:        318      INTC  28  mmc1
     52:          0      INTC  36  tilcdc
     56:          0      INTC  40  4a100000.ethernet
     57:        394      INTC  41  4a100000.ethernet
     58:        174      INTC  42  4a100000.ethernet
     59:          0      INTC  43  4a100000.ethernet
     80:       8874      INTC  64  mmc0
     84:       1688      INTC  68  gp_timer
     86:        212      INTC  70  44e0b000.i2c
     88:        222      INTC  72  OMAP UART0
     91:          0      INTC  75  rtc0
     92:          0      INTC  76  rtc0
     93:          0      INTC  77  wkup_m3
     94:          1      INTC  78  wkup_m3_txev
    125:          0      INTC 109  53100000.sham
    127:          0      INTC 111  48310000.rng
    150:          0  44e07000.gpio   6  mmc0
    Err:          0
    

    After loading test_module_timer.ko

    cat /proc/interrupts 
               CPU0       
     28:       3058      INTC  12  edma
     30:         13      INTC  14  edma_error
     33:          0      INTC  17  47400000.dma-controller
     34:          0      INTC  18  musb-hdrc.0.auto
     35:          1      INTC  19  musb-hdrc.1.auto
     44:        318      INTC  28  mmc1
     52:          0      INTC  36  tilcdc
     56:          0      INTC  40  4a100000.ethernet
     57:        612      INTC  41  4a100000.ethernet
     58:        257      INTC  42  4a100000.ethernet
     59:          0      INTC  43  4a100000.ethernet
     80:       9009      INTC  64  mmc0
     84:       1951      INTC  68  gp_timer
     86:        221      INTC  70  44e0b000.i2c
     88:        222      INTC  72  OMAP UART0
     91:          0      INTC  75  rtc0
     92:          0      INTC  76  rtc0
     93:          0      INTC  77  wkup_m3
     94:          1      INTC  78  wkup_m3_txev
    108:          1      INTC  92  test_module_timer
    125:          0      INTC 109  53100000.sham
    127:          0      INTC 111  48310000.rng
    150:          0  44e07000.gpio   6  mmc0
    Err:          0
    

    Let me know if you need any more information.

    Thanks,

    Sean

  • Hi,
    im trying to implement something very similar to what is described here on a beaglebone black. After I call the omap_dm_timer_start function, my ethernet just goes down. Have you seen anything like this? More info below

    Im using ti-sdk/processor-sdk-linux-03.01.00 branch (db0b54c)

    # [ 4054.089079] omap_hwmod: davinci_mdio: _wait_target_ready failed: -16
    [ 4055.085951] cpsw 4a100000.ethernet eth0: Link is Down
    # uname -a
    Linux beaglebone 4.4.19-34066-g161da15-dirty #3 SMP Mon Jan 2 15:25:20 EST 2017 armv7l GNU/Linux
  • Ive enabled the kernel debug so I have more info now:
    [ 43.440510] ------------[ cut here ]------------
    [ 43.445377] WARNING: CPU: 0 PID: 0 at kernel/locking/mutex.c:872 mutex_trylock+0x1fc/0x21c()
    [ 43.454186] DEBUG_LOCKS_WARN_ON(in_interrupt())
    [ 43.458733] Modules linked in: timer_phc(O) ti_cpsw
    [ 43.464041] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G O 4.4.19-34066-g161da15-dirty #3
    [ 43.473392] Hardware name: Generic AM33XX (Flattened Device Tree)
    [ 43.479778] [<c0017e0c>] (unwind_backtrace) from [<c0014074>] (show_stack+0x10/0x14)
    [ 43.487876] [<c0014074>] (show_stack) from [<c035d2f0>] (dump_stack+0xb0/0xe4)
    [ 43.495427] [<c035d2f0>] (dump_stack) from [<c003e6e0>] (warn_slowpath_common+0x78/0xb4)
    [ 43.503878] [<c003e6e0>] (warn_slowpath_common) from [<c003e74c>] (warn_slowpath_fmt+0x30/0x40)
    [ 43.512964] [<c003e74c>] (warn_slowpath_fmt) from [<c0669060>] (mutex_trylock+0x1fc/0x21c)
    [ 43.521605] [<c0669060>] (mutex_trylock) from [<c04f7ca8>] (clk_prepare_lock+0xc/0xec)
    [ 43.529878] [<c04f7ca8>] (clk_prepare_lock) from [<c04f9dbc>] (clk_core_get_rate+0xc/0x60)
    [ 43.538521] [<c04f9dbc>] (clk_core_get_rate) from [<c0039fb0>] (omap_dm_timer_stop+0x16c/0x184)
    [ 43.547617] [<c0039fb0>] (omap_dm_timer_stop) from [<bf00d0ac>] (timer_irq_handler+0x40/0x74 [timer_phc])
    [ 43.557624] [<bf00d0ac>] (timer_irq_handler [timer_phc]) from [<c00a12b0>] (handle_irq_event_percpu+0x44/0x2f0)
    [ 43.568162] [<c00a12b0>] (handle_irq_event_percpu) from [<c00a1594>] (handle_irq_event+0x38/0x5c)
    [ 43.577432] [<c00a1594>] (handle_irq_event) from [<c00a4618>] (handle_level_irq+0xc0/0x158)
    [ 43.586156] [<c00a4618>] (handle_level_irq) from [<c00a0960>] (generic_handle_irq+0x20/0x34)
    [ 43.594970] [<c00a0960>] (generic_handle_irq) from [<c00a0c40>] (__handle_domain_irq+0x64/0xe0)
    [ 43.604055] [<c00a0c40>] (__handle_domain_irq) from [<c066d8b8>] (__irq_svc+0x58/0x78)
    [ 43.612332] [<c066d8b8>] (__irq_svc) from [<c0010264>] (arch_cpu_idle+0x20/0x3c)
    [ 43.620061] [<c0010264>] (arch_cpu_idle) from [<c0088630>] (cpu_startup_entry+0x2dc/0x364)
    [ 43.628704] [<c0088630>] (cpu_startup_entry) from [<c08d3c48>] (start_kernel+0x378/0x3f0)
    [ 43.637239] ---[ end trace 12555e3e9dfc54bc ]---
    [ 43.644468] cpsw 4a100000.ethernet eth0: Link is Down