/*
 *  File Name       : linux/drivers/input/keyboard/mp200_btn.c
 *  Function        : button Interface
 *  Release Version : Ver 1.01
 *  Release Date    : 2006/02/22
 *
 *  Copyright (C) NEC Electronics Corporation 2005-2006
 *
 *
 *  This program is free software;you can redistribute it and/or modify itunder the terms of
 *  the GNU General Public License as published by Free Softwere Foundation;either version 2
 *  of License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warrnty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with this program;
 *  If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 *  MA 02111-1307, USA.
 *
 */

#include <linux/config.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>

#include <asm/arch/gpiocore.h>

#include "mp200_btn.h"

/**** Global parameters ****/
static unsigned int scan_delay = BTN_CHAT_GARDTIME;
static unsigned int scan_interval = BTN_SCAN_INTERVAL;

static spinlock_t scan_lock = SPIN_LOCK_UNLOCKED;

static struct input_dev mp200_btn_dev;
static void mp200_btn_scan_core(unsigned long data);
struct timer_list mp200_btn_scan_timer = {	/* timer for scan button matrix */
	.data = (unsigned long)&mp200_btn_dev,
	.function = mp200_btn_scan_core,
};

static unsigned char last_bcode = 0;	/* store last button matrix state */
static unsigned char black_bcode = 0;	/* the button should be abandoned */
static int button_irq = 0;

/* Translation table from the got data to keycode */
static struct mp200_btn_table mp200_btn_code_tbl[] = {
	{.scancode = KEY_UP,.btn_id_bit = (1 << 0),},
	{.scancode = KEY_RIGHT,.btn_id_bit = (1 << 1),},
	{.scancode = KEY_DOWN,.btn_id_bit = (1 << 2),},
	{.scancode = KEY_LEFT,.btn_id_bit = (1 << 3),},
	{.scancode = 0,.btn_id_bit = 0,},
};

/*
 * mp200_btn_scan_core
 *    scan button matrix, and judge valid pressing
 */
static void mp200_btn_scan_core(unsigned long data)
{
	int btn_push_cnt = 0;
	unsigned char chg_bcode;
	unsigned char tmp_bcode;
	unsigned char ulmask;
	unsigned char btncode = 0;
	struct input_dev *btn_dev = (struct input_dev *)data;

	struct mp200_btn_table *bst;

#ifdef BUTTON_DEBUG
	printk(KERN_INFO "%s(): time=%ld\n", __FUNCTION__, jiffies);
#endif

	spin_lock_irq(&scan_lock);

	mp200_gio_write_outputs_low32(BTN_SCAN_MASK, BTN_SCAN);

	btncode =
	    (mp200_gio_read_inputs_low32() >> BTN_DATA_SHIFT) & BTN_DATA_MASK;

	spin_unlock_irq(&scan_lock);

	/* check out pressed button number */
	ulmask = 1;
	bst = mp200_btn_code_tbl;
	tmp_bcode = btncode;

	while ((bst->btn_id_bit != 0) && (tmp_bcode != 0)) {
		if ((btncode & ulmask) != 0) {
			btn_push_cnt++;
			tmp_bcode &= ~ulmask;
		}
		ulmask <<= 1;
		bst++;
	}

	chg_bcode = last_bcode ^ btncode;

	/* if total pressed button number is invalid, ignore it */
	if (btn_push_cnt > 1) {
		tmp_bcode = (chg_bcode & btncode);
		black_bcode |= (tmp_bcode & ~last_bcode);
	}

	/* which button released ? */
	tmp_bcode = (chg_bcode & last_bcode);
	if ((tmp_bcode & ~black_bcode) != 0) {
		ulmask = 1;
		bst = mp200_btn_code_tbl;
		while (bst->btn_id_bit != 0) {
			if ((tmp_bcode & ulmask) != 0) {	/* check out which button */
				/* button release */
				input_report_key(btn_dev, bst->scancode, 0);
#ifdef BUTTON_DEBUG
				printk(KERN_INFO "%s(): RELEASE %d time=%ld\n",
				       __FUNCTION__, bst->scancode, jiffies);
#endif
				break;
			}
			ulmask <<= 1;
			bst++;
		}
	}

	/* which button pressed ? */
	tmp_bcode = (chg_bcode & btncode);
	if ((tmp_bcode & ~black_bcode) != 0) {
		ulmask = 1;
		bst = mp200_btn_code_tbl;
		while (bst->btn_id_bit != 0) {
			if ((tmp_bcode & ulmask) != 0) {	/* check out which button */
				/* button press */
				input_report_key(btn_dev, bst->scancode, 1);
#ifdef BUTTON_DEBUG
				printk(KERN_INFO "%s(): PRESS %d time=%ld\n",
				       __FUNCTION__, bst->scancode, jiffies);
#endif
				break;
			}
			ulmask <<= 1;
			bst++;
		}
	}

	/* make a record for legal button */
	last_bcode = btncode & ~black_bcode;

	/* try to clear black bit */
	black_bcode &= btncode;

	if (btncode != 0) {
		mod_timer(&mp200_btn_scan_timer, jiffies + scan_interval);
	} else {
		/* all button scan mode */
		mp200_gio_write_outputs_low32(BTN_SCAN_MASK, BTN_SCAN);

		/* button int unmask */
		spin_lock_irq(&scan_lock);

		enable_irq(button_irq);
		button_irq = 0;

		spin_unlock_irq(&scan_lock);
	}

	return;
}

