Using OMAP4 General-Purpose Timers

28 views
Skip to first unread message

Benjamin B

unread,
Nov 6, 2015, 1:40:31 PM11/6/15
to pandaboard
I am attempting to use one of the OMAP4's general-purpose timers to do the timing for communication over GPIO pins between the PandaBoard ES and a custom chip.  It has some pretty particular timings, and I need a clock speed of at least ~2 MHz.

Right now I'm just trying to set up a clock for the reading portion of communication.  I have a kernel driver which attaches an interrupt request to a general purpose timer and flips the GPIO pin we're using as a clock on the interrupt.  This works well for speeds up to ~60 KHz, but then won't go faster, and the PandaBoard seems to freeze up if I try speeds above 100 KHz.  The code I'm using is below.

(1) Is this the right way to do this?  I need to synchronize manipulation of the GPIO pins with a clock.

(2) Why can't I drive the GPIO pin faster?  Is my kernel module written badly, so the interrupts/flipping the pins is too expensive, or am I not using the clock/timer correctly?

I also am only able to run this kernel module once per boot.  After I load the module, then unload it, if I load it a second time, it complains that it cannot find an available clock.  I cannot load the module again until I reboot.  The clock is released in the exit function, so I am not sure why this is happening.

Thanks for your help.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <plat/dmtimer.h>
#include <linux/types.h>

#include "skywalker.h"

static struct skywalker_data sky_ptr;

#define TIMER_MAX 0xFFFFFFFF

static void timer_handler(void)
{
  /* Reset the timer interrupt status. */
  omap_dm_timer_write_status(timer_ptr, OMAP_TIMER_INT_OVERFLOW);
  omap_dm_timer_read_status(timer_ptr);

  /* Toggle clock pin. */
  gpio_set_value(sky_ptr.pin, sky_ptr.adc_clk_val);
  sky_ptr.adc_clk_val = !sky_ptr.adc_clk_val;
}

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

  return IRQ_HANDLED;
}

static int set_clk_freq(int freq)
{
  // set preload, and autoreload of the timer
  sky_ptr.period = sky_ptr.timer_rate / (4*freq);
  sky_ptr.load = TIMER_MAX+1 - sky_ptr.period;
  omap_dm_timer_set_load(timer_ptr, 1, sky_ptr.load);
  // store the new frequency
  sky_ptr.frequency = freq;

  return 0;
}

static int gpio_setup_pin(uint32_t gpio_number)
{
  int err;

  if (gpio_is_valid(gpio_number)) {
    printk(KERN_INFO "skywalker module: setting up gpio pin %i...",gpio_number);
    err = gpio_request(gpio_number,"skyIRQ");
    if (err) {
      printk(KERN_WARNING "skywalker module: failed to request GPIO %i\n",gpio_number);
      return -1;
    }

    err = gpio_direction_output(gpio_number,0);

    if (err) {
      printk(KERN_WARNING "skywalker module: failed to set GPIO to ouput\n");
      return -1;
    }

    sky_ptr.pin = gpio_number;
  } else {
    printk(KERN_DEBUG "skywalker module: requested GPIO is not valid\n");
    return -1;
  }

  printk(KERN_INFO "skywalker module: setup DONE\n");

  return 0;
}

