aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-04-07 10:36:54 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-04-07 10:36:54 +0800
commitd84e43b809fd405375c873b614e21d2064b5bdaf (patch)
treea9294e8660550d0e3f7cbaad6a752a790e8d7be1
parentf08c1e22ab4cd9f25ed8c011b4780327fc0ce615 (diff)
parentd7244794b7983eda774bef24b190a61040e8933f (diff)
Merge remote-tracking branch 'origin/topic-keypad' into m2-candidate-0407
-rw-r--r--arch/arm/boot/dts/hi3620.dtsi7
-rw-r--r--arch/arm/boot/dts/hi4511.dts16
-rw-r--r--arch/arm/configs/hs_defconfig1
-rw-r--r--drivers/input/keyboard/Kconfig9
-rw-r--r--drivers/input/keyboard/Makefile1
-rwxr-xr-xdrivers/input/keyboard/k3_keypad.c653
-rw-r--r--drivers/input/keyboard/k3_keypad.h47
7 files changed, 724 insertions, 10 deletions
diff --git a/arch/arm/boot/dts/hi3620.dtsi b/arch/arm/boot/dts/hi3620.dtsi
index 78aaeda5f18..bf20687c76d 100644
--- a/arch/arm/boot/dts/hi3620.dtsi
+++ b/arch/arm/boot/dts/hi3620.dtsi
@@ -1340,5 +1340,12 @@
#size-cells = <0>;
clocks = <&clk_mmc3>;
};
+ kpc: kpc@fc805000 {
+ compatible = "hisilicon,k3_keypad";
+ reg = <0xfc805000 0x1000>;
+ interrupts = <0 10 4>;
+ clocks = <&clk_kpc>;
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/hi4511.dts b/arch/arm/boot/dts/hi4511.dts
index aad79d01119..a124213bde0 100644
--- a/arch/arm/boot/dts/hi4511.dts
+++ b/arch/arm/boot/dts/hi4511.dts
@@ -103,6 +103,12 @@
status = "ok";
};
+ kpc: kpc@fc805000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&kpc_pmx_func &kpc_cfg_func>;
+ status = "ok";
+ };
+
gpio0: gpio@fc806000 {
status = "ok";
};
@@ -366,16 +372,6 @@
0x114 0x0 /* KEY_OUT2 (IOMG67) */
>;
};
- kpc_pmx_idle: pinmux_kpc_pins@1 {
- pinctrl-single,pins = <
- 0x12c 0x1 /* GPIO (IOMG73) */
- 0x130 0x1 /* GPIO (IOMG74) */
- 0x134 0x1 /* GPIO (IOMG75) */
- 0x10c 0x1 /* GPIO (IOMG65) */
- 0x110 0x1 /* GPIO (IOMG66) */
- 0x114 0x1 /* GPIO (IOMG67) */
- >;
- };
gpio_key_func: pinmux_gpiokey_pins {
pinctrl-single,pins = <
0x10c 0x1 /* KEY_OUT0/GPIO (IOMG65) */
diff --git a/arch/arm/configs/hs_defconfig b/arch/arm/configs/hs_defconfig
index 3eb2cb11244..94cdfd4807e 100644
--- a/arch/arm/configs/hs_defconfig
+++ b/arch/arm/configs/hs_defconfig
@@ -860,6 +860,7 @@ CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_KEYBOARD=y
# CONFIG_KEYBOARD_ADP5588 is not set
# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_K3V200 is not set
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_QT1070 is not set
# CONFIG_KEYBOARD_QT2160 is not set
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 5a240c60342..f8889ee2d61 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -618,4 +618,13 @@ config KEYBOARD_W90P910
To compile this driver as a module, choose M here: the
module will be called w90p910_keypad.
+config KEYBOARD_K3V200
+ tristate "K3V200 Matrix Keypad support"
+ depends on ARCH_HS
+ select INPUT_MATRIXKMAP
+ help
+ Say Y here to enable the matrix keypad support for k3v200 platform.
+
+ To compile this driver as a module, choose M here: the
+ module will be called k3_keypad.
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 44e76002f54..635d2049da1 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -54,3 +54,4 @@ obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
+obj-$(CONFIG_KEYBOARD_K3V200) += k3_keypad.o
diff --git a/drivers/input/keyboard/k3_keypad.c b/drivers/input/keyboard/k3_keypad.c
new file mode 100755
index 00000000000..03c6debb947
--- /dev/null
+++ b/drivers/input/keyboard/k3_keypad.c
@@ -0,0 +1,653 @@
+/* kernel/drivers/input/keyboard/k3_keypad.c
+ *
+ * K3 Keypad Driver
+ * Copyright (C) 2011 Hisilicon
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <asm/irq.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of.h>
+#include "k3_keypad.h"
+#include <linux/input/matrix_keypad.h>
+
+/*The switch to support long-press key and combo key*/
+#define ADVANCED_FUNCTION_SUPPORT
+
+#define KPC_MAX_ROWS (8)
+#define KPC_MAX_COLS (8)
+#define KEY_RELEASE (0)
+#define KEY_PRESS (1)
+#define KPC_BIT_PER_KEY (6)
+#define KPC_BIT_PER_KEYROW (3)
+
+/*KPC clock frequency*/
+#define KPC_CLK_RATE (32768)
+
+/*KPC Register Offset*/
+#define KPC_CONTROL_OFFSET (0x000)
+#define KPC_INTERVAL_SHORT_OFFSET (0x004)
+#define KPC_INT_STATUS_OFFSET (0x01C)
+#define KPC_INT_CLR_OFFSET (0x058)
+#define KPC_RELEASE_INT_CLR_OFFSET (0x06C)
+
+/*BITMASK in KPC_INT_STATUS REG */
+#define KEY_NUM_BITMASK (0x3C0000)
+#define KEY_NUM_BITPOS (18)
+#define KEY_VALUE_BITMASK (0xFFF)
+#define KEY_VALUE_BITPOS (0)
+#define KPC_RELEASE_INT_BITMASK (0x40000000)
+#define KPC_RELEASE_INT_BITPOS (30)
+
+/*REG config value */
+/* [8:0]: 0_0000_0011 */
+#define KPC_VAL_CTLREG (0x023)
+/* 120 x 250us = 30ms */
+#define KPC_VAL_SHORT_INTERVAL (0x28)
+
+struct k3v2_keypad {
+ struct input_dev *input_dev;
+ void __iomem *base;
+ int irq;
+ struct clk *clk;
+ int rows;
+ int cols;
+ int row_shift;
+ /* Used for keymap*/
+ unsigned short keycodes[KPC_MAX_ROWS * KPC_MAX_COLS];
+ /* Used for result of keypad scan*/
+ unsigned char scancode_state[KPC_MAX_ROWS];
+ /* Used for store all keycode state*/
+ uint16_t keycode_state[KPC_MAX_ROWS * 2];
+};
+
+
+static struct keypad_remap *keypad_long_remap;
+static struct keypad_remap_state *g_long_remap_state;
+
+static int k3v2_keypad_open(struct input_dev *dev)
+{
+ struct k3v2_keypad *keypad = input_get_drvdata(dev);
+ struct pinctrl *block = NULL;
+ int ret = 0;
+
+ if (keypad == NULL) {
+ dev_err(&dev->dev, "get invalid keypad pointer\n");
+ return -EINVAL;
+ }
+
+ block = devm_pinctrl_get_select_default(&dev->dev);
+ if (!block) {
+ dev_warn(&keypad->input_dev->dev, "Failed to get KPC GPIO BLOCK\n");
+ ret = -EINVAL;
+ }
+ /*Clean interrupt*/
+ writel(0x1, keypad->base + KPC_INT_CLR_OFFSET);
+ writel(0x1, keypad->base + KPC_RELEASE_INT_CLR_OFFSET);
+ /*config KPC_CONTROL REG*/
+ writel(KPC_VAL_CTLREG, keypad->base + KPC_CONTROL_OFFSET);
+ /*config KPC_INTERVAL_SHORT REG*/
+ writel(KPC_VAL_SHORT_INTERVAL,
+ keypad->base + KPC_INTERVAL_SHORT_OFFSET);
+
+ enable_irq(keypad->irq);
+ return ret;
+}
+
+static void k3v2_keypad_close(struct input_dev *dev)
+{
+ struct k3v2_keypad *keypad = input_get_drvdata(dev);
+
+ if (keypad == NULL) {
+ dev_err(&dev->dev, "get invalid keypad pointer\n");
+ return;
+ }
+
+ disable_irq(keypad->irq);
+}
+
+/* Update the new_keycode_state*/
+static void k3v2_keypad_update_keycode(struct k3v2_keypad *keypad,
+ unsigned char *new_scancode_state,
+ uint16_t *new_keycode_state)
+{
+ int row = 0;
+ int col = 0;
+ int r = 0;
+ int c = 0;
+ int index = 0;
+ uint8_t keycode = 0;
+
+ for (row = 0; row < KPC_MAX_ROWS; row++) {
+ for (col = 0; col < KPC_MAX_COLS; col++) {
+ if (new_scancode_state[row] & (1 << col)) {
+ index = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+ keycode = keypad->keycodes[index];
+ c = keycode & 0x0F;
+ r = keycode >> 4;
+ new_keycode_state[r] |= (1 << c);
+ }
+ }
+ }
+}
+
+/*Remap long-press func key or combo keys to target keys.*/
+static void k3v2_keypad_remap_keycode(struct k3v2_keypad *keypad,
+ struct keypad_remap_state *remap_state,
+ uint16_t *new_keycode_state)
+{
+ int i = 0;
+ int j = 0;
+
+ unsigned long current_time = jiffies_to_msecs(jiffies);
+
+ if (!remap_state)
+ return;
+
+ for (i = 0; i < keypad_long_remap->count; i++) {
+
+ int down_num = 0;
+ uint16_t keycode;
+ unsigned char key_down_state = 0;
+ const struct keypad_remap_item *item = &keypad_long_remap->items[i];
+ struct keypad_remap_state *state = &remap_state[i];
+
+ for (j = 0; j < item->keynum; j++) {
+ keycode = item->keycodes[j];
+ if (KEYPAD_CHECK_KEYCODE(new_keycode_state, keycode) != 0) {
+ key_down_state |= (1 << j);
+ down_num++;
+ }
+ }
+ /*the number of down keys are enough to remap.*/
+ if (down_num >= item->keynum) {
+ if (item->keynum > 1) {
+ /*clean all mapping keys in new_keycode_state*/
+ for (j = 0; j < item->keynum; j++) {
+ keycode = item->keycodes[j];
+ KEYPAD_CLR_KEYCODE(new_keycode_state, keycode);
+ }
+ /*set the remapped keycode*/
+ keycode = item->target_keycode;
+ KEYPAD_SET_KEYCODE(new_keycode_state, keycode);
+ state->pending = false;
+ state->remapped = true;
+ } else {
+ /*start pending period*/
+ if ((state->pending == false) && (state->remapped == false)) {
+ state->pending = true;
+ state->time = current_time + item->delay;
+ }
+ KEYPAD_CLR_KEYCODE(new_keycode_state, item->keycodes[0]);
+ /*if pending, then check if it is timeout*/
+ if (state->pending == true) {
+ /*it behinds timeout, then set the remapped keycode*/
+ if (current_time >= state->time) {
+ keycode = item->target_keycode;
+ KEYPAD_SET_KEYCODE(new_keycode_state, keycode);
+ state->pending = false;
+ state->remapped = true;
+ }
+ } else if (state->remapped == true) {
+ keycode = item->target_keycode;
+ KEYPAD_SET_KEYCODE(new_keycode_state, keycode);
+ }
+ }
+ }
+ /*keys down, but not enough number for combo keys*/
+ else if (down_num > 0) {
+ if ((state->remapped == true) || (state->pending == false)
+ || (current_time < state->time)) {
+
+ if ((state->pending == false) && (state->remapped == false)) {
+ state->pending = true;
+ state->time = current_time + item->delay;
+ }
+
+ for (j = 0; j < item->keynum; j++) {
+ keycode = item->keycodes[j];
+ KEYPAD_CLR_KEYCODE(new_keycode_state, keycode);
+ }
+ }
+ } else {
+ /*All keys are up.
+ *If pending, set the cleaned remapping keys back.
+ *Then call timer to report again.*/
+ if (state->pending) {
+ for (j = 0; j < item->keynum; j++) {
+ if (((state->down_state) & (1 << j)) != 0) {
+ keycode = item->keycodes[j];
+ input_report_key(keypad->input_dev, keycode, 1);
+ input_report_key(keypad->input_dev, keycode, 0);
+ input_sync(keypad->input_dev);
+ }
+ }
+ state->pending = false;
+ }
+ state->remapped = false;
+ }
+ /*save keys*/
+ state->down_state = key_down_state;
+ }
+}
+
+static void k3v2_keypad_report_keycode(struct k3v2_keypad *keypad, uint16_t *new_keycode_state)
+{
+ int row = 0;
+ int col = 0;
+ unsigned int keycode = 0;
+ unsigned int pressed = 0;
+ uint16_t changed = 0;
+
+ for (row = 0; row < KPC_MAX_ROWS * 2; row++) {
+ changed = keypad->keycode_state[row] ^ new_keycode_state[row];
+ if (0 == changed)
+ continue;
+ for (col = 0; col < KPC_MAX_COLS * 2; col++) {
+ if (changed & (1 << col)) {
+ keycode = (row << 4) | (col & 0x0F);
+ pressed = (new_keycode_state[row] & (1 << col)) >> col;
+ dev_dbg(&keypad->input_dev->dev,
+ "row = %d, col = %d, keycode = %d, press = %d\n", row, col, keycode, pressed);
+ input_report_key(keypad->input_dev, keycode, pressed);
+ }
+ }
+ }
+ input_sync(keypad->input_dev);
+}
+
+
+static irqreturn_t k3v2_keypad_irq_handler(int irq, void *dev_id)
+{
+ struct k3v2_keypad *keypad = dev_id;
+ unsigned int reg = 0;
+ unsigned int key_num = 0;
+ unsigned int key_val = 0;
+
+ int row = 0;
+ int col = 0;
+ int i = 0;
+#ifndef ADVANCED_FUNCTION_SUPPORT
+ unsigned int changed;
+ unsigned int pressed;
+ unsigned int val = 0;
+#endif /* #ifndef ADVANCED_FUNCTION_SUPPORT */
+ unsigned char new_scancode_state[ARRAY_SIZE(keypad->scancode_state)];
+ uint16_t new_keycode_state[ARRAY_SIZE(keypad->keycode_state)];
+
+ memset(new_scancode_state, 0, sizeof(new_scancode_state));
+ memset(new_keycode_state, 0, sizeof(new_keycode_state));
+
+ reg = readl(keypad->base + KPC_INT_STATUS_OFFSET);
+ key_num = (reg & KEY_NUM_BITMASK) >> KEY_NUM_BITPOS;
+ key_val = (reg & KEY_VALUE_BITMASK) >> KEY_VALUE_BITPOS;
+
+ for (i = 0; i < key_num; i++) {
+ row = (key_val >> (i*KPC_BIT_PER_KEY)) & 0x7;
+ col = (key_val >> (i*KPC_BIT_PER_KEY + KPC_BIT_PER_KEYROW)) & 0x7;
+ new_scancode_state[row] |= (1 << col);
+ }
+
+#ifndef ADVANCED_FUNCTION_SUPPORT
+ for (row = 0; row < keypad->rows; row++) {
+ changed = new_scancode_state[row] ^ keypad->scancode_state[row];
+ if (!changed)
+ continue;
+
+ for (col = 0; col < keypad->cols; col++) {
+ if (changed & (1 << col)) {
+ val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+ pressed = (new_scancode_state[row] & (1 << col)) >> col;
+ input_report_key(keypad->input_dev, keypad->keycodes[val], pressed);
+ dev_dbg(&keypad->input_dev->dev,
+ "key_num = %d, row = %d, col = %d, press = %d\n", key_num, row, col, pressed);
+ }
+ }
+ }
+ input_sync(keypad->input_dev);
+ memcpy(keypad->scancode_state, new_scancode_state, sizeof(new_scancode_state));
+#else
+ k3v2_keypad_update_keycode(keypad, new_scancode_state, new_keycode_state);
+ k3v2_keypad_remap_keycode(keypad, g_long_remap_state, new_keycode_state);
+ k3v2_keypad_report_keycode(keypad, new_keycode_state);
+ memcpy(keypad->scancode_state, new_scancode_state, sizeof(new_scancode_state));
+ memcpy(keypad->keycode_state, new_keycode_state, sizeof(new_keycode_state));
+#endif
+
+ /*Clean interrupt*/
+ writel(0x1, keypad->base + KPC_INT_CLR_OFFSET);
+ writel(0x1, keypad->base + KPC_RELEASE_INT_CLR_OFFSET);
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+/* Keypad device and platform data start, use KPC realizing keypad. */
+static const uint32_t default_keymap[] = {
+ /*row, col, key*/
+#if 0
+ /* used for truly platform.*/
+ KEY(0, 0, KEY_MENU),
+ KEY(1, 0, KEY_SEND),
+ KEY(2, 0, KEY_VOLUMEUP),
+
+ KEY(0, 1, KEY_HOME),
+ KEY(1, 1, KEY_END),
+ KEY(2, 1, KEY_VOLUMEDOWN),
+
+ KEY(0, 2, KEY_CAMERA_FOCUS),
+ KEY(1, 2, KEY_CAMERA),
+ KEY(2, 2, DPAD_CENTER),
+#endif
+
+ /*row, col, key*/
+ /* used for debug only.*/
+ KEY(0, 0, KEY_MENU),
+ KEY(0, 1, KEY_BACK),
+
+ KEY(1, 0, KEY_LEFT),
+ KEY(1, 1, KEY_RIGHT),
+
+ KEY(2, 0, KEY_UP),
+
+ KEY(2, 1, KEY_DOWN),
+ KEY(2, 2, DPAD_CENTER),
+
+ KEY(0, 2, KEY_CAMERA_FOCUS),
+ KEY(1, 2, KEY_CAMERA),
+
+ /* TODO: add your keys below*/
+
+ /*Used for software function, not physical connection!*/
+
+};
+
+static struct matrix_keymap_data hisik3_keymap_data = {
+ .keymap = default_keymap,
+ .keymap_size = ARRAY_SIZE(default_keymap),
+};
+static uint16_t long_func_key1[] = {KEY_BACK};
+/*static uint16_t long_func_key2[] = {DPAD_CENTER, KEY_VOLUMEDOWN};*/
+
+static struct keypad_remap_item remap_items[] = {
+ {KEY_HOME, 1, 1000/*ms*/, long_func_key1},
+ /*{KEY_A, 2, 500, long_func_key2},*/
+ /*TODO: add your remap_item here*/
+};
+
+static struct keypad_remap hisik3_keypad_long_remap = {
+ .count = ARRAY_SIZE(remap_items),
+ .items = remap_items,
+};
+
+static struct k3v2_keypad_platdata hisik3_keypad_platdata = {
+ .keymap_data = &hisik3_keymap_data,
+ .keypad_remap = &hisik3_keypad_long_remap,
+ .rows = 8,
+ .cols = 8,
+ .row_shift = 3,
+};
+
+static const struct of_device_id keypad_match[] = {
+ { .compatible = "hisilicon,k3_keypad",
+ .data = &hisik3_keypad_platdata,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, keypad_match);
+#endif
+
+static int k3v2_keypad_probe(struct platform_device *pdev)
+{
+ const struct k3v2_keypad_platdata *platdata;
+ const struct matrix_keymap_data *keymap_data;
+ struct k3v2_keypad *keypad;
+ struct resource *res;
+ struct input_dev *input_dev;
+ int err;
+#ifdef CONFIG_OF
+ const struct of_device_id *match;
+
+ match = of_match_node(keypad_match, pdev->dev.of_node);
+ platdata = match->data;
+#else
+ platdata = pdev->dev.platform_data;
+#endif
+ if (!platdata) {
+ dev_err(&pdev->dev, "platform data is null!\n");
+ return -EINVAL;
+ }
+ keymap_data = platdata->keymap_data;
+ if (!keymap_data) {
+ dev_err(&pdev->dev, "keymap data is null!\n");
+ return -EINVAL;
+ }
+
+ if (!platdata->rows || platdata->rows > KPC_MAX_ROWS) {
+ dev_err(&pdev->dev, "keypad rows is null or bigger than the max rows!\n");
+ return -EINVAL;
+ }
+
+ if (!platdata->cols || platdata->cols > KPC_MAX_COLS) {
+ dev_err(&pdev->dev, "keypad cols is null or bigger than the max cols!\n");
+ return -EINVAL;
+ }
+
+ keypad = kzalloc(sizeof(struct k3v2_keypad) , GFP_KERNEL);
+ if (!keypad) {
+ dev_err(&pdev->dev, "Failed to allocate struct k3v2_keypad!\n");
+ err = -ENOMEM;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&pdev->dev, "Failed to allocate struct k3v2_keypad or input_dev!\n");
+ err = -ENOMEM;
+ goto err_alloc_input_device;
+ }
+
+ keypad_long_remap = platdata->keypad_remap;
+ if (!keypad_long_remap) {
+ dev_err(&pdev->dev, "Failed to get_keypad_long_remap!\n");
+ err = -EINVAL;
+ goto err_get_keypad_remap;
+ }
+
+ g_long_remap_state = (struct keypad_remap_state *)kzalloc(
+ keypad_long_remap->count * sizeof(struct keypad_remap_state), GFP_KERNEL);
+ if (!g_long_remap_state) {
+ dev_err(&pdev->dev, "Failed to allocate g_long_remap_state!\n");
+ err = -ENOMEM;
+ goto err_alloc_remap_state;
+ }
+ /*Get REG_BASE_KPC address*/
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get KPC base address!\n");
+ err = -ENODEV;
+ goto err_get_base;
+ }
+
+ keypad->base = ioremap(res->start, resource_size(res));
+ if (!keypad->base) {
+ dev_err(&pdev->dev, "Failed to remap KPC base address!\n");
+ err = -EBUSY;
+ goto err_ioremap_base;
+ }
+
+ /*get clock*/
+ keypad->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(keypad->clk)) {
+ dev_err(&pdev->dev, "Failed to get clk_kpc!\n");
+ err = -ENODEV;
+ goto err_get_clk;
+ }
+ err = clk_set_rate(keypad->clk, KPC_CLK_RATE);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Failed to set clk rate!\n");
+ goto err_set_clk;
+ }
+ clk_prepare_enable(keypad->clk);
+
+ keypad->input_dev = input_dev;
+ keypad->row_shift = platdata->row_shift;
+ keypad->rows = platdata->rows;
+ keypad->cols = platdata->cols;
+
+ input_dev->name = pdev->name;
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &pdev->dev;
+ input_set_drvdata(input_dev, keypad);
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(EV_SYN, input_dev->evbit);
+ input_dev->keycode = keypad->keycodes;
+ input_dev->keycodesize = sizeof(keypad->keycodes[0]);
+ input_dev->open = k3v2_keypad_open;
+ input_dev->close = k3v2_keypad_close;
+
+ matrix_keypad_build_keymap(keymap_data, "keypad_default_keymap", keypad->rows, keypad->cols,
+ input_dev->keycode, input_dev);
+
+ keypad->irq = platform_get_irq(pdev, 0);
+ if (keypad->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq!\n");
+ err = keypad->irq;
+ goto err_get_irq;
+
+ }
+
+ err = request_irq(keypad->irq, k3v2_keypad_irq_handler, IRQF_NO_SUSPEND, pdev->name, keypad);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to request interupt handler!\n");
+ goto err_request_irq;
+ }
+
+ disable_irq(keypad->irq);
+
+ err = input_register_device(keypad->input_dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register input device!\n");
+ goto err_register_device;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+ platform_set_drvdata(pdev, keypad);
+ dev_info(&pdev->dev, "k3v2 keypad probe successfully!\n");
+
+ return 0;
+
+err_register_device:
+ free_irq(keypad->irq, keypad);
+err_request_irq:
+err_get_irq:
+ clk_disable_unprepare(keypad->clk);
+err_set_clk:
+ clk_put(keypad->clk);
+err_get_clk:
+ iounmap(keypad->base);
+err_ioremap_base:
+err_get_base:
+ kfree(g_long_remap_state);
+err_alloc_remap_state:
+err_get_keypad_remap:
+ input_free_device(input_dev);
+err_alloc_input_device:
+ kfree(keypad);
+
+ pr_info("K3v2 keypad probe failed! ret = %d\n", err);
+ return err;
+}
+
+static int k3v2_keypad_remove(struct platform_device* pdev)
+{
+ struct k3v2_keypad *keypad = platform_get_drvdata(pdev);
+
+ if (keypad == NULL) {
+ dev_err(&pdev->dev, "get invalid keypad pointer\n");
+ return -EINVAL;
+ }
+
+ free_irq(keypad->irq, keypad);
+ iounmap(keypad->base);
+
+ clk_disable_unprepare(keypad->clk);
+ clk_put(keypad->clk);
+
+ input_unregister_device(keypad->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(keypad);
+
+ if (!g_long_remap_state)
+ kfree(g_long_remap_state);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int k3v2_keypad_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ pr_info("[keypad]suspend successfully\n");
+ return 0;
+}
+
+static int k3v2_keypad_resume(struct platform_device *pdev)
+{
+ pr_info("[keypad]resume successfully\n");
+ return 0;
+}
+#endif
+
+
+
+struct platform_driver k3v2_keypad_driver = {
+ .probe = k3v2_keypad_probe,
+ .remove = k3v2_keypad_remove,
+ .driver = {
+ .name = "k3_keypad",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(keypad_match),
+ },
+ #ifdef CONFIG_PM
+ .suspend = k3v2_keypad_suspend,
+ .resume = k3v2_keypad_resume,
+ #endif
+};
+
+static int __init k3v2_keypad_init(void)
+{
+ pr_info("k3v2 keypad init!\n");
+ return platform_driver_register(&k3v2_keypad_driver);
+}
+
+static void __exit k3v2_keypad_exit(void)
+{
+ platform_driver_unregister(&k3v2_keypad_driver);
+}
+
+module_init(k3v2_keypad_init);
+module_exit(k3v2_keypad_exit);
+MODULE_AUTHOR("Hisilicon K3 Driver Group");
+MODULE_DESCRIPTION("K3v2 keypad platform driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/k3_keypad.h b/drivers/input/keyboard/k3_keypad.h
new file mode 100644
index 00000000000..9e8ed9dcfe2
--- /dev/null
+++ b/drivers/input/keyboard/k3_keypad.h
@@ -0,0 +1,47 @@
+#ifndef _K3_KEYPAD_H_
+#define _K3_KEYPAD_H_
+#include <linux/input/matrix_keypad.h>
+
+#define DPAD_CENTER 232
+
+#define KEYPAD_CHECK_KEYCODE(map, keycode) \
+ (map[(keycode) >> 4] & (1 << ((keycode) & 0x0F)))
+#define KEYPAD_SET_KEYCODE(map, keycode) \
+ (map[(keycode) >> 4] |= (1 << ((keycode) & 0x0F)))
+#define KEYPAD_CLR_KEYCODE(map, keycode) \
+ (map[(keycode) >> 4] &= ~(1 << ((keycode) & 0x0F)))
+
+
+struct k3v2_keypad_platdata {
+ struct matrix_keymap_data *keymap_data;
+ struct keypad_remap *keypad_remap;
+ unsigned int rows;
+ unsigned int cols;
+ unsigned int row_shift;
+};
+
+/*
+ * target_keycode: target keycode remapped to
+ * keynum : number of keycodes to remap
+ * delay : timeout for pending
+ */
+struct keypad_remap_item {
+ uint16_t target_keycode;
+ uint16_t keynum;
+ uint16_t delay;
+ uint16_t *keycodes;
+};
+
+struct keypad_remap_state {
+ bool pending;
+ bool remapped;
+ unsigned int time;
+ unsigned char down_state;
+};
+
+struct keypad_remap {
+ int count;
+ struct keypad_remap_item *items;
+};
+
+#endif