/*
 * mp200_btn_interrupt
 *     button interrupt handler for all buttons
 */
static irqreturn_t
mp200_btn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
#ifdef BUTTON_DEBUG
	printk(KERN_INFO "%s(): time=%ld\n", __FUNCTION__, jiffies);
#endif
	/* button int unmask */
	if (button_irq != 0) {
		return IRQ_NONE;	/* ignore interrupt */
	}
	button_irq = irq;
	disable_irq(button_irq);

	mod_timer(&mp200_btn_scan_timer, jiffies + scan_delay);

	return IRQ_HANDLED;
}

/*
 * mp200_btn_init
 */
static int __init mp200_btn_init(void)
{
	int err;
	struct mp200_btn_table *bst;

	printk(KERN_INFO "Starting button.\n");

	/* set scan delay */
	scan_delay = scan_delay * HZ / 1000;
	if (scan_delay < 1) {
		scan_delay = BTN_CHAT_GARDTIME * HZ / 1000;
	}

	/* set scan interval */
	scan_interval = scan_interval * HZ / 1000;
	if (scan_interval < 1) {
		scan_interval = BTN_SCAN_INTERVAL * HZ / 1000;
	}

	/* init timers */
	init_timer(&mp200_btn_scan_timer);

	/* setup input device */
	set_bit(EV_KEY, mp200_btn_dev.evbit);
	set_bit(EV_REP, mp200_btn_dev.evbit);

	bst = mp200_btn_code_tbl;
	while (bst->btn_id_bit != 0) {
		set_bit(bst->scancode, mp200_btn_dev.keybit);
		bst++;
	}

	mp200_btn_dev.name = MP200_BTN_NAME;

	input_register_device(&mp200_btn_dev);

	/* set interrupt edge rise */
	set_irq_type(INT_GPIO_KEY_DATA0, (IRQT_RISING | __IRQT_32KPLS));
	set_irq_type(INT_GPIO_KEY_DATA1, (IRQT_RISING | __IRQT_32KPLS));
	set_irq_type(INT_GPIO_KEY_DATA2, (IRQT_RISING | __IRQT_32KPLS));
	set_irq_type(INT_GPIO_KEY_DATA3, (IRQT_RISING | __IRQT_32KPLS));

	/* button scan all on */
	mp200_gio_write_outputs_low32(BTN_SCAN_MASK, BTN_SCAN);

	/* request interrupt handler */
	err =
	    request_irq(INT_GPIO_KEY_DATA0, mp200_btn_interrupt, SA_INTERRUPT,
			MP200_BTN_NAME, 0);
	if (err != 0) {
		printk(KERN_INFO
		       "%s(): reuest_irq(%#x, %p, SA_INTERRUPT, %s, 0) failed (%d)\n",
		       __FUNCTION__, INT_GPIO_KEY_DATA0, mp200_btn_interrupt,
		       MP200_BTN_NAME, err);
		goto out_input_unregister_device;
	}

	err =
	    request_irq(INT_GPIO_KEY_DATA1, mp200_btn_interrupt, SA_INTERRUPT,
			MP200_BTN_NAME, 0);
	if (err != 0) {
		printk(KERN_INFO
		       "%s(): reuest_irq(%#x, %p, SA_INTERRUPT, %s, 0) failed (%d)\n",
		       __FUNCTION__, INT_GPIO_KEY_DATA1, mp200_btn_interrupt,
		       MP200_BTN_NAME, err);
		goto out_free_irq0;
	}

	err =
	    request_irq(INT_GPIO_KEY_DATA2, mp200_btn_interrupt, SA_INTERRUPT,
			MP200_BTN_NAME, 0);
	if (err != 0) {
		printk(KERN_INFO
		       "%s(): reuest_irq(%#x, %p, SA_INTERRUPT, %s, 0) failed (%d)\n",
		       __FUNCTION__, INT_GPIO_KEY_DATA2, mp200_btn_interrupt,
		       MP200_BTN_NAME, err);
		goto out_free_irq1;
	}

	err =
	    request_irq(INT_GPIO_KEY_DATA3, mp200_btn_interrupt, SA_INTERRUPT,
			MP200_BTN_NAME, 0);
	if (err != 0) {
		printk(KERN_INFO
		       "%s(): reuest_irq(%#x, %p, SA_INTERRUPT, %s, 0) failed (%d)\n",
		       __FUNCTION__, INT_GPIO_KEY_DATA3, mp200_btn_interrupt,
		       MP200_BTN_NAME, err);
		goto out_free_irq2;
	}

	return 0;

      out_free_irq2:
	free_irq(INT_GPIO_KEY_DATA2, 0);
      out_free_irq1:
	free_irq(INT_GPIO_KEY_DATA1, 0);
      out_free_irq0:
	free_irq(INT_GPIO_KEY_DATA0, 0);
      out_input_unregister_device:
	input_unregister_device(&mp200_btn_dev);

	return err;
}

