305 lines
7.8 KiB
C
305 lines
7.8 KiB
C
/*
|
|
* amr_keypad.c
|
|
*
|
|
*
|
|
* Copyright (C) 2012 Loongson Corporation
|
|
*
|
|
* 2013-03-15
|
|
*/
|
|
#include <linux/types.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/init.h>
|
|
#include <linux/err.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/completion.h>
|
|
#include <asm/io.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/input.h>
|
|
#include <linux/serio.h>
|
|
#include <linux/delay.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/gpio.h>
|
|
#include <gsc3280/gpio.h>
|
|
#include <gsc3280/sysctl.h>
|
|
|
|
|
|
|
|
#define AMR_KPD_GPIO_SET_REG_BIT(reg) do {\
|
|
int i;\
|
|
unsigned int reg_val = 0;\
|
|
reg_val = __raw_readl((volatile unsigned int *)(reg));\
|
|
for (i = 0; i < AMR_PIN_NR; i++) {\
|
|
reg_val |= (0x01 << amr_gpios[i].gpio);\
|
|
}\
|
|
__raw_writel(reg_val, (volatile unsigned int *)(reg));\
|
|
} while (0)
|
|
|
|
|
|
#define AMR_KPD_GPIO_CLR_REG_BIT(reg) do {\
|
|
int i;\
|
|
unsigned int reg_val = 0;\
|
|
reg_val = __raw_readl((volatile unsigned int *)(reg));\
|
|
for (i = 0; i < AMR_PIN_NR; i++) {\
|
|
reg_val &= ~(0x01 << amr_gpios[i].gpio);\
|
|
}\
|
|
__raw_writel(reg_val, (volatile unsigned int *)(reg));\
|
|
} while (0)
|
|
|
|
enum {
|
|
AMR_KYP_UP = 0,
|
|
AMR_KYP_DOWN,
|
|
AMR_KYP_LEFT,
|
|
AMR_KYP_RIGHT,
|
|
AMR_KYP_OK,
|
|
AMR_KYP_CANCEL,
|
|
AMR_PIN_NR,
|
|
};
|
|
|
|
struct gpio amr_gpios[AMR_PIN_NR] = {
|
|
{GSC3280_GPA(9) , GPIOF_IN, "UP"},
|
|
{GSC3280_GPA(10), GPIOF_IN, "DOWN"},
|
|
{GSC3280_GPA(11), GPIOF_IN, "LEFT"},
|
|
{GSC3280_GPA(12), GPIOF_IN, "RIGHT"},
|
|
{GSC3280_GPA(21), GPIOF_IN, "OK"},
|
|
{GSC3280_GPA(27), GPIOF_IN, "CANCEL"}
|
|
};
|
|
|
|
static const int amr_keycode[AMR_PIN_NR] = {
|
|
KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT,
|
|
KEY_OK, KEY_CANCEL
|
|
};
|
|
/*----------------------------------------------------------------------------*/
|
|
struct amr_keypad {
|
|
struct input_dev *input;
|
|
struct gpio *arr_gpio;
|
|
|
|
struct timer_list amr_timer;
|
|
unsigned int cur_index;
|
|
bool timer_run;
|
|
};
|
|
|
|
struct amr_keypad amr_keypad_priv;
|
|
|
|
void amr_timer_fn(unsigned long arg)
|
|
{
|
|
unsigned int cur_gpio_index;
|
|
struct amr_keypad *p_amr_keypad = (struct amr_keypad *)arg;
|
|
|
|
|
|
cur_gpio_index = p_amr_keypad->cur_index;
|
|
|
|
if (!gpio_get_value(p_amr_keypad->arr_gpio[cur_gpio_index].gpio)) {
|
|
|
|
int key_code = amr_keycode[cur_gpio_index];
|
|
|
|
input_report_key(p_amr_keypad->input, key_code,
|
|
!test_bit(key_code, p_amr_keypad->input->key));
|
|
input_sync(p_amr_keypad->input);
|
|
}
|
|
|
|
p_amr_keypad->timer_run = false;
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static irqreturn_t amr_keypad_isr(int irq, void *dev_id)
|
|
{
|
|
unsigned int reg_val = 0;
|
|
int key_code = 0;
|
|
struct amr_keypad *p_amr_keypad = dev_id;
|
|
|
|
/*
|
|
if (timer_pending(&p_amr_keypad->amr_timer))
|
|
return IRQ_HANDLED;
|
|
*/
|
|
reg_val =
|
|
__raw_readl((volatile unsigned int *)(GSC3280_REGADDR_GPIO_INTSTATUS));
|
|
|
|
if (reg_val & (0x01 << p_amr_keypad->arr_gpio[AMR_KYP_UP].gpio)) {
|
|
p_amr_keypad->cur_index = AMR_KYP_UP;
|
|
} else if (reg_val & (0x01 << p_amr_keypad->arr_gpio[AMR_KYP_DOWN].gpio)) {
|
|
p_amr_keypad->cur_index = AMR_KYP_DOWN;
|
|
} else if (reg_val & (0x01 << p_amr_keypad->arr_gpio[AMR_KYP_LEFT].gpio)) {
|
|
p_amr_keypad->cur_index = AMR_KYP_LEFT;
|
|
} else if (reg_val & (0x01 << p_amr_keypad->arr_gpio[AMR_KYP_RIGHT].gpio)) {
|
|
p_amr_keypad->cur_index = AMR_KYP_RIGHT;
|
|
} else if (reg_val & (0x01 << p_amr_keypad->arr_gpio[AMR_KYP_OK].gpio)) {
|
|
p_amr_keypad->cur_index = AMR_KYP_OK;
|
|
} else if (reg_val & (0x01 << p_amr_keypad->arr_gpio[AMR_KYP_CANCEL].gpio)) {
|
|
p_amr_keypad->cur_index = AMR_KYP_CANCEL;
|
|
} else {
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
if (!gpio_get_value(p_amr_keypad->arr_gpio[p_amr_keypad->cur_index].gpio)) {
|
|
key_code = amr_keycode[p_amr_keypad->cur_index];
|
|
input_report_key(p_amr_keypad->input, key_code,
|
|
!test_bit(key_code, p_amr_keypad->input->key));
|
|
input_sync(p_amr_keypad->input);
|
|
}
|
|
/*
|
|
if (false == p_amr_keypad->timer_run)
|
|
{
|
|
printk(KERN_ERR "=************==== BUTONN %d", p_amr_keypad->cur_index);
|
|
p_amr_keypad->timer_run = true;
|
|
p_amr_keypad->amr_timer.data = (unsigned long)p_amr_keypad;
|
|
p_amr_keypad->amr_timer.expires = jiffies + 20;
|
|
p_amr_keypad->amr_timer.function = amr_timer_fn;
|
|
add_timer(&p_amr_keypad->amr_timer);
|
|
}
|
|
*/
|
|
AMR_KPD_GPIO_SET_REG_BIT(GSC3280_REGADDR_GPIO_PORTA_EOI);
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int amr_kp_open(struct input_dev *dev)
|
|
{
|
|
//enable pin interrupt
|
|
AMR_KPD_GPIO_SET_REG_BIT(GSC3280_REGADDR_GPIO_INTEN);
|
|
// clear interrupt
|
|
AMR_KPD_GPIO_SET_REG_BIT(GSC3280_REGADDR_GPIO_PORTA_EOI);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static void amr_kp_close(struct input_dev *dev)
|
|
{
|
|
//disable pin interrupt
|
|
AMR_KPD_GPIO_CLR_REG_BIT(GSC3280_REGADDR_GPIO_INTEN);
|
|
return;
|
|
}
|
|
|
|
void enable_amr_keypad_gpio_ctrl(void)
|
|
{
|
|
//edge trigger
|
|
AMR_KPD_GPIO_SET_REG_BIT(GSC3280_REGADDR_GPIO_INTTYPE_LEVEL);
|
|
//debounce
|
|
AMR_KPD_GPIO_SET_REG_BIT(GSC3280_REGADDR_GPIO_DEBOUNCE);
|
|
//unmask
|
|
AMR_KPD_GPIO_CLR_REG_BIT(GSC3280_REGADDR_GPIO_INTMASK);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int __init amr_keypad_init(void)
|
|
{
|
|
int i = 0;
|
|
int err = 0;
|
|
int p_irq = gpio_to_irq(amr_gpios[0].gpio);
|
|
|
|
|
|
err = gpio_request_array(amr_gpios, AMR_PIN_NR);
|
|
if (err < 0) {
|
|
printk(KERN_ERR "amr_keypad_init gpio_request_array failed!\n");
|
|
err = -ENODEV;
|
|
goto EXIT;
|
|
}
|
|
amr_keypad_priv.arr_gpio = amr_gpios;
|
|
err = request_irq(p_irq, &amr_keypad_isr, IRQF_DISABLED,
|
|
"amr-kpd", &amr_keypad_priv);
|
|
if (err < 0) {
|
|
printk(KERN_ERR "amr_keypad_init request_irq[%d] failed!\n", p_irq);
|
|
err = -ENODEV;
|
|
goto FREE_GPIO;
|
|
}
|
|
|
|
enable_amr_keypad_gpio_ctrl();
|
|
|
|
amr_keypad_priv.input = input_allocate_device();
|
|
if (!amr_keypad_priv.input) {
|
|
err = -ENOMEM;
|
|
goto out_free_irq;
|
|
}
|
|
|
|
/* event type set */
|
|
set_bit(EV_KEY, amr_keypad_priv.input->evbit);
|
|
amr_keypad_priv.input->name = "amr_keypad";
|
|
amr_keypad_priv.input->phys = "input/amr_keypad";
|
|
|
|
amr_keypad_priv.input->id.bustype = BUS_HOST;
|
|
amr_keypad_priv.input->id.vendor = 0;
|
|
amr_keypad_priv.input->id.product = 0;
|
|
amr_keypad_priv.input->id.version = 0x0001;
|
|
|
|
for (i = 0; i < AMR_PIN_NR; i++)
|
|
set_bit(amr_keycode[i], amr_keypad_priv.input->keybit);
|
|
//set_bit(KEY_WAKEUP, amr_keypad_priv.input->keybit);
|
|
|
|
input_set_drvdata(amr_keypad_priv.input, &amr_keypad_priv);
|
|
amr_keypad_priv.input->open = amr_kp_open;
|
|
amr_keypad_priv.input->close = amr_kp_close;
|
|
|
|
err = input_register_device(amr_keypad_priv.input);
|
|
if (err) {
|
|
printk("unable to register input device: %d\n", err);
|
|
goto out_freeinput;
|
|
}
|
|
|
|
init_timer(&amr_keypad_priv.amr_timer);
|
|
amr_keypad_priv.timer_run = false;
|
|
sysctl_iomux_disable(SYSCTL_IOMUX_JTAG);
|
|
//sysctl_mod_disable(SYSCTL_IOMUX_JTAG);
|
|
|
|
return err;
|
|
|
|
out_freeinput:
|
|
input_free_device(amr_keypad_priv.input);
|
|
out_free_irq:
|
|
for (i = 0; i < AMR_PIN_NR; i++) {
|
|
int p_irq = gpio_to_irq(amr_gpios[i].gpio);
|
|
free_irq(p_irq, &amr_gpios[i]);
|
|
}
|
|
FREE_GPIO:
|
|
gpio_free_array(amr_gpios, AMR_PIN_NR);
|
|
EXIT:
|
|
return err;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static void __exit amr_keypad_exit(void)
|
|
{
|
|
int i = 0;
|
|
|
|
input_free_device(amr_keypad_priv.input);
|
|
|
|
for (i = 0; i < AMR_PIN_NR; i++) {
|
|
int p_irq = gpio_to_irq(amr_gpios[i].gpio);
|
|
free_irq(p_irq, &amr_gpios[i]);
|
|
}
|
|
|
|
gpio_free_array(amr_gpios, AMR_PIN_NR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
module_init(amr_keypad_init);
|
|
module_exit(amr_keypad_exit);
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
MODULE_AUTHOR("ansonn.wang@gmail.com");
|
|
MODULE_DESCRIPTION("amr keypad");
|
|
MODULE_LICENSE("GPL");
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|