EmbeddedRelated.com
Forums
Memfault Beyond the Launch

Using ioperm() example?

Started by Dan Lyke March 3, 2009
Hey, I'm trying to move some simple code into Linux, and I'm trying to
figure out the easiest way to use ioperm() in a process running as
root to write to and read from some port A pins.

So I want to basically do something like:

AT91C_BASE_PIOA->PIO_SODR = 1 << 24;

Where AT91C_BASE_PIOA is, I believe, defined in AT91SAM9261.h. If I'm
reading this right, I do

ioperm(AT91C_BASE_PIOA, sizeof(*AT91C_BASE_PIOA));

(with the appropriate casts), and then use inl() and outl(). Is this
right? Is copying the AT91SAM9261.h from my other at91lib/... the right
way to find these ports?

Thanks!
On Tue, 3 Mar 2009 15:12:34 -0800
Dan Lyke wrote:
> So I want to basically do something like:
>
> AT91C_BASE_PIOA->PIO_SODR = 1 << 24;

Answering my own question:

ioperm() doesn't work. Using memmap() on /dev/mem works:
http://www.arm.linux.org.uk/mailinglists/faq.php#f8

Dan
Hi Dan,

A bit late, but I was off with other things on Linux.

> ioperm() doesn't work.

Indeed, I read that's only for x86.

> Using memmap() on /dev/mem works:

Seems you can avoid devices altogether, if I get this right :
if you can guarantee no one else is accessing the I/O, my understanding is
that you can simply use __raw_readl() and __raw_writel().
These are inlines, an example for timer programming on SAM9 was posted
recently on kernelnewbies.org (of course need to use 9261 headers instead of
9200) by Ole Loots.

Anyone feels this is wrong, please correct me !
/*
* Kernel interrupt demo for Timer Channel 0
* Author :
* Date : 24.02.2009
* Version : 1.00
* License : GPL
*/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#include
#include

#include
#include
#include

#include
#include
#include

#include
#include
#include
#include "adirq.h"
//#include "at91_timer.h"

#define AT91_TC0 0x00
#define AT91_TC1 0x40
#define AT91_TC2 0x80

#define AT91_TC3 0x00
#define AT91_TC4 0x40
#define AT91_TC5 0x80

static void __exit ad_irq_cleanup(void);

static volatile unsigned long ad_irq_count = 0;

#ifdef MODULE_AUTHOR
MODULE_AUTHOR("Ole");
MODULE_DESCRIPTION("AD Kernel interrupt module for DNP/9200");
MODULE_LICENSE("GPL");
#endif

/* unload kernel module */
static void __exit ad_irq_cleanup(void) {
/* unregister irq handler */
free_irq(AT91_ID_TC0, NULL);
printk(KERN_INFO "ad_irq module removed.\n");
}

/*
* Read from Timerblock 0 registers.
*/
static inline unsigned long at91_tcb1_read(unsigned int reg)
{
void __iomem *tcb1_base = (void __iomem *)AT91_VA_BASE_TCB0;
printk(KERN_INFO "read p: %p + (%d)\n", tcb1_base, reg);

return __raw_readl(tcb1_base + reg);
}

/*
* Write to Timerblock 0 registers.
*/
static inline void at91_tcb1_write(unsigned int reg, unsigned long value)
{
void __iomem *tcb1_base = (void __iomem *)AT91_VA_BASE_TCB0;
printk(KERN_INFO "write p: %p + (%d)\n", tcb1_base, reg);

__raw_writel(value, tcb1_base + reg);
}

/*
* Read from Timerblock 1 registers.
*/
static inline unsigned long at91_tcb2_read(unsigned int reg)
{
void __iomem *tcb2_base = (void __iomem *)AT91_VA_BASE_TCB1;
return __raw_readl(tcb2_base + reg);
}

/*
* Write to Timerblock 1 registers.
*/
static inline void at91_tcb2_write(unsigned int reg, unsigned long value)
{
void __iomem *tcb2_base = (void __iomem *)AT91_VA_BASE_TCB1;

__raw_writel(value, tcb2_base + reg);
}

/* irq timer routine */
irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) {
unsigned int status;

// read & clear status:
status = at91_tcb1_read(AT91_TC0 + AT91_TC_SR);
ad_irq_count++;
return(IRQ_HANDLED);
}
static int __init ad_irq_init(void)
{
printk(KERN_INFO "init timer\n");

// disable clock:
at91_tcb1_write(AT91_TC0 + AT91_TC_CCR, ((at91_tcb1_read(AT91_TC0 +
AT91_TC_CCR) | 2) ) );

// disable all Timer Channel 0 interrupts:
at91_tcb1_write(AT91_TC0 + AT91_TC_IDR, 0xFFFFFFFF );

// read & clear status:
at91_tcb1_read(AT91_TC0 + AT91_TC_SR );

// enable timer clock 5, reset counter and start clock
at91_tcb1_write(AT91_TC0 + AT91_TC_CMR, AT91_TC_TIMER_CLOCK4 |
AT91_TC_CPCTRG | !AT91_TC_WAVE );
printk(KERN_INFO "CMR: %d\n", at91_tcb1_read(AT91_TC0 +
AT91_TC_CMR) );

// Enables the RC Compare Interrupt:
at91_tcb1_write(AT91_TC0 + AT91_TC_IER, 0x10 );
if (request_irq(AT91_ID_TC0, (void *)timer_interrupt, 0
,AD_IRQ_DEVICE_NAME, NULL)) {
printk(KERN_ERR "ad_irq: irq alrdy claimed!\n");
return -EIO;
}

// write something to timer register c:
at91_tcb1_write( AT91_TC0 + AT91_TC_RC , 0xFBC5 );

// enable & start clock
at91_tcb1_write(AT91_TC0 + AT91_TC_CCR , 0x01 );
at91_tcb1_write(AT91_TC0 + AT91_TC_CCR , 0x05 );

// maybe sync is needed ?
at91_tcb1_write(AT91_TC0 + AT91_TC_BMR, 1 );

printk(KERN_INFO "ad_irq module installed with irqnr=%d.\n",
AT91_ID_TC0);
return 0;
}
EXPORT_NO_SYMBOLS;

module_init(ad_irq_init);
module_exit(ad_irq_cleanup);

HTH
Best Regards,
Kris
-----Original Message-----
From: A... [mailto:A...] On Behalf Of
Dan Lyke
Sent: Wednesday, 4 March 2009 11:36 AM
To: A...
Subject: Re: [AT91SAM] Using ioperm() example?

On Tue, 3 Mar 2009 15:12:34 -0800
Dan Lyke wrote:
> So I want to basically do something like:
>
> AT91C_BASE_PIOA->PIO_SODR = 1 << 24;

Answering my own question:

ioperm() doesn't work. Using memmap() on /dev/mem works:
http://www.arm.linux.org.uk/mailinglists/faq.php#f8

Dan

Memfault Beyond the Launch