[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: PortA Interrupt Handling



I have included my code (which i called gpioa.c)

I modified my Makefile in the drivers directory
comment out gpio.c...
and use this instead for the same build variable...

There are several ways of testing this code.

hold user button down
reset dev board...
telnet to dev board and cat /proc/kmsg
release user button...

I suppose the debug serial port should work as well.

Basically, the tasklet goes into a loop where it sleeps for a little while
before testing to see if the PortA line has returned low.

I am certain my problem lies with the interruptible_sleep_on_timeout() call.
I cannot use delay_usec() call however, as the line may be up for a long
time, and I want to do other things with the processor.  I was hoping that I
would be running on a kernel thread that could block during the sleep call,
and apparently this is not happening.  I have very little documentation on
Tasklets, so any help would be appreciated.

Also, I suspect that there might be a way to solve this with the help of
some user-land code, i.e. to use the scheduler queue with the
schedule_task() call.  (I have tried this and failed as well :) However, in
my application the driver should be able to function properly without a
registered driver present.

//
Greg


#include <linux/config.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/tqueue.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/init.h>

#include <asm/segment.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/svinto.h>
#include <asm/io.h>

#define GPIOA_MAJOR 125

DECLARE_WAIT_QUEUE_HEAD(poll_porta_wq);
static unsigned char porta_poll_tl_data;
static void porta_poll_tasklet(unsigned long l);
DECLARE_TASKLET (porta_poll_tl, porta_poll_tasklet, (unsigned long)
&porta_poll_tl_data);

static ssize_t gpioa_read(struct file *file, char *buf,
                         size_t count, loff_t *off);
static ssize_t gpioa_write(struct file *file, const char *buf,
                         size_t count, loff_t *off);
static int gpioa_ioctl(struct inode *inode, struct file *file,
                     unsigned int op, unsigned long arg);
static int gpioa_open(struct inode *inode, struct file *file);
static int gpioa_close(struct inode *inode, struct file *file);

static struct file_operations gpiod_fops = {
  read:    gpioa_read,   /* read    */
  write:   gpioa_write,  /* write   */
  ioctl:   gpioa_ioctl,  /* ioctl   */
  open:    gpioa_open,   /* open    */
  release: gpioa_close,  /* release */
};

static ssize_t gpioa_read(struct file *file, char *buf, size_t count, loff_t
*off) {
	return 0;
}

static ssize_t gpioa_write(struct file *file, const char *buf, size_t count,
loff_t *off) {
	return 0;
}

static int gpioa_ioctl(struct inode *inode, struct file *file,
              unsigned int op, unsigned long arg)
{
  switch (op) {
   default:
    return -EINVAL;
    break;
  }

  return 0;
}

static int gpioa_open(struct inode *inode, struct file *file)
{
  return 0;
}

static int gpioa_close(struct inode *inode, struct file *file)
{
  return 0;
}

static void porta_bit1_handler() {
    printk("got a bit1 handler call\n");
}

static void porta_irq(int irq, void* dev_id, struct pt_regs *regs) {
	unsigned char porta_val;
	unsigned char mask;
	int i;

	// diable offending irq
	*R_IRQ_MASK1_CLR = 2;  // diables offending irqs

	printk("got a PortA IRQ\n");

	porta_bit1_handler();

	tasklet_schedule(&porta_poll_tl);
}

static void porta_poll_tasklet(unsigned long l) {
	unsigned char porta_poll;
	int reason;

	porta_poll = *R_PORT_PA_DATA;
	porta_poll = porta_poll & 0x02;
	printk("got a poll tasklet with PA1 masked PA = %2X\n",porta_poll);
	while (porta_poll!=0) {
//		reason = interruptible_sleep_on_timeout(&poll_porta_wq,10); // wait 100
msec for the line to drop...
		porta_poll = *R_PORT_PA_DATA;
		porta_poll = porta_poll & 0x02;
		porta_poll = 0;
	}
	printk("finished the poll tasklet\n");
//	*R_IRQ_MASK1_SET = 2; // Reenable the Button Interrupt
}


static int __init etrax_par_gpioa_init(void)
{
  printk("ETRAX 100LX GPIOA IO driver v0.1\r\n");

  if (register_chrdev(GPIOA_MAJOR, "GPIOA", &gpiod_fops)) {
    printk("unable to get major %d for GPIOA_MAJOR\n", GPIOA_MAJOR);
  }

  if (request_irq(11,porta_irq,0,"gpioa PORTA IRQ",NULL)) {
    printk("unable to get gpioa PortA IRQ\n");
  }

  *R_IRQ_MASK1_SET = 2; // PA 1 (The Button)
  // this should cause an immediate interrupt - since the button is high
when not pressed...

  return 0;
}

module_init(etrax_par_gpioa_init);