/*
 *  linux/drivers/char/sa1100_switches.c
 *
 *  Copyright (C) 2000 John Dorsey
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  5 October 2000 - created.
 *
 *  25 October 2000 - userland file interface added.
 */

#include <linux/config.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/malloc.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/switches.h>
#include <linux/wait.h>

#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/uaccess.h>

/* NOTE: At the time of this writing, Erik Mouw is developing a
 *       generalized UCB1200 driver framework which will allow 
 *       separate logical drivers to share device. When this new
 *       code is released, sa1100_switches must be rewritten to
 *       not claim the Assabet UCB1300 exclusively.
 *
 *       As of now, this driver does not coexist with the
 *       UCB1200 touchscreen driver.
 */

#ifdef CONFIG_SA1100_ASSABET
# include <asm/arch/ucb1200.h>
#endif

#define SA1100_SWITCHES_NAME "sa1100_switches"

struct switches_action {
  struct list_head list;
  switches_mask_t  mask;
};

static int switches_users = 0;

static spinlock_t switches_lock = SPIN_LOCK_UNLOCKED;

DECLARE_WAIT_QUEUE_HEAD(switches_wait);
LIST_HEAD(switches_event_queue);

static void sa1100_switches_handler(int, void *, struct pt_regs *);

static ssize_t switches_read(struct file *file, char *buffer, 
			     size_t count, loff_t *pos){
  unsigned long flags;
  struct list_head *event;
  struct switches_action *action;

  if(count < sizeof(struct switches_mask_t))
    return -EINVAL;

  while(list_empty(&switches_event_queue)){ 
    if(file->f_flags & O_NDELAY) 
      return -EAGAIN; 

    interruptible_sleep_on(&switches_wait);

    if(signal_pending(current))
      return -ERESTARTSYS;
  }

  if(verify_area(VERIFY_WRITE, buffer, sizeof(struct switches_mask_t)))
    return -EFAULT;

  spin_lock_irqsave(&switches_lock, flags); 

  event=switches_event_queue.next;
  action=list_entry(event, struct switches_action, list);
  copy_to_user(buffer, &(action->mask), sizeof(struct switches_mask_t));
  list_del(event);
  kfree(action);

  spin_unlock_irqrestore(&switches_lock, flags);

  return 0;
}

static ssize_t switches_write(struct file *file, const char *buffer,
			      size_t count, loff_t *ppos){ 
  return -EINVAL; 
}

static unsigned int switches_poll(struct file *file, poll_table *wait){ 
  poll_wait(file, &switches_wait, wait); 

  if(!list_empty(&switches_event_queue)) 
    return POLLIN | POLLRDNORM; 

  return 0; 
}

static int switches_open(struct inode *inode, struct file *file){
  if(switches_users > 0)
    return -EBUSY;

  MOD_INC_USE_COUNT;
  ++switches_users;
  return 0;
}

static int switches_release(struct inode *inode, struct file *file){
  --switches_users;
  MOD_DEC_USE_COUNT;
  return 0;
}

static struct file_operations switches_ops = {
  read:    switches_read,
  write:   switches_write,
  poll:    switches_poll,
  open:    switches_open,
  release: switches_release
};

static struct miscdevice switches_misc = {
  MISC_DYNAMIC_MINOR, SA1100_SWITCHES_NAME, &switches_ops
};
    

#ifdef CONFIG_SA1100_ASSABET
static int assabet_switches_init(void){

  if(machine_has_neponset())
    NCR_0 |= NCR_GP01_OFF;
  
  set_GPIO_IRQ_edge(GPIO_GPIO0, GPIO_BOTH_EDGES);
  set_GPIO_IRQ_edge(GPIO_GPIO1, GPIO_BOTH_EDGES);
  set_GPIO_IRQ_edge(GPIO_UCB1300_IRQ, GPIO_BOTH_EDGES);
  
  if(request_irq(IRQ_GPIO0, sa1100_switches_handler, SA_INTERRUPT, 
		 SA1100_SWITCHES_NAME, NULL)<0){
    printk(KERN_ERR "%s: unable to register IRQ for GPIO 0\n",
	   SA1100_SWITCHES_NAME);
    return -EIO;
  }
  
  if(request_irq(IRQ_GPIO1, sa1100_switches_handler, SA_INTERRUPT, 
		 SA1100_SWITCHES_NAME, NULL)<0){
    printk(KERN_ERR "%s: unable to register IRQ for GPIO 1\n",
	   SA1100_SWITCHES_NAME);
    return -EIO;
  }
  
  if(request_irq(IRQ_GPIO_UCB1300_IRQ, sa1100_switches_handler,
		 SA_INTERRUPT, SA1100_SWITCHES_NAME, NULL)<0){
    printk(KERN_ERR "%s: UCB1300 in use, switches 3-8 not available\n",
	   SA1100_SWITCHES_NAME);
  } else {
    
    BCR_set(BCR_CODEC_RST);
    Ser4MCCR0 = MCCR0_MCE | MCCR0_SmpCnt;
    
    codec_write(CODEC_REG_IO_DIRECTION, 0);
    codec_write(CODEC_REG_RISE_INT_ENABLE, 0xff);
    codec_write(CODEC_REG_FALL_INT_ENABLE, 0xff);
    
  }

  return 0;
}

