Embedded Linux Frequency Meter

Fabiano Ferronato February 5, 2013 Coded in C for the TI OMAP 5912

A simple frequency meter implemented in embedded Linux in dual core TI OMAP 5912 (ARM + DSP). It´s a good example in how to use Linux I/Os, Irqs and Timers.
The data is shared between kernel and user space.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/arch/gpio.h>
#include <asm/arch/dmtimer.h>

/*
The prescaler is enabled when TCLR bit 5 is set (PRE). The 2n division ratio value
(PTV) can be configured in the TCLR register.

Each internal interrupt source can be independently enabled/disabled in the interrupt
enable register TIER.

When the interrupt event has been issued, the associated interrupt status bit is set
in the timer status register (TISR). The pending interrupt event is reset when the 
set status bit is overwritten by a 1.

The timer rate is defined by:
-Value of the prescaler fields (PRE and PTV of TCLR register)
-Value loaded into the timer load register (TLDR)

timer rate = (0xFFFF FFFF – TLDR + 1) x timer clock period x clock divider (PS)

PTV + 1)
PS = 2
*/

static unsigned long freq , ct, round;

extern struct omap_dm_timer * frequencimeter; // timer reserved to measure frequency

static irqreturn_t gpio4_freqmeter_irq_handler(int irq, void *arg);

static int __init freqmeter_init(void)
{
	int r;
	round = 0; freq = 0 ; ct = 0;
	printk(KERN_DEBUG "Init driver Freqmeter.\n");

	/* request gpios*/
	/* GPIO - P20_1610_GPIO4 */
	if ( omap_request_gpio(4) < 0 ) printk(KERN_ERR "Error init GPIO4 (freqmeter).\n");
	
	/* entrada */
	omap_set_gpio_direction(4,1); /* in */

	r = request_irq(OMAP_GPIO_IRQ(4), gpio4_freqmeter_irq_handler, IRQF_TRIGGER_RISING, "freqmeter", gpio4_freqmeter_irq_handler);
	if ( r < 0 ) {
		printk(KERN_ERR "freqmeter: request_irq() failed.\n");
		return r;
	}
	
	printk(KERN_DEBUG "freqmeter initialized.\n");
	return 0;
}

static irqreturn_t gpio4_freqmeter_irq_handler(int irq, void *arg)
{
	// dummy: 	no interrupt? freq = 0Hz
	//		only one int? freq = 0Hz  
	
	/**	there is interference?: lread INT again
		should be in same logic level */
	if ( omap_get_gpio_datain(4) )
	{
		if(round > 0)
		{
			if(round == 50)
			{
				
				ct = omap_dm_timer_read_counter(frequencimeter);
				omap_dm_timer_stop(frequencimeter);
				ct /= 50;
				freq = 1200000000/(ct +1);
				
				printk("freq = %d\n",(freq/*-8*/));
				round = 0xFFFFFFFF;
				ct = 0;
				freq = 0;
			}
		}
		else			// first read
		{
			freq = 0;
			
			printk(KERN_DEBUG "Iniciou o freqmeter");
			omap_dm_timer_write_counter(frequencimeter,0x0);
			omap_dm_timer_start(frequencimeter);
		}
		round++;
	}
		
	return IRQ_HANDLED;
}

asmlinkage long sys_freq_read(void)
{
	
	return freq;
}

static void __exit freqmeter_cleanup(void)
{
	free_irq(OMAP_GPIO_IRQ(4), NULL);
	omap_free_gpio(4);
}

module_init(freqmeter_init);
module_exit(freqmeter_cleanup);

MODULE_LICENSE("GPL");