This is the driver part of the device. We implemented a character driver for our device. We are proramming GPIO and CLKOUT0 registers during open() and mapping physical address of the MMIO using ioremap() to map the address in kernel virtual address space. We are handing read(), write() and same read/write via ioctl(). These read/write/ioctl are blocking and will block in wait queue till device sends an interrupt and wait queue is woke up. We have installed interrupt handler for handing EINT3 for this.

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/sched.h>
#include <linux/wait.h>

#define DEVICE_NAME "myled"
#define GPACON  0x56000000 
#define GPA13   (<< 13)
#define MISCCR 0x56000080 
#define GPHCON 0x56000070
#define LEDBASE 0x10000000
#define IO_READ 0
#define IO_WRITE 1
#define IO_NONE (-1)

wait_queue_head_t my_write_wait;
wait_queue_head_t my_read_wait;
spinlock_t myled_lock = SPIN_LOCK_UNLOCKED;

static unsigned char __iomem *base_ledaddr = NULL;
static int dev_req[2] = { IO_NONE, IO_NONE };
void Clk0_Enable(int clock_sel)  
{
  /* 0:MPLLin, 1:UPLL, 2:FCLK, 3:HCLK, 4:PCLK, 5:DCLK0 */
  unsigned int __iomem *rMISCCR, *rGPHCON;
  rMISCCR = ioremap(MISCCR, 0x100);
  if (rMISCCR) {
    *rMISCCR = (*rMISCCR & (~(7<<4))) | (clock_sel<<4);
    iounmap(rMISCCR);
  }
  rGPHCON = ioremap(GPHCON, 0x100);
  if (rMISCCR) {
    *rGPHCON = (*rGPHCON & (~(3<<18))) | (2<<18);
    iounmap(rGPHCON);
  }
  
}
static irqreturn_t myled_interrupt(int irq, void *dev_id)
{
  int *req = (int*)dev_id;
  printk("Entered %s\n", "myled_interrupt");
  
  if (req[0] == IO_READ) {
    printk("wakeup read\n");
    wake_up_interruptible(&my_read_wait);
    dev_req[0] = IO_NONE;
  } else if(req[1] == IO_WRITE) {
    printk("wakeup write\n");
    wake_up_interruptible(&my_write_wait);
    dev_req[1] = IO_NONE;
  }
  
  return IRQ_RETVAL(IRQ_HANDLED);
}
static int myled_open (struct inode *inodep,
                       struct file *filep)
{
  int err = 0;
  unsigned int __iomem *base_addr;
  unsigned long flags;
  if(base_ledaddr != NULL) return -1;
  printk("Entered %s\n", "myled_open");
  base_addr = ioremap(GPACON, 0x100);
  if(base_addr) {
    *base_addr = *base_addr  | GPA13;
  }
  iounmap(base_addr);
  Clk0_Enable(4);
  init_waitqueue_head (&my_read_wait);
  init_waitqueue_head (&my_write_wait);
  base_ledaddr = ioremap(LEDBASE, 0x100);
  if(base_ledaddr == NULL) {
    return -1;
  }
  spin_lock_irqsave(&myled_lock, flags);
  dev_req[0] = IO_NONE;
  dev_req[1] = IO_NONE;
  spin_unlock_irqrestore(&myled_lock, flags);
    err = request_irq(IRQ_EINT3,
                      myled_interrupt,
                      IRQ_TYPE_LEVEL_LOW, 
                      DEVICE_NAME,
                      (void *)dev_req);
  return err;
  
}
static int myled_release (struct inode *inodep,
                          struct file *filep)
{
  unsigned long flags;
  printk("Entered %s\n", "myled_release");
  if(base_ledaddr){
    iounmap(base_ledaddr);
    base_ledaddr = NULL;
  }
  free_irq(IRQ_EINT3, (void *)&dev_req);
  spin_lock_irqsave(&myled_lock, flags);
  dev_req[0] = IO_NONE;
  dev_req[1] = IO_NONE;
  spin_unlock_irqrestore(&myled_lock, flags);
   return 0;
  
}
static ssize_t myled_read (struct file *filep,
                           char __user * val,
                           size_t sizen,
                           loff_t * off)
{
  
  unsigned char k_val;
  unsigned long flags;
  printk("Entered %s\n", "myled_read");
  spin_lock_irqsave(&myled_lock, flags);
  dev_req[0] = IO_READ;
  k_val = *base_ledaddr;
  spin_unlock_irqrestore(&myled_lock, flags);
  wait_event_interruptible(my_read_wait,
                             (dev_req[0] == IO_NONE));
  copy_to_user(val, &k_val, 1);
  return 1;
}
static ssize_t myled_write(struct file *filep,
                           const char __user *val,
                           size_t sizen,
                           loff_t * off)
{

  unsigned char k_val;
  unsigned long flags;
  printk("Entered %s\n", "myled_write");
  copy_from_user(&k_val, val, 1);
  spin_lock_irqsave(&myled_lock, flags);
  dev_req[1] = IO_WRITE;
  *base_ledaddr = k_val;
  spin_unlock_irqrestore(myled_lock, flags);
  wait_event_interruptible(my_write_wait,
                             (dev_req[1] == IO_NONE));
  return 1;
}
static int myled_ioctl(
  struct inode *inode, 
  struct file *file, 
  unsigned int cmd, 
  unsigned long val)
{
  unsigned char k_val;
  unsigned long flags;
  printk("Entered %s request %s\n",
            "myled_ioctl", (cmd == IO_READ) ? "READ" : "WRITE");
  switch(cmd) {
    case IO_READ:
      spin_lock_irqsave(&myled_lock, flags);
      dev_req[0] = IO_READ;
      k_val = *base_ledaddr;
      spin_unlock_irqrestore(&myled_lock, flags);
      wait_event_interruptible(my_read_wait,
                               (dev_req[0] == IO_NONE));
      copy_to_user((unsigned char *)val, &k_val, 1);
      return 0;
    case IO_WRITE:
      copy_from_user(&k_val, (unsigned char *)val, 1);
      spin_lock_irqsave(&myled_lock, flags);
      dev_req[1] = IO_WRITE;
      *base_ledaddr = k_val;
      spin_unlock_irqrestore(&myled_lock, flags);
      wait_event_interruptible(my_write_wait,
                               (dev_req[1] == IO_NONE));
    return 0;
  default:
    return -EINVAL;
  }
}

static struct file_operations dev_fops = {
  .owner  =  THIS_MODULE,
  .ioctl  =  myled_ioctl,
  .open   =  myled_open,
  .release   =  myled_release,
  .read    = myled_read,
  .write   = myled_write,
};

static struct miscdevice misc = {
  .minor = MISC_DYNAMIC_MINOR,
  .name = DEVICE_NAME,
  .fops = &dev_fops,
};

static int __init leddev_init(void)
{
  int ret;
  printk("Entered %s\n", "leddev_init");
  ret = misc_register(&misc);

  printk (DEVICE_NAME"\tinitialized\n");

  return ret;
}

static void __exit leddev_exit(void)
{
  printk("Entered %s\n", "leddev_exit");
  misc_deregister(&misc);
}

module_init(leddev_init);
module_exit(leddev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EQuestionAnswers.com Inc.");

#