/*
 * mp200_btn_exit
 */
static void __exit mp200_btn_exit(void)
{
	free_irq(INT_GPIO_KEY_DATA3, 0);
	free_irq(INT_GPIO_KEY_DATA2, 0);
	free_irq(INT_GPIO_KEY_DATA1, 0);
	free_irq(INT_GPIO_KEY_DATA0, 0);

	del_timer_sync(&mp200_btn_scan_timer);

	mp200_gio_write_outputs_low32(BTN_SCAN_MASK, 0);

	input_unregister_device(&mp200_btn_dev);
}

#ifndef MODULE
/* 
 * boot command line parameters
 * button=scan_delay[mS],scan_interval[mS]
 */
static int __init mp200_btn_setup(char *str)
{
	int inkb[3];

	get_options(str, ARRAY_SIZE(inkb), inkb);

	if ((inkb[0] >= 1) && (inkb[1] > 0)) {
		scan_delay = (unsigned int)inkb[1];
	}
	if ((inkb[0] >= 2) && (inkb[2] > 0)) {
		scan_interval = (unsigned int)inkb[2];
	}
	return 0;
}

__setup("button=", mp200_btn_setup);
#endif

module_init(mp200_btn_init);
module_exit(mp200_btn_exit);

MODULE_PARM(scan_delay, "l");
MODULE_PARM_DESC(scan_delay, "mp200 button chattering gard time [ms]");

MODULE_PARM(scan_interval, "l");
MODULE_PARM_DESC(scan_interval, "mp200 button scan interval time [ms]");

MODULE_LICENSE("GPL");