static int __init skywalker_start(void)
{
  int rv = 0;
  uint32_t gt_rate;

  printk(KERN_INFO "Loading Skywalker Module... \n");

  // request any timer
  timer_ptr = omap_dm_timer_request();

  if (timer_ptr == NULL) {
    // no timers available
    printk(KERN_ERR "skywalker module: No more gp timers available, bailing out\n");
    return -1;
  }

  // set the clock source to the system clock
  rv = omap_dm_timer_set_source(timer_ptr, OMAP_TIMER_SRC_SYS_CLK);
  if (rv) {
    printk(KERN_ERR "skywalker module: could not set source\n");
    return rv;
  }

  // set prescalar to 1:1
  omap_dm_timer_set_prescaler(timer_ptr, 0);

  // figure out what IRQ our timer triggers
  timer_irq = omap_dm_timer_get_irq(timer_ptr);

  // install our IRQ handler for our timer
  rv = request_irq(timer_irq, timer_irq_handler, IRQF_DISABLED | IRQF_TIMER , "skywalker", timer_irq_handler);
  if (rv) {
    printk(KERN_WARNING "skywalker module: request_irq failed (on irq %d), bailing out\n", timer_irq);
    return rv;
  }

  /* Get clock rate. */
  gt_rate = clk_get_rate(omap_dm_timer_get_fclk(timer_ptr));
  sky_ptr.timer_rate = gt_rate;

  /* Set frequency. */
  set_clk_freq(1000);

  /* Set up timer to trigger interrupt on overflow. */
  omap_dm_timer_set_int_enable(timer_ptr, OMAP_TIMER_INT_OVERFLOW);

  omap_dm_timer_start(timer_ptr);

  printk(KERN_INFO "skywalker module: GP Timer initialized and started (%lu Hz, IRQ %d)\n",
      (long unsigned)gt_rate, timer_irq);

  printk(KERN_INFO "skywalker module: IRQ with frequency %u load %u, and period %u\n", sky_ptr.frequency, sky_ptr.load, sky_ptr.period);

  gpio_setup_pin(121);
  sky_ptr.pin = 121;

  sky_ptr.adc_clk_val = 1;

  return 0;
}

static void __exit skywalker_end(void)
{
  printk(KERN_INFO "Exiting skywalker Module. \n");

  omap_dm_timer_stop(timer_ptr);

  free_irq(timer_irq, timer_irq_handler);

  omap_dm_timer_free(timer_ptr);

  gpio_free(sky_ptr.pin);
}

module_init(skywalker_start);
module_exit(skywalker_end);

MODULE_LICENSE("GPL");


Tom Mitchell

unread,
Nov 6, 2015, 5:14:46 PM11/6/15
to panda...@googlegroups.com
Interrupt handlers are expensive... equally important your handler is not the only interrupt
handler in the system. That alone will limit the rate and risk stack overflow in the kernel which 
will look like a frozen system.

Unloading the driver when the stack contains multiple unhandled interrupts may be your
problem. Unloading handlers and drivers is a too common trouble for a list of forgotten
to me reasons.   Look at other drivers to see how they unwind watch dog timers and
more.


--
You received this message because you are subscribed to the Google Groups "pandaboard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pandaboard+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
  T o m    M i t c h e l l

Benjamin B

unread,
Nov 7, 2015, 3:01:17 PM11/7/15
to pandaboard
Thanks, Tom.  I was afraid I was doing something bad with interrupts.  Can you suggest other drivers to look at?  I've had difficulty finding simple examples similar to what I'm trying to do.
...

modyrater

unread,
Nov 7, 2015, 3:36:22 PM11/7/15
to panda...@googlegroups.com

Wow Sounds like Tom explain why Linux isn't a hard real time RTOS

...

Michael Trimarchi

unread,
Nov 8, 2015, 3:47:22 PM11/8/15
to panda...@googlegroups.com

Hi

On Nov 7, 2015 9:36 PM, "modyrater" <mark.la...@gmail.com> wrote:
>
> Wow Sounds like Tom explain why Linux isn't a hard real time RTOS
>
> On Nov 6, 2015 1:40 PM, "Benjamin B" <benjamina...@gmail.com> wrote:
>>
>> I am attempting to use one of the OMAP4's general-purpose timers to do the timing for communication over GPIO pins between the PandaBoard ES and a custom chip.  It has some pretty particular timings, and I need a clock speed of at least ~2 MHz.
>>

Can you use 1-wire controller?

Michael

Reply all
Reply to author
Forward
0 new messages