Benjamin B
2015-11-06 18:40:31 UTC
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");
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.
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.