* more details.
*
*/
-
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/types.h>
#include <linux/input.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/errno.h>
-#include <linux/irq.h>
-#include <linux/of_irq.h>
-
-#include <asm/irq.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-#include <uapi/linux/input.h>
-#include <linux/major.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/of.h>
-#include <linux/amlogic/aml_gpio_consumer.h>
-#include <linux/amlogic/gpio-amlogic.h>
-#include <linux/amlogic/sd.h>
-#include <linux/amlogic/iomap.h>
-#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/pm.h>
-#include <linux/of_address.h>
-#include "../../../gpio/gpiolib.h"
-
-#define OFFSET 24
-
-#define MOD_NAME "gpio_key"
#undef pr_fmt
-#define pr_fmt(fmt) "gpio_key: " fmt
+#define pr_fmt(fmt) "gpio-keypad: " fmt
-struct gpio_key {
- int code; /* input key code */
- const char *name;
- int pin; /*pin number*/
- int status; /*0 up , 1 down*/
-};
+#define DEFAULT_SCAN_PERION 20
+#define DEFAULT_POLL_MODE 0
+#define KEY_JITTER_COUNT 1
-struct gpio_platform_data {
- struct gpio_key *key;
- int key_num;
- int repeat_delay;
- int repeat_period;
- int irq_keyup;
- int irq_keydown;
+struct pin_desc {
+ int current_status;
+ struct gpio_desc *desc;
+ int irq_num;
+ u32 code;
+ const char *name;
};
-struct kp {
- struct input_dev *input;
- struct timer_list timer;
- int config_major;
- char config_name[20];
- struct class *config_class;
- struct device *config_dev;
- struct gpio_key *keys;
- int key_num;
- struct work_struct work_update;
+struct gpio_keypad {
+ int key_size;
+ int use_irq;//1:irq mode ; 0:polling mode
+ int scan_period;
+ int current_irq;
+ int count;
+ int index;
+ struct pin_desc *key;
+ struct pin_desc *current_key;
+ struct timer_list polling_timer;
+ struct input_dev *input_dev;
};
-static struct kp *gp_kp;
-
-int amlogic_gpio_name_map_num(struct platform_device *pdev, const char *value)
-{
- return of_property_read_string(pdev->dev.of_node, "key_pin", &value);
-}
-
-
-static void kp_work(struct kp *kp)
-{
- struct gpio_key *key;
- int i;
- int io_status;
-
- key = kp->keys;
- for (i = 0; i < kp->key_num; i++) {
- io_status = gpiod_get_value(gpio_to_desc(key->pin));
- if (io_status != key->status) {
- if (io_status) {
- dev_info(&kp->input->dev,
- "key %d up\n", key->code);
- input_report_key(kp->input, key->code, 0);
- input_sync(kp->input);
- } else {
- dev_info(&kp->input->dev,
- "key %d down\n", key->code);
- input_report_key(kp->input, key->code, 1);
- input_sync(kp->input);
- }
- key->status = io_status;
- }
- key++;
- }
-}
-
-static void update_work_func(struct work_struct *work)
-{
- struct kp *kp_data = container_of(work, struct kp, work_update);
-
- kp_work(kp_data);
-}
-
-/*
- * What we do here is just for loss wakeup key when suspend.
- * In suspend routine , the intr is disable.
- * we need do more things to adapt the gpio change.
- */
-int det_pwr_key(void)
-{
- /*return aml_read_reg32(P_AO_IRQ_STAT) & (1<<8);*/
- return 0;
-}
-/*Enable gpio interrupt for AO domain interrupt*/
-void set_pwr_key(void)
+static irqreturn_t gpio_irq_handler(int irq, void *data)
{
- /* aml_write_reg32(P_AO_IRQ_GPIO_REG,
- * aml_read_reg32(P_AO_IRQ_GPIO_REG)|(1<<18)|(1<<16)|(0x3<<0));
- * aml_write_reg32(P_AO_IRQ_MASK_FIQ_SEL,
- * aml_read_reg32(P_AO_IRQ_MASK_FIQ_SEL)|(1<<8));
- * aml_set_reg32_mask(P_AO_IRQ_STAT_CLR, 1<<8);
- */
-}
+ struct gpio_keypad *keypad;
-void clr_pwr_key(void)
-{
- /*aml_set_reg32_mask(P_AO_IRQ_STAT_CLR, 1<<8);*/
-}
-
-
-#ifdef USE_IRQ
-
-static irqreturn_t kp_isr(int irq, void *data)
-{
- struct kp *kp_data = (struct kp *)data;
-
- queue_work(system_freezable_wq, &(kp_data->work_update));
-
- /* if (!deep_suspend_flag)
- * clr_pwr_key();
- */
+ keypad = (struct gpio_keypad *)data;
+ keypad->current_irq = irq;
+ keypad->count++;
+ mod_timer(&(keypad->polling_timer),
+ jiffies+msecs_to_jiffies(20));
return IRQ_HANDLED;
}
-#else
-void kp_timer_sr(unsigned long data)
+static struct pin_desc *get_current_key(struct gpio_keypad *keypad)
{
- struct kp *kp_data = (struct kp *)data;
+ int i;
- queue_work(system_freezable_wq, &(kp_data->work_update));
- mod_timer(&kp_data->timer, jiffies+msecs_to_jiffies(25));
+ for (i = 0; i < keypad->key_size; i++) {
+ if (keypad->current_irq == keypad->key[i].irq_num)
+ return &(keypad->key[i]);
+ }
+ return NULL;
}
-#endif
-static int gpio_key_config_open(struct inode *inode, struct file *file)
+static void report_key_code(struct gpio_keypad *keypad, int key_status)
{
- file->private_data = gp_kp;
- return 0;
-}
-
-static int gpio_key_config_release(struct inode *inode, struct file *file)
-{
- file->private_data = NULL;
- return 0;
+ struct pin_desc *key;
+
+ if (keypad->count < KEY_JITTER_COUNT)
+ keypad->count++;
+ else {
+ key = keypad->current_key;
+ key->current_status = gpiod_get_value(key->desc);
+ input_report_key(keypad->input_dev,
+ key->code, key_status);
+ input_sync(keypad->input_dev);
+ if (key_status)
+ dev_info(&(keypad->input_dev->dev),
+ "key %d down.\n",
+ key->code);
+ else
+ dev_info(&(keypad->input_dev->dev),
+ "key %d up.\n",
+ key->code);
+ keypad->count = 0;
+ }
}
-
-static const struct file_operations keypad_fops = {
- .owner = THIS_MODULE,
- .open = gpio_key_config_open,
- .release = gpio_key_config_release,
-};
-
-static int register_keypad_dev(struct kp *kp)
+static void polling_timer_handler(unsigned long data)
{
- int ret = 0;
- char name[] = "gpio_keyboard";
+ struct gpio_keypad *keypad;
+ struct pin_desc *key;
+ int i;
- strncpy(kp->config_name, name, sizeof(name));
- ret = register_chrdev(0, kp->config_name, &keypad_fops);
- if (ret <= 0) {
- dev_info(&kp->input->dev, "register char device error\n");
- return ret;
+ keypad = (struct gpio_keypad *)data;
+ if (keypad->use_irq) {//irq mode
+ keypad->current_key = get_current_key(keypad);
+ if (!(keypad->current_key))
+ return;
+ key = keypad->current_key;
+ if (key->current_status != gpiod_get_value(key->desc))
+ report_key_code(keypad, key->current_status);
+ else
+ keypad->count = 0;
+ if (key->current_status == 0)
+ mod_timer(&(keypad->polling_timer),
+ jiffies+msecs_to_jiffies(keypad->scan_period));
+ } else {//polling mode
+ for (i = 0; i < keypad->key_size; i++) {
+ if (keypad->key[i].current_status !=
+ gpiod_get_value(keypad->key[i].desc)) {
+ keypad->index = i;
+ keypad->current_key = &(keypad->key[i]);
+ report_key_code(keypad,
+ keypad->key[i].current_status);
+ } else if (keypad->index == i)
+ keypad->count = 0;
+ mod_timer(&(keypad->polling_timer),
+ jiffies+msecs_to_jiffies(keypad->scan_period));
+ }
}
- kp->config_major = ret;
- dev_info(&kp->input->dev, "gpio keypad major:%d\n", ret);
- kp->config_class = class_create(THIS_MODULE, kp->config_name);
- kp->config_dev = device_create(kp->config_class, NULL,
- MKDEV(kp->config_major, 0), NULL, kp->config_name);
- return ret;
}
-static int gpio_key_probe(struct platform_device *pdev)
+static int meson_gpio_kp_probe(struct platform_device *pdev)
{
- const char *str;
- struct kp *kp;
- struct input_dev *input_dev;
- int i, ret, key_size;
- struct gpio_key *key;
- struct gpio_platform_data *pdata = NULL;
struct gpio_desc *desc;
- int *key_param = NULL;
- int state = -EINVAL;
-#ifdef USE_IRQ
- int irq_keyup;
- int irq_keydown;
-#endif
-
- int gpio_highz = 0;
-
- if (!pdev->dev.of_node) {
- dev_info(&pdev->dev, "gpio_key: pdev->dev.of_node == NULL!\n");
- state = -EINVAL;
- goto get_key_node_fail;
- }
- ret = of_property_read_u32(pdev->dev.of_node, "key_num", &key_size);
- if (ret) {
- dev_info(&pdev->dev, "gpio_key: failed to get key_num!\n");
- state = -EINVAL;
- goto get_key_node_fail;
- }
+ int ret, i;
+ struct input_dev *input_dev;
+ struct gpio_keypad *keypad;
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- state = -EINVAL;
- goto get_key_node_fail;
+ if (!(pdev->dev.of_node)) {
+ dev_err(&pdev->dev,
+ "pdev->dev.of_node == NULL!\n");
+ return -EINVAL;
}
-
- ret = of_property_read_bool(pdev->dev.of_node, "gpio_high_z");
+ keypad = devm_kzalloc(&pdev->dev,
+ sizeof(struct gpio_keypad), GFP_KERNEL);
+ if (!keypad)
+ return -EINVAL;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "detect_mode", &(keypad->use_irq));
+ if (ret)
+ //The default mode is polling.
+ keypad->use_irq = DEFAULT_POLL_MODE;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "scan_period", &(keypad->scan_period));
+ if (ret)
+ //The default scan period is 20.
+ keypad->scan_period = DEFAULT_SCAN_PERION;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "key_num", &(keypad->key_size));
if (ret) {
- gpio_highz = 1;
- dev_info(&pdev->dev, "gpio request set to High-Z status\n");
- }
-
- pdata->key = kcalloc(key_size, sizeof(*(pdata->key)), GFP_KERNEL);
- if (!(pdata->key)) {
- dev_err(&pdev->dev, "platform key is required!\n");
- goto get_key_mem_fail;
+ dev_err(&pdev->dev,
+ "failed to get key_num!\n");
+ return -EINVAL;
}
-
- pdata->key_num = key_size;
- for (i = 0; i < key_size; i++) {
- ret = of_property_read_string_index(pdev->dev.of_node,
- "key_name", i, &(pdata->key[i].name));
+ keypad->key = devm_kzalloc(&pdev->dev,
+ (keypad->key_size)*sizeof(*(keypad->key)), GFP_KERNEL);
+ if (!(keypad->key))
+ return -EINVAL;
+ for (i = 0; i < keypad->key_size; i++) {
+ //get all gpio desc.
+ desc = devm_gpiod_get_index(&pdev->dev, "key", i, GPIOD_IN);
+ if (!desc)
+ return -EINVAL;
+ keypad->key[i].desc = desc;
+ //The gpio default is high level.
+ keypad->key[i].current_status = 1;
+ ret = of_property_read_u32_index(pdev->dev.of_node,
+ "key_code", i, &(keypad->key[i].code));
if (ret < 0) {
- dev_info(&pdev->dev,
- "gpio_key: find key_name=%d finished\n", i);
- break;
+ dev_err(&pdev->dev,
+ "find key_code=%d finished\n", i);
+ return -EINVAL;
}
- }
- key_param = kzalloc((sizeof(*key_param))*(pdata->key_num), GFP_KERNEL);
- if (!key_param)
- goto get_param_mem_fail;
- ret = of_property_read_u32_array(pdev->dev.of_node,
- "key_code", key_param, pdata->key_num);
- if (ret) {
- dev_info(&pdev->dev, "gpio_key: failed to get key_code!\n");
- goto get_key_param_failed;
- }
-
- for (i = 0; i < pdata->key_num; i++) {
- pdata->key[i].code = *(key_param+i);
- pdata->key[i].status = -1;
- }
-
-#ifdef USE_IRQ
- pdata->irq_keyup = irq_of_parse_and_map(pdev->dev.of_node, 0);
- pdata->irq_keydown = irq_of_parse_and_map(pdev->dev.of_node, 1);
- pdata->irq_keyup = irq_keyup;
- pdata->irq_keydown = irq_keydown;
- pr_info("irq_keyup:%d,irq_keydown:%d\n",
- pdata->irq_keyup, pdata->irq_keydown);
-#endif
-
- for (i = 0; i < key_size; i++) {
ret = of_property_read_string_index(pdev->dev.of_node,
- "key_pin", i, &str);
- dev_info(&pdev->dev, "gpio_key: %d name(%s) pin(%s)\n",
- i, (pdata->key[i].name), str);
- if (ret < 0) {
- dev_info(&pdev->dev,
- "gpio_key: find key_name=%d finished\n", i);
- break;
- }
- ret = amlogic_gpio_name_map_num(pdev, str);
- dev_info(&pdev->dev, "amlogic_gpio_name_map_num pin %d!%s::\n",
- ret, str);
+ "key_name", i, &(keypad->key[i].name));
if (ret < 0) {
- dev_info(&pdev->dev, "gpio_key bad pin !\n");
- goto get_key_param_failed;
+ dev_err(&pdev->dev,
+ "find key_name=%d finished\n", i);
+ return -EINVAL;
}
- desc = of_get_named_gpiod_flags(pdev->dev.of_node,
- "key_pin", i, NULL);
- pdata->key[i].pin = desc_to_gpio(desc);
- dev_info(&pdev->dev, "gpio_key: %d %s(%d)\n", i,
- (pdata->key[i].name), pdata->key[i].pin);
- /*pdata->key[i].pin = ret;*/
- gpio_request(pdata->key[i].pin, MOD_NAME);
- if (!gpio_highz) {
- gpio_direction_input(pdata->key[i].pin);
- gpiod_set_pull(desc, GPIOD_PULL_UP);
- }
-#ifdef USE_IRQ
- gpio_for_irq(pdata->key[i].pin,
- AML_GPIO_IRQ(irq_keyup, FILTER_NUM7, IRQF_TRIGGER_RISING));
- gpio_for_irq(pdata->key[i].pin,
- AML_GPIO_IRQ(irq_keydown, FILTER_NUM7, IRQF_TRIGGER_FALLING));
-#endif
+ gpiod_direction_input(keypad->key[i].desc);
+ gpiod_set_pull(keypad->key[i].desc, GPIOD_PULL_UP);
}
-
- kp = kzalloc(sizeof(struct kp), GFP_KERNEL);
+ //input
input_dev = input_allocate_device();
- if (!kp || !input_dev) {
- kfree(kp);
- input_free_device(input_dev);
- state = -ENOMEM;
- goto get_key_param_failed;
- }
- gp_kp = kp;
- platform_set_drvdata(pdev, pdata);
- kp->input = input_dev;
- INIT_WORK(&(kp->work_update), update_work_func);
-#ifdef USE_IRQ
- if (request_irq(irq_keyup, kp_isr, IRQF_DISABLED,
- "irq_keyup", kp)) {
- dev_info(&pdev->dev, "Failed to request gpio key up irq.\n");
- kfree(kp);
- input_free_device(input_dev);
- state = -EINVAL;
- goto get_key_param_failed;
- }
-
- if (request_irq(irq_keydown, kp_isr, IRQF_DISABLED,
- "irq_keydown", kp)) {
- dev_info(&pdev->dev, "Failed to request gpio key down irq.\n");
- kfree(kp);
- input_free_device(input_dev);
- state = -EINVAL;
- goto get_key_param_failed;
- }
-#else
- dev_info(&pdev->dev, "start setup_timer");
- setup_timer(&kp->timer, kp_timer_sr, (unsigned long) kp);
- mod_timer(&kp->timer, jiffies+msecs_to_jiffies(100));
-#endif
- /* setup input device */
+ if (!input_dev)
+ return -EINVAL;
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
- kp->keys = pdata->key;
- kp->key_num = pdata->key_num;
- key = pdata->key;
-
- for (i = 0; i < kp->key_num; i++) {
- set_bit(pdata->key[i].code, input_dev->keybit);
- dev_info(&pdev->dev, "%s key(%d) registed.\n",
- key->name, pdata->key[i].code);
- }
input_dev->name = "gpio_keypad";
input_dev->phys = "gpio_keypad/input0";
input_dev->dev.parent = &pdev->dev;
-
input_dev->id.bustype = BUS_ISA;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
-
input_dev->rep[REP_DELAY] = 0xffffffff;
input_dev->rep[REP_PERIOD] = 0xffffffff;
-
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = 0x1ff;
- ret = input_register_device(kp->input);
+ keypad->input_dev = input_dev;
+ ret = input_register_device(keypad->input_dev);
if (ret < 0) {
- dev_err(&pdev->dev, "Unable to register keypad input device.\n");
- kfree(kp);
- input_free_device(input_dev);
- state = -EINVAL;
- goto get_key_param_failed;
+ input_free_device(keypad->input_dev);
+ return -EINVAL;
+ }
+ platform_set_drvdata(pdev, keypad);
+ keypad->count = 0;
+ keypad->index = -1;
+ setup_timer(&(keypad->polling_timer),
+ polling_timer_handler, (unsigned long) keypad);
+ if (keypad->use_irq) {
+ for (i = 0; i < keypad->key_size; i++) {
+ keypad->key[i].irq_num =
+ gpiod_to_irq(keypad->key[i].desc);
+ ret = devm_request_irq(&pdev->dev,
+ keypad->key[i].irq_num,
+ gpio_irq_handler, IRQF_TRIGGER_FALLING,
+ "gpio_keypad", keypad);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Requesting irq failed!\n");
+ input_free_device(keypad->input_dev);
+ del_timer(&(keypad->polling_timer));
+ return -EINVAL;
+ }
+ }
+ } else {
+ mod_timer(&(keypad->polling_timer),
+ jiffies+msecs_to_jiffies(keypad->scan_period));
}
-/*set_pwr_key();*/
- dev_info(&pdev->dev, "gpio keypad register input device completed.\n");
- register_keypad_dev(gp_kp);
- kfree(key_param);
return 0;
-
-get_key_param_failed:
- kfree(key_param);
-get_param_mem_fail:
- kfree(pdata->key);
-get_key_mem_fail:
- kfree(pdata);
-get_key_node_fail:
- return state;
}
-static int gpio_key_remove(struct platform_device *pdev)
+static int meson_gpio_kp_remove(struct platform_device *pdev)
{
- struct gpio_platform_data *pdata = platform_get_drvdata(pdev);
- struct kp *kp = gp_kp;
+ struct gpio_keypad *keypad;
- input_unregister_device(kp->input);
- input_free_device(kp->input);
- unregister_chrdev(kp->config_major, kp->config_name);
- if (kp->config_class) {
- if (kp->config_dev)
- device_destroy(kp->config_class,
- MKDEV(kp->config_major, 0));
- class_destroy(kp->config_class);
- }
- kfree(kp);
-#ifdef CONFIG_OF
- kfree(pdata->key);
- kfree(pdata);
-#endif
- gp_kp = NULL;
+ keypad = platform_get_drvdata(pdev);
+ input_unregister_device(keypad->input_dev);
+ input_free_device(keypad->input_dev);
+ del_timer(&(keypad->polling_timer));
return 0;
}
-static int gpio_key_suspend(struct platform_device *dev, pm_message_t state)
+static const struct of_device_id key_dt_match[] = {
+ { .compatible = "amlogic, gpio_keypad", },
+ {},
+};
+
+static int meson_gpio_kp_suspend(struct platform_device *dev,
+ pm_message_t state)
{
+ int i;
+ struct gpio_keypad *pdata;
+
+ pdata = (struct gpio_keypad *)platform_get_drvdata(dev);
+ if (pdata->use_irq) {
+ for (i = 0; i < pdata->key_size; i++)
+ disable_irq_nosync(pdata->key[i].irq_num);
+ } else
+ del_timer(&(pdata->polling_timer));
return 0;
}
-static int gpio_key_resume(struct platform_device *dev)
+static int meson_gpio_kp_resume(struct platform_device *dev)
{
int i;
- struct gpio_platform_data *pdata;
-
- pdata = (struct gpio_platform_data *)platform_get_drvdata(dev);
-
+ struct gpio_keypad *pdata;
+
+ pdata = (struct gpio_keypad *)platform_get_drvdata(dev);
+ if (pdata->use_irq) {
+ for (i = 0; i < pdata->key_size; i++)
+ enable_irq(pdata->key[i].irq_num);
+ } else
+ mod_timer(&(pdata->polling_timer),
+ jiffies+msecs_to_jiffies(5));
if (get_resume_method() == POWER_KEY_WAKEUP) {
- for (i = 0; i < pdata->key_num; i++) {
+ for (i = 0; i < pdata->key_size; i++) {
if (pdata->key[i].code == KEY_POWER) {
pr_info("gpio keypad wakeup\n");
-
- input_report_key(gp_kp->input, KEY_POWER, 1);
- input_sync(gp_kp->input);
- input_report_key(gp_kp->input, KEY_POWER, 0);
- input_sync(gp_kp->input);
-
+ input_report_key(pdata->input_dev,
+ KEY_POWER, 1);
+ input_sync(pdata->input_dev);
+ input_report_key(pdata->input_dev,
+ KEY_POWER, 0);
+ input_sync(pdata->input_dev);
break;
}
}
return 0;
}
-#ifdef CONFIG_OF
-static const struct of_device_id key_dt_match[] = {
- { .compatible = "amlogic, gpio_keypad", },
- {},
-};
-#else
-#define key_dt_match NULL
-#endif
-
-static struct platform_driver gpio_driver = {
- .probe = gpio_key_probe,
- .remove = gpio_key_remove,
- .suspend = gpio_key_suspend,
- .resume = gpio_key_resume,
+static struct platform_driver meson_gpio_kp_driver = {
+ .probe = meson_gpio_kp_probe,
+ .remove = meson_gpio_kp_remove,
+ .suspend = meson_gpio_kp_suspend,
+ .resume = meson_gpio_kp_resume,
.driver = {
- .name = "gpio-key",
+ .name = "gpio-keypad",
.of_match_table = key_dt_match,
},
};
-static int __init gpio_key_init(void)
-{
- return platform_driver_register(&gpio_driver);
-}
-
-static void __exit gpio_key_exit(void)
-{
- platform_driver_unregister(&gpio_driver);
-}
-
-module_init(gpio_key_init);
-module_exit(gpio_key_exit);
-
-MODULE_AUTHOR("Frank Chen");
+module_platform_driver(meson_gpio_kp_driver);
+MODULE_AUTHOR("Amlogic");
MODULE_DESCRIPTION("GPIO Keypad Driver");
MODULE_LICENSE("GPL");
-
--- /dev/null
+/*
+ * drivers/amlogic/input/keyboard/gpio_keypad.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the 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 warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+
+#include <asm/irq.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <uapi/linux/input.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/amlogic/gpio-amlogic.h>
+#include <linux/amlogic/sd.h>
+#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/cpu_version.h>
+#include <linux/amlogic/pm.h>
+#include <linux/of_address.h>
+#include "../../../gpio/gpiolib.h"
+
+#define OFFSET 24
+
+#define MOD_NAME "gpio_key"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "gpio_key: " fmt
+
+struct gpio_key {
+ int code; /* input key code */
+ const char *name;
+ int pin; /*pin number*/
+ int status; /*0 up , 1 down*/
+};
+
+struct gpio_platform_data {
+ struct gpio_key *key;
+ int key_num;
+ int repeat_delay;
+ int repeat_period;
+ int irq_keyup;
+ int irq_keydown;
+};
+
+struct kp {
+ struct input_dev *input;
+ struct timer_list timer;
+ int config_major;
+ char config_name[20];
+ struct class *config_class;
+ struct device *config_dev;
+ struct gpio_key *keys;
+ int key_num;
+ struct work_struct work_update;
+};
+
+static struct kp *gp_kp;
+
+int amlogic_gpio_name_map_num(struct platform_device *pdev, const char *value)
+{
+ return of_property_read_string(pdev->dev.of_node, "key_pin", &value);
+}
+
+
+static void kp_work(struct kp *kp)
+{
+ struct gpio_key *key;
+ int i;
+ int io_status;
+
+ key = kp->keys;
+ for (i = 0; i < kp->key_num; i++) {
+ io_status = gpiod_get_value(gpio_to_desc(key->pin));
+ if (io_status != key->status) {
+ if (io_status) {
+ dev_info(&kp->input->dev,
+ "key %d up\n", key->code);
+ input_report_key(kp->input, key->code, 0);
+ input_sync(kp->input);
+ } else {
+ dev_info(&kp->input->dev,
+ "key %d down\n", key->code);
+ input_report_key(kp->input, key->code, 1);
+ input_sync(kp->input);
+ }
+ key->status = io_status;
+ }
+ key++;
+ }
+}
+
+static void update_work_func(struct work_struct *work)
+{
+ struct kp *kp_data = container_of(work, struct kp, work_update);
+
+ kp_work(kp_data);
+}
+
+/*
+ * What we do here is just for loss wakeup key when suspend.
+ * In suspend routine , the intr is disable.
+ * we need do more things to adapt the gpio change.
+ */
+int det_pwr_key(void)
+{
+ /*return aml_read_reg32(P_AO_IRQ_STAT) & (1<<8);*/
+ return 0;
+}
+/*Enable gpio interrupt for AO domain interrupt*/
+void set_pwr_key(void)
+{
+ /* aml_write_reg32(P_AO_IRQ_GPIO_REG,
+ * aml_read_reg32(P_AO_IRQ_GPIO_REG)|(1<<18)|(1<<16)|(0x3<<0));
+ * aml_write_reg32(P_AO_IRQ_MASK_FIQ_SEL,
+ * aml_read_reg32(P_AO_IRQ_MASK_FIQ_SEL)|(1<<8));
+ * aml_set_reg32_mask(P_AO_IRQ_STAT_CLR, 1<<8);
+ */
+}
+
+void clr_pwr_key(void)
+{
+ /*aml_set_reg32_mask(P_AO_IRQ_STAT_CLR, 1<<8);*/
+}
+
+
+#ifdef USE_IRQ
+
+static irqreturn_t kp_isr(int irq, void *data)
+{
+ struct kp *kp_data = (struct kp *)data;
+
+ queue_work(system_freezable_wq, &(kp_data->work_update));
+
+ /* if (!deep_suspend_flag)
+ * clr_pwr_key();
+ */
+ return IRQ_HANDLED;
+}
+
+#else
+void kp_timer_sr(unsigned long data)
+{
+ struct kp *kp_data = (struct kp *)data;
+
+ queue_work(system_freezable_wq, &(kp_data->work_update));
+ mod_timer(&kp_data->timer, jiffies+msecs_to_jiffies(25));
+}
+#endif
+static int gpio_key_config_open(struct inode *inode, struct file *file)
+{
+ file->private_data = gp_kp;
+ return 0;
+}
+
+static int gpio_key_config_release(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return 0;
+}
+
+static const struct file_operations keypad_fops = {
+ .owner = THIS_MODULE,
+ .open = gpio_key_config_open,
+ .release = gpio_key_config_release,
+};
+
+static int register_keypad_dev(struct kp *kp)
+{
+ int ret = 0;
+ char name[] = "gpio_keyboard";
+
+ strncpy(kp->config_name, name, sizeof(name));
+ ret = register_chrdev(0, kp->config_name, &keypad_fops);
+ if (ret <= 0) {
+ dev_info(&kp->input->dev, "register char device error\n");
+ return ret;
+ }
+ kp->config_major = ret;
+ dev_info(&kp->input->dev, "gpio keypad major:%d\n", ret);
+ kp->config_class = class_create(THIS_MODULE, kp->config_name);
+ kp->config_dev = device_create(kp->config_class, NULL,
+ MKDEV(kp->config_major, 0), NULL, kp->config_name);
+ return ret;
+}
+
+static int gpio_key_probe(struct platform_device *pdev)
+{
+ const char *str;
+ struct kp *kp;
+ struct input_dev *input_dev;
+ int i, ret, key_size;
+ struct gpio_key *key;
+ struct gpio_platform_data *pdata = NULL;
+ struct gpio_desc *desc;
+ int *key_param = NULL;
+ int state = -EINVAL;
+#ifdef USE_IRQ
+ int irq_keyup;
+ int irq_keydown;
+#endif
+
+ int gpio_highz = 0;
+
+ if (!pdev->dev.of_node) {
+ dev_info(&pdev->dev, "gpio_key: pdev->dev.of_node == NULL!\n");
+ state = -EINVAL;
+ goto get_key_node_fail;
+ }
+ ret = of_property_read_u32(pdev->dev.of_node, "key_num", &key_size);
+ if (ret) {
+ dev_info(&pdev->dev, "gpio_key: failed to get key_num!\n");
+ state = -EINVAL;
+ goto get_key_node_fail;
+ }
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ state = -EINVAL;
+ goto get_key_node_fail;
+ }
+
+ ret = of_property_read_bool(pdev->dev.of_node, "gpio_high_z");
+ if (ret) {
+ gpio_highz = 1;
+ dev_info(&pdev->dev, "gpio request set to High-Z status\n");
+ }
+
+ pdata->key = kcalloc(key_size, sizeof(*(pdata->key)), GFP_KERNEL);
+ if (!(pdata->key)) {
+ dev_err(&pdev->dev, "platform key is required!\n");
+ goto get_key_mem_fail;
+ }
+
+ pdata->key_num = key_size;
+ for (i = 0; i < key_size; i++) {
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "key_name", i, &(pdata->key[i].name));
+ if (ret < 0) {
+ dev_info(&pdev->dev,
+ "gpio_key: find key_name=%d finished\n", i);
+ break;
+ }
+ }
+ key_param = kzalloc((sizeof(*key_param))*(pdata->key_num), GFP_KERNEL);
+ if (!key_param)
+ goto get_param_mem_fail;
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "key_code", key_param, pdata->key_num);
+ if (ret) {
+ dev_info(&pdev->dev, "gpio_key: failed to get key_code!\n");
+ goto get_key_param_failed;
+ }
+
+ for (i = 0; i < pdata->key_num; i++) {
+ pdata->key[i].code = *(key_param+i);
+ pdata->key[i].status = -1;
+ }
+
+#ifdef USE_IRQ
+ pdata->irq_keyup = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ pdata->irq_keydown = irq_of_parse_and_map(pdev->dev.of_node, 1);
+ pdata->irq_keyup = irq_keyup;
+ pdata->irq_keydown = irq_keydown;
+ pr_info("irq_keyup:%d,irq_keydown:%d\n",
+ pdata->irq_keyup, pdata->irq_keydown);
+#endif
+
+ for (i = 0; i < key_size; i++) {
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "key_pin", i, &str);
+ dev_info(&pdev->dev, "gpio_key: %d name(%s) pin(%s)\n",
+ i, (pdata->key[i].name), str);
+ if (ret < 0) {
+ dev_info(&pdev->dev,
+ "gpio_key: find key_name=%d finished\n", i);
+ break;
+ }
+ ret = amlogic_gpio_name_map_num(pdev, str);
+ dev_info(&pdev->dev, "amlogic_gpio_name_map_num pin %d!%s::\n",
+ ret, str);
+ if (ret < 0) {
+ dev_info(&pdev->dev, "gpio_key bad pin !\n");
+ goto get_key_param_failed;
+ }
+ desc = of_get_named_gpiod_flags(pdev->dev.of_node,
+ "key_pin", i, NULL);
+ pdata->key[i].pin = desc_to_gpio(desc);
+ dev_info(&pdev->dev, "gpio_key: %d %s(%d)\n", i,
+ (pdata->key[i].name), pdata->key[i].pin);
+ /*pdata->key[i].pin = ret;*/
+ gpio_request(pdata->key[i].pin, MOD_NAME);
+ if (!gpio_highz) {
+ gpio_direction_input(pdata->key[i].pin);
+ gpiod_set_pull(desc, GPIOD_PULL_UP);
+ }
+#ifdef USE_IRQ
+ gpio_for_irq(pdata->key[i].pin,
+ AML_GPIO_IRQ(irq_keyup, FILTER_NUM7, IRQF_TRIGGER_RISING));
+ gpio_for_irq(pdata->key[i].pin,
+ AML_GPIO_IRQ(irq_keydown, FILTER_NUM7, IRQF_TRIGGER_FALLING));
+#endif
+ }
+
+ kp = kzalloc(sizeof(struct kp), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!kp || !input_dev) {
+ kfree(kp);
+ input_free_device(input_dev);
+ state = -ENOMEM;
+ goto get_key_param_failed;
+ }
+ gp_kp = kp;
+ platform_set_drvdata(pdev, pdata);
+ kp->input = input_dev;
+ INIT_WORK(&(kp->work_update), update_work_func);
+#ifdef USE_IRQ
+ if (request_irq(irq_keyup, kp_isr, IRQF_DISABLED,
+ "irq_keyup", kp)) {
+ dev_info(&pdev->dev, "Failed to request gpio key up irq.\n");
+ kfree(kp);
+ input_free_device(input_dev);
+ state = -EINVAL;
+ goto get_key_param_failed;
+ }
+
+ if (request_irq(irq_keydown, kp_isr, IRQF_DISABLED,
+ "irq_keydown", kp)) {
+ dev_info(&pdev->dev, "Failed to request gpio key down irq.\n");
+ kfree(kp);
+ input_free_device(input_dev);
+ state = -EINVAL;
+ goto get_key_param_failed;
+ }
+#else
+ dev_info(&pdev->dev, "start setup_timer");
+ setup_timer(&kp->timer, kp_timer_sr, (unsigned long) kp);
+ mod_timer(&kp->timer, jiffies+msecs_to_jiffies(100));
+#endif
+ /* setup input device */
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(EV_REP, input_dev->evbit);
+ kp->keys = pdata->key;
+ kp->key_num = pdata->key_num;
+ key = pdata->key;
+
+ for (i = 0; i < kp->key_num; i++) {
+ set_bit(pdata->key[i].code, input_dev->keybit);
+ dev_info(&pdev->dev, "%s key(%d) registed.\n",
+ key->name, pdata->key[i].code);
+ }
+ input_dev->name = "gpio_keypad";
+ input_dev->phys = "gpio_keypad/input0";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->id.bustype = BUS_ISA;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+
+ input_dev->rep[REP_DELAY] = 0xffffffff;
+ input_dev->rep[REP_PERIOD] = 0xffffffff;
+
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = 0x1ff;
+ ret = input_register_device(kp->input);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to register keypad input device.\n");
+ kfree(kp);
+ input_free_device(input_dev);
+ state = -EINVAL;
+ goto get_key_param_failed;
+ }
+/*set_pwr_key();*/
+ dev_info(&pdev->dev, "gpio keypad register input device completed.\n");
+ register_keypad_dev(gp_kp);
+ kfree(key_param);
+ return 0;
+
+get_key_param_failed:
+ kfree(key_param);
+get_param_mem_fail:
+ kfree(pdata->key);
+get_key_mem_fail:
+ kfree(pdata);
+get_key_node_fail:
+ return state;
+}
+
+static int gpio_key_remove(struct platform_device *pdev)
+{
+ struct gpio_platform_data *pdata = platform_get_drvdata(pdev);
+ struct kp *kp = gp_kp;
+
+ input_unregister_device(kp->input);
+ input_free_device(kp->input);
+ unregister_chrdev(kp->config_major, kp->config_name);
+ if (kp->config_class) {
+ if (kp->config_dev)
+ device_destroy(kp->config_class,
+ MKDEV(kp->config_major, 0));
+ class_destroy(kp->config_class);
+ }
+ kfree(kp);
+#ifdef CONFIG_OF
+ kfree(pdata->key);
+ kfree(pdata);
+#endif
+ gp_kp = NULL;
+ return 0;
+}
+
+static int gpio_key_suspend(struct platform_device *dev, pm_message_t state)
+{
+ return 0;
+}
+
+static int gpio_key_resume(struct platform_device *dev)
+{
+ int i;
+ struct gpio_platform_data *pdata;
+
+ pdata = (struct gpio_platform_data *)platform_get_drvdata(dev);
+
+ if (get_resume_method() == POWER_KEY_WAKEUP) {
+ for (i = 0; i < pdata->key_num; i++) {
+ if (pdata->key[i].code == KEY_POWER) {
+ pr_info("gpio keypad wakeup\n");
+
+ input_report_key(gp_kp->input, KEY_POWER, 1);
+ input_sync(gp_kp->input);
+ input_report_key(gp_kp->input, KEY_POWER, 0);
+ input_sync(gp_kp->input);
+
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id key_dt_match[] = {
+ { .compatible = "amlogic, gpio_keypad", },
+ {},
+};
+#else
+#define key_dt_match NULL
+#endif
+
+static struct platform_driver gpio_driver = {
+ .probe = gpio_key_probe,
+ .remove = gpio_key_remove,
+ .suspend = gpio_key_suspend,
+ .resume = gpio_key_resume,
+ .driver = {
+ .name = "gpio-key",
+ .of_match_table = key_dt_match,
+ },
+};
+
+static int __init gpio_key_init(void)
+{
+ return platform_driver_register(&gpio_driver);
+}
+
+static void __exit gpio_key_exit(void)
+{
+ platform_driver_unregister(&gpio_driver);
+}
+
+module_init(gpio_key_init);
+module_exit(gpio_key_exit);
+
+MODULE_AUTHOR("Frank Chen");
+MODULE_DESCRIPTION("GPIO Keypad Driver");
+MODULE_LICENSE("GPL");
+