/* 2009-08-20: File added and changed by Sony Corporation */
/*
 *  File Name       : linux/drivers/input/keyboard/mp200_btn.c
 *  Function        : button Interface
 *  Release Version : Ver 1.02
 *  Release Date    : 2009/02/04
 *
 *  Copyright (C) NEC Electronics Corporation 2009
 *
 *
 *  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/delay.h>
#include <linux/device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include <asm/mach/irq.h>

#include "mp200_btn.h"

//#define BUTTON_DEBUG

/**** Global parameters ****/
static unsigned int scan_delay = BTN_CHAT_GUARDTIME;
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 */
	.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 */

/* 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),},		/* SW12 */
	{.scancode = KEY_LEFT,.btn_id_bit = (1 << 1),},		/* SW13 */
	{.scancode = KEY_DOWN,.btn_id_bit = (1 << 2),},		/* SW9 */
	{.scancode = KEY_RIGHT,.btn_id_bit = (1 << 3),},	/* SW8 */
	{.scancode = KEY_BACK,.btn_id_bit = (1 << 4),},		/* SW11 */
	{.scancode = KEY_ENTER,.btn_id_bit = (1 << 5),},	/* SW10 */
	{.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;
	unsigned int val, val1, val2;
	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);

	pwc_write(PWC_IOOUT1, BTN_SCAN1, BTN_SCAN_MASK);
	pwc_read(PWC_IOIN1, &val1);
	pwc_write(PWC_IOOUT1, BTN_SCAN2, BTN_SCAN_MASK);
	pwc_read(PWC_IOIN1, &val2);

	val = (val1 & 0xf) | ((val2 & 0xc) << 2);

	btncode = val & 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 {
		/* button int unmask */
		spin_lock_irq(&scan_lock);

		pwc_write(PWC_IOOUT1, BTN_SCAN, BTN_SCAN_MASK);
		enable_irq(INT_PWC_GPIO_KEY_DATA0);
		enable_irq(INT_PWC_GPIO_KEY_DATA1);
		enable_irq(INT_PWC_GPIO_KEY_DATA2);
		enable_irq(INT_PWC_GPIO_KEY_DATA3);

		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)
{
#ifdef BUTTON_DEBUG
	printk(KERN_INFO "%s(): time=%ld\n", __FUNCTION__, jiffies);
#endif

	/* button int unmask */
	disable_irq(INT_PWC_GPIO_KEY_DATA0);
	disable_irq(INT_PWC_GPIO_KEY_DATA1);
	disable_irq(INT_PWC_GPIO_KEY_DATA2);
	disable_irq(INT_PWC_GPIO_KEY_DATA3);

	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;
	int irq = INT_KEY_DATA_BASE;

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

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

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

	mp200_btn_dev = input_allocate_device();
	if (!mp200_btn_dev) {
		printk(KERN_ERR "mp200_btn.c: Not enough memory for input device\n");
		return -ENOMEM;
	}
	mp200_btn_scan_timer.data = (unsigned long)mp200_btn_dev,

	/* 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;

	err = input_register_device(mp200_btn_dev);
	if(err < 0) {
		printk(KERN_ERR "Unable to register mp200_btn input device\n");
		goto out_input_free_device;
	}

	for (; irq <= INT_KEY_DATA_LAST; irq++) {
		/* set interrupt high level */
		set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);

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

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

	return 0;


out_free_irq:
	for (irq--; irq >= INT_KEY_DATA_BASE; irq--) {
		free_irq(irq, 0);
	}
	input_unregister_device(mp200_btn_dev);

out_input_free_device:
	input_free_device(mp200_btn_dev);


	return err;
}

/*
 * mp200_btn_exit
 */
static void __exit mp200_btn_exit(void)
{
	int irq;

	pwc_write(PWC_IOOUT1, 0, BTN_SCAN_MASK);

	for (irq = INT_KEY_DATA_BASE; irq <= INT_KEY_DATA_LAST; irq++) {
		free_irq(irq, 0);
	}

	del_timer_sync(&mp200_btn_scan_timer);

	input_unregister_device(mp200_btn_dev);
	input_free_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_param(scan_delay, uint, 0644);
MODULE_PARM_DESC(scan_delay, "mp200 button chattering guard time [ms]");

module_param(scan_interval, uint, 0644);
MODULE_PARM_DESC(scan_interval, "mp200 button scan interval time [ms]");

MODULE_LICENSE("GPL");
