Discussion:
[pandaboard] Using OMAP4 General-Purpose Timers
Benjamin B
2015-11-06 18:40:31 UTC
Permalink
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");
--
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.
Tom Mitchell
2015-11-06 22:14:22 UTC
Permalink
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.
Post by Benjamin B
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");
--
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
For more options, visit https://groups.google.com/d/optout.
--
T o m M i t c h e l l
--
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.
Benjamin B
2015-11-07 20:01:17 UTC
Permalink
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.
Post by Tom Mitchell
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.
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. */
...
--
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.
modyrater
2015-11-07 20:36:17 UTC
Permalink
Wow Sounds like Tom explain why Linux isn't a hard real time RTOS
Post by Benjamin B
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;
...
--
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.
Michael Trimarchi
2015-11-08 20:47:17 UTC
Permalink
Hi
Post by modyrater
Wow Sounds like Tom explain why Linux isn't a hard real time RTOS
Post by Benjamin B
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
Post by modyrater
Post by Benjamin B
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.
Post by modyrater
Post by Benjamin B
(1) Is this the right way to do this? I need to synchronize
manipulation of the GPIO pins with a clock.
Post by modyrater
Post by Benjamin B
(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?
Post by modyrater
Post by Benjamin B
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.
Post by modyrater
Post by Benjamin B
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;
...
--
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
For more options, visit https://groups.google.com/d/optout.
--
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.
Loading...