static int assabet_switches_shutdown(void){

  free_irq(IRQ_GPIO1, NULL);
  free_irq(IRQ_GPIO0, NULL);

  return 0;
}

/* Assabet
 * ^^^^^^^
 * We have eight general-purpose switches, S1 -- S8. To avoid 
 * potential confusion, since the switch names are marked on the
 * PCB, we set bits in the range [1, 8] in the mask that we return
 * to userland. Note that our internal (debouncing)  bookkeeping is
 * performed over [0, 7], and that we transpose signals SW7 and SW8.
 *
 * When no userland process is listening for switch events, switches
 * S1 and S2 have the following default action:
 *
 *   S1 - dump memory statistics to console (SysRq)
 *   S2 - dump task state information to console
 */
static void assabet_switches_handler(int irq, switches_mask_t *mask){
  unsigned int i, ucb1300_data, ucb1300_icsr, last, this;
  static unsigned int states = 0;

  switch(irq){
  case IRQ_GPIO0:
    last = ((states & 1<<0) != 0);
    this = ((GPLR & GPIO_GPIO0) != 0);

    if(last == this) /* debounce */
      break;

    if(switches_users)
      SWITCHES_SET(mask, 1, this);
    else
      show_mem();

    states = this ? (states | 1<<0) : (states & ~(1<<0));
    break;
    
  case IRQ_GPIO1:
    last = ((states & 1<<1) != 0);
    this = ((GPLR & GPIO_GPIO1) != 0);

    if(last == this) /* debounce */
      break;

    if(switches_users)
      SWITCHES_SET(mask, 2, this);
    else
      show_state();

    states = this ? (states | 1<<1) : (states & ~(1<<1));
    break;
    
  case IRQ_GPIO_UCB1300_IRQ:
    ucb1300_data = codec_read(CODEC_REG_IO_DATA);
    ucb1300_icsr = codec_read(CODEC_REG_INT_STATUS);

    for(i=0; i<6; ++i)
      if(ucb1300_icsr & 1<<i){
	
	last = ((states & 1<<(i+2)) != 0);
	this = ((ucb1300_data & 1<<i) != 0);

	if(last == this) /* debounce */
	  break;
	
	if(switches_users){

	  /* Intel StrongARM SA-1110 Development Board, Schematics
	   * Figure 5. Sheet 5 of 12
	   *
	   * See switches S8 and S7. Notice their relationship
	   * to signals SW7 and SW8. Hmmm.
	   */

	  switch(i){
	  case 4:
	    SWITCHES_SET(mask, 8, this);
	    break;
	  case 5:
	    SWITCHES_SET(mask, 7, this);
	    break;
	  default:
	    SWITCHES_SET(mask, i+3, this);
	  }

	}

	states = this ? (states | 1<<(i+2)) : (states & ~(1<<(i+2)));
      }

    codec_write(CODEC_REG_INT_STATUS, 0);
    codec_write(CODEC_REG_INT_STATUS, ucb1300_icsr & 0x3f);
	   
    break;
    
  default:
  }
}
#endif

static void sa1100_switches_handler(int irq, void *dev_id, 
				    struct pt_regs *regs){
  struct switches_action *action;

  if((action=
      (struct switches_action *)kmalloc(sizeof(struct switches_action), 
					GFP_KERNEL))==NULL){
    printk(KERN_ERR "%s: unable to allocate action descriptor\n",
	   SA1100_SWITCHES_NAME);
    return;
  }

  SWITCHES_ZERO(&action->mask);

  if(machine_is_assabet()){
#ifdef CONFIG_SA1100_ASSABET
    assabet_switches_handler(irq, &action->mask);
#endif
  }

  if(SWITCHES_COUNT(&action->mask)>0){

    spin_lock(&switches_lock);
    list_add_tail(&action->list, &switches_event_queue);
    spin_unlock(&switches_lock);

    wake_up_interruptible(&switches_wait);

  } else
    kfree(action);
}

static int __init sa1100_switches_init(void){

  if(machine_is_assabet()){
#ifdef CONFIG_SA1100_ASSABET
    if(assabet_switches_init()<0){
      printk(KERN_ERR "%s: unable to initialize Assabet switches\n",
	     SA1100_SWITCHES_NAME);
      return -EIO;
    }
#endif    
  }

  if(misc_register(&switches_misc)<0){
    printk(KERN_ERR "%s: unable to register misc device\n",
	   SA1100_SWITCHES_NAME);
    return -EIO;
  }

  printk("SA-1100 switches initialized\n");

  return 0;
}  

static void __exit sa1100_switches_exit(void){

  if(machine_is_assabet()){
#ifdef CONFIG_SA1100_ASSABET
    assabet_switches_shutdown();
#endif
  }

  if(misc_deregister(&switches_misc)<0)
    printk(KERN_ERR "%s: unable to deregister misc device\n",
	   SA1100_SWITCHES_NAME);
}

module_init(sa1100_switches_init);
module_exit(sa1100_switches_exit);
