summaryrefslogtreecommitdiff
path: root/drivers/input/keyboard/sc_keypad.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/input/keyboard/sc_keypad.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/input/keyboard/sc_keypad.c')
-rw-r--r--drivers/input/keyboard/sc_keypad.c802
1 files changed, 802 insertions, 0 deletions
diff --git a/drivers/input/keyboard/sc_keypad.c b/drivers/input/keyboard/sc_keypad.c
new file mode 100644
index 00000000..abe32e81
--- /dev/null
+++ b/drivers/input/keyboard/sc_keypad.c
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/kernel.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/sysrq.h>
+#include <linux/sched.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+
+#include <soc/sprd/globalregs.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/board.h>
+#include <soc/sprd/gpio.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/kpd.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+#include <linux/input-hook.h>
+
+#define DEBUG_KEYPAD 0
+
+#ifndef CONFIG_OF
+#define KPD_REG_BASE (SPRD_KPD_BASE)
+#else
+static unsigned long KPD_REG_BASE;
+#endif
+
+#define KPD_CTRL (KPD_REG_BASE + 0x00)
+#define KPD_EN (0x01 << 0)
+#define KPD_SLEEP_EN (0x01 << 1)
+#define KPD_LONG_KEY_EN (0x01 << 2)
+
+//v0
+#define KPDCTL_ROW_MSK_V0 (0x3f << 18) /* enable rows 2 - 7 */
+#define KPDCTL_COL_MSK_V0 (0x3f << 10) /* enable cols 2 - 7 */
+
+//v1
+#define KPDCTL_ROW_MSK_V1 (0xff << 16) /* enable rows 0 - 7 */
+#define KPDCTL_COL_MSK_V1 (0xff << 8) /* enable cols 0 - 7 */
+
+#define KPD_INT_EN (KPD_REG_BASE + 0x04)
+#define KPD_INT_RAW_STATUS (KPD_REG_BASE + 0x08)
+#define KPD_INT_MASK_STATUS (KPD_REG_BASE + 0x0C)
+#define KPD_INT_ALL (0xfff)
+#define KPD_INT_DOWNUP (0x0ff)
+#define KPD_INT_LONG (0xf00)
+#define KPD_PRESS_INT0 (1 << 0)
+#define KPD_PRESS_INT1 (1 << 1)
+#define KPD_PRESS_INT2 (1 << 2)
+#define KPD_PRESS_INT3 (1 << 3)
+#define KPD_RELEASE_INT0 (1 << 4)
+#define KPD_RELEASE_INT1 (1 << 5)
+#define KPD_RELEASE_INT2 (1 << 6)
+#define KPD_RELEASE_INT3 (1 << 7)
+#define KPD_LONG_KEY_INT0 (1 << 8)
+#define KPD_LONG_KEY_INT1 (1 << 9)
+#define KPD_LONG_KEY_INT2 (1 << 10)
+#define KPD_LONG_KEY_INT3 (1 << 11)
+
+#define KPD_INT_CLR (KPD_REG_BASE + 0x10)
+#define KPD_POLARITY (KPD_REG_BASE + 0x18)
+#define KPD_CFG_ROW_POLARITY (0xff)
+#define KPD_CFG_COL_POLARITY (0xFF00)
+#define CFG_ROW_POLARITY (KPD_CFG_ROW_POLARITY & 0x00FF)
+#define CFG_COL_POLARITY (KPD_CFG_COL_POLARITY & 0xFF00)
+
+#define KPD_DEBOUNCE_CNT (KPD_REG_BASE + 0x1C)
+#define KPD_LONG_KEY_CNT (KPD_REG_BASE + 0x20)
+
+#define KPD_SLEEP_CNT (KPD_REG_BASE + 0x24)
+#define KPD_SLEEP_CNT_VALUE(_X_MS_) (_X_MS_ * 32.768 -1)
+#define KPD_CLK_DIV_CNT (KPD_REG_BASE + 0x28)
+
+#define KPD_KEY_STATUS (KPD_REG_BASE + 0x2C)
+#define KPD_INT0_COL(_X_) (((_X_)>> 0) & 0x7)
+#define KPD_INT0_ROW(_X_) (((_X_)>> 4) & 0x7)
+#define KPD_INT0_DOWN(_X_) (((_X_)>> 7) & 0x1)
+#define KPD_INT1_COL(_X_) (((_X_)>> 8) & 0x7)
+#define KPD_INT1_ROW(_X_) (((_X_)>> 12) & 0x7)
+#define KPD_INT1_DOWN(_X_) (((_X_)>> 15) & 0x1)
+#define KPD_INT2_COL(_X_) (((_X_)>> 16) & 0x7)
+#define KPD_INT2_ROW(_X_) (((_X_)>> 20) & 0x7)
+#define KPD_INT2_DOWN(_X_) (((_X_)>> 23) & 0x1)
+#define KPD_INT3_COL(_X_) (((_X_)>> 24) & 0x7)
+#define KPD_INT3_ROW(_X_) (((_X_)>> 28) & 0x7)
+#define KPD_INT3_DOWN(_X_) (((_X_)>> 31) & 0x1)
+
+#define KPD_SLEEP_STATUS (KPD_REG_BASE + 0x0030)
+#define KPD_DEBUG_STATUS1 (KPD_REG_BASE + 0x0034)
+#define KPD_DEBUG_STATUS2 (KPD_REG_BASE + 0x0038)
+
+#ifndef CONFIG_OF
+#define PB_INT EIC_KEY_POWER
+#else
+static int PB_INT;
+#endif
+
+#if defined(CONFIG_ARCH_SC8825)
+static __devinit void __keypad_enable(void)
+{
+ sci_glb_set(REG_GLB_SOFT_RST, BIT_KPD_RST);
+ mdelay(2);
+ sci_glb_clr(REG_GLB_SOFT_RST, BIT_KPD_RST);
+ sci_glb_set(REG_GLB_GEN0, BIT_KPD_EB | BIT_RTC_KPD_EB);
+}
+static __devexit void __keypad_disable(void)
+{
+ sci_glb_clr(REG_GLB_GEN0, BIT_KPD_EB | BIT_RTC_KPD_EB);
+}
+
+static int __keypad_controller_ver(void)
+{
+ return 0;
+}
+
+#elif defined(CONFIG_ARCH_SCX35)
+static void __keypad_enable(void)
+{
+ sci_glb_set(REG_AON_APB_APB_RST0, BIT_KPD_SOFT_RST);
+ mdelay(2);
+ sci_glb_clr(REG_AON_APB_APB_RST0, BIT_KPD_SOFT_RST);
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_KPD_EB);
+ sci_glb_set(REG_AON_APB_APB_RTC_EB, BIT_KPD_RTC_EB);
+}
+
+static void __keypad_disable(void)
+{
+ sci_glb_clr(REG_AON_APB_APB_EB0, BIT_KPD_EB);
+ sci_glb_clr(REG_AON_APB_APB_RTC_EB, BIT_KPD_RTC_EB);
+}
+static int __keypad_controller_ver(void)
+{
+ return 1;
+}
+
+#else
+#error "Pls fill the low level enable function"
+#endif
+
+struct sci_keypad_t {
+ struct input_dev *input_dev;
+ int irq;
+ int rows;
+ int cols;
+ unsigned int keyup_test_jiffies;
+};
+
+
+#if DEBUG_KEYPAD
+static void dump_keypad_register(void)
+{
+#define INT_MASK_STS (SPRD_INTC1_BASE + 0x0000)
+#define INT_RAW_STS (SPRD_INTC1_BASE + 0x0004)
+#define INT_EN (SPRD_INTC1_BASE + 0x0008)
+#define INT_DIS (SPRD_INTC1_BASE + 0x000C)
+
+ printk("\nREG_INT_MASK_STS = 0x%08x\n", __raw_readl(INT_MASK_STS));
+ printk("REG_INT_RAW_STS = 0x%08x\n", __raw_readl(INT_RAW_STS));
+ printk("REG_INT_EN = 0x%08x\n", __raw_readl(INT_EN));
+ printk("REG_INT_DIS = 0x%08x\n", __raw_readl(INT_DIS));
+ printk("REG_KPD_CTRL = 0x%08x\n", __raw_readl(KPD_CTRL));
+ printk("REG_KPD_INT_EN = 0x%08x\n", __raw_readl(KPD_INT_EN));
+ printk("REG_KPD_INT_RAW_STATUS = 0x%08x\n",
+ __raw_readl(KPD_INT_RAW_STATUS));
+ printk("REG_KPD_INT_MASK_STATUS = 0x%08x\n",
+ __raw_readl(KPD_INT_MASK_STATUS));
+ printk("REG_KPD_INT_CLR = 0x%08x\n", __raw_readl(KPD_INT_CLR));
+ printk("REG_KPD_POLARITY = 0x%08x\n", __raw_readl(KPD_POLARITY));
+ printk("REG_KPD_DEBOUNCE_CNT = 0x%08x\n",
+ __raw_readl(KPD_DEBOUNCE_CNT));
+ printk("REG_KPD_LONG_KEY_CNT = 0x%08x\n",
+ __raw_readl(KPD_LONG_KEY_CNT));
+ printk("REG_KPD_SLEEP_CNT = 0x%08x\n", __raw_readl(KPD_SLEEP_CNT));
+ printk("REG_KPD_CLK_DIV_CNT = 0x%08x\n", __raw_readl(KPD_CLK_DIV_CNT));
+ printk("REG_KPD_KEY_STATUS = 0x%08x\n", __raw_readl(KPD_KEY_STATUS));
+ printk("REG_KPD_SLEEP_STATUS = 0x%08x\n",
+ __raw_readl(KPD_SLEEP_STATUS));
+}
+#else
+static void dump_keypad_register(void)
+{
+}
+#endif
+
+
+
+static irqreturn_t sci_keypad_isr(int irq, void *dev_id)
+{
+ unsigned short key = 0;
+ unsigned long value;
+ struct sci_keypad_t *sci_kpd = dev_id;
+ unsigned long int_status = __raw_readl(KPD_INT_MASK_STATUS);
+ unsigned long key_status = __raw_readl(KPD_KEY_STATUS);
+ unsigned short *keycodes = sci_kpd->input_dev->keycode;
+ unsigned int row_shift = get_count_order(sci_kpd->cols);
+ int col, row;
+ /* add by lgd */
+ static unsigned int press_flag = 1;
+
+ value = __raw_readl(KPD_INT_CLR);
+ value |= KPD_INT_ALL;
+ __raw_writel(value, KPD_INT_CLR);
+ /* < changed by lgd */
+ if ((int_status & KPD_PRESS_INT0)) {
+ if (press_flag) {
+ col = KPD_INT0_COL(key_status);
+ row = KPD_INT0_ROW(key_status);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+ input_report_key(sci_kpd->input_dev, key, 1);
+ input_sync(sci_kpd->input_dev);
+ printk("%03dD, press_flag = %d, set press_flag = 0\n",
+ key, press_flag);
+ press_flag = 0;
+ } else {
+ printk("%03dD-OHH, button double press\n", key);
+ }
+ }
+ if (int_status & KPD_RELEASE_INT0) {
+ col = KPD_INT0_COL(key_status);
+ row = KPD_INT0_ROW(key_status);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+
+ input_report_key(sci_kpd->input_dev, key, 0);
+ input_sync(sci_kpd->input_dev);
+ press_flag = 1;
+ printk("%03dU, force set press_flag = 1\n", key);
+ }
+ /* changed by lgd > */
+
+ if ((int_status & KPD_PRESS_INT1)) {
+ col = KPD_INT1_COL(key_status);
+ row = KPD_INT1_ROW(key_status);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+
+ input_report_key(sci_kpd->input_dev, key, 1);
+ input_sync(sci_kpd->input_dev);
+ printk("%03dD\n", key);
+ }
+ if (int_status & KPD_RELEASE_INT1) {
+ col = KPD_INT1_COL(key_status);
+ row = KPD_INT1_ROW(key_status);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+
+ input_report_key(sci_kpd->input_dev, key, 0);
+ input_sync(sci_kpd->input_dev);
+ printk("%03dU\n", key);
+ }
+
+ if ((int_status & KPD_PRESS_INT2)) {
+ col = KPD_INT2_COL(key_status);
+ row = KPD_INT2_ROW(key_status);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+
+ input_report_key(sci_kpd->input_dev, key, 1);
+ input_sync(sci_kpd->input_dev);
+ printk("%03d\n", key);
+ }
+ if (int_status & KPD_RELEASE_INT2) {
+ col = KPD_INT2_COL(key_status);
+ row = KPD_INT2_ROW(key_status);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+
+ input_report_key(sci_kpd->input_dev, key, 0);
+ input_sync(sci_kpd->input_dev);
+ printk("%03d\n", key);
+ }
+
+ if (int_status & KPD_PRESS_INT3) {
+ col = KPD_INT3_COL(key_status);
+ row = KPD_INT3_ROW(key_status);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+
+ input_report_key(sci_kpd->input_dev, key, 1);
+ input_sync(sci_kpd->input_dev);
+ printk("%03d\n", key);
+ }
+ if (int_status & KPD_RELEASE_INT3) {
+ col = KPD_INT3_COL(key_status);
+ row = KPD_INT3_ROW(key_status);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
+
+ input_report_key(sci_kpd->input_dev, key, 0);
+ input_sync(sci_kpd->input_dev);
+ printk("%03d\n", key);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sci_powerkey_isr(int irq, void *dev_id)
+{ //TODO: if usign gpio(eic), need add row , cols to platform data.
+ static unsigned long last_value = 1;
+ unsigned short key = KEY_POWER;
+ unsigned long value = !(gpio_get_value(PB_INT));
+ struct sci_keypad_t *sci_kpd = dev_id;
+
+ if (last_value == value) {
+ /* seems an event is missing, just report it */
+ input_report_key(sci_kpd->input_dev, key, last_value);
+ input_sync(sci_kpd->input_dev);
+
+ printk("%dX\n", key);
+ }
+
+ if (value) {
+ /* Release : low level */
+#ifdef HOOK_POWER_KEY
+ input_report_key_hook(sci_kpd->input_dev, key, 0);
+#endif
+ input_report_key(sci_kpd->input_dev, key, 0);
+ input_sync(sci_kpd->input_dev);
+ printk("Powerkey:%dU\n", key);
+ irq_set_irq_type(irq, IRQF_TRIGGER_HIGH);
+ } else {
+ /* Press : high level */
+#ifdef HOOK_POWER_KEY
+ input_report_key_hook(sci_kpd->input_dev, key, 1);
+#endif
+ input_report_key(sci_kpd->input_dev, key, 1);
+ input_sync(sci_kpd->input_dev);
+ printk("Powerkey:%dD\n", key);
+ irq_set_irq_type(irq, IRQF_TRIGGER_LOW);
+ }
+
+ last_value = value;
+
+ return IRQ_HANDLED;
+}
+#ifdef CONFIG_OF
+static struct sci_keypad_platdata *sci_keypad_parse_dt(
+ struct device *dev)
+{
+ struct sci_keypad_platform_data *pdata;
+ struct device_node *np = dev->of_node, *key_np;
+ uint32_t num_rows, num_cols;
+ uint32_t rows_choose_hw, cols_choose_hw;
+ uint32_t debounce_time;
+ struct matrix_keymap_data *keymap_data;
+ uint32_t *keymap, key_count;
+ struct resource res;
+ int ret;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if(ret < 0){
+ dev_err(dev, "no reg of property specified\n");
+ return NULL;
+ }
+ KPD_REG_BASE = (unsigned long)ioremap_nocache(res.start,
+ resource_size(&res));
+ if(!KPD_REG_BASE)
+ BUG();
+ PB_INT = of_get_gpio(np, 0);
+ if(PB_INT < 0){
+ dev_err(dev, "no gpios of property specified\n");
+ return NULL;
+ }
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "could not allocate memory for platform data\n");
+ return NULL;
+ }
+ ret = of_property_read_u32(np, "sprd,keypad-num-rows", &num_rows);
+ if(ret){
+ dev_err(dev, "no sprd,keypad-num-rows of property specified\n");
+ goto fail;
+ }
+ pdata->rows_number = num_rows;
+
+ ret = of_property_read_u32(np, "sprd,keypad-num-columns", &num_cols);
+ if(ret){
+ dev_err(dev, "no sprd,keypad-num-columns of property specified\n");
+ goto fail;
+ }
+ pdata->cols_number = num_cols;
+
+ ret = of_property_read_u32(np, "sprd,debounce_time", &debounce_time);
+ if(ret){
+ debounce_time = 0;
+ }
+ pdata->debounce_time = debounce_time;
+
+ if (of_get_property(np, "linux,input-no-autorepeat", NULL))
+ pdata->repeat = false;
+ if(of_get_property(np, "sprd,support_long_key", NULL))
+ pdata->support_long_key = false;
+ if (of_get_property(np, "linux,input-wakeup", NULL))
+ pdata->wakeup = true;
+
+
+ ret = of_property_read_u32(np, "sprd,keypad-rows-choose-hw", &rows_choose_hw);
+ if(ret){
+ dev_err(dev, "no sprd,keypad-rows-choose-hw of property specified\n");
+ goto fail;
+ }
+ pdata->rows_choose_hw = rows_choose_hw;
+
+ ret = of_property_read_u32(np, "sprd,keypad-cols-choose-hw", &cols_choose_hw);
+ if(ret){
+ dev_err(dev, "no sprd,keypad-cols-choose-hw of property specified\n");
+ goto fail;
+ }
+ pdata->cols_choose_hw = cols_choose_hw;
+
+ keymap_data = kzalloc(sizeof(*keymap_data), GFP_KERNEL);
+ if (!keymap_data) {
+ dev_err(dev, "could not allocate memory for keymap_data\n");
+ goto fail;
+ }
+ pdata->keymap_data = keymap_data;
+
+ key_count = of_get_child_count(np);
+ keymap_data->keymap_size = key_count;
+ keymap = kzalloc(sizeof(uint32_t) * key_count, GFP_KERNEL);
+ if (!keymap) {
+ dev_err(dev, "could not allocate memory for keymap\n");
+ goto fail_keymap;
+ }
+ keymap_data->keymap = keymap;
+
+ for_each_child_of_node(np, key_np) {
+ u32 row, col, key_code;
+ ret = of_property_read_u32(key_np, "keypad,row", &row);
+ if(ret)
+ goto fail_parse_keymap;
+ ret = of_property_read_u32(key_np, "keypad,column", &col);
+ if(ret)
+ goto fail_parse_keymap;
+ ret = of_property_read_u32(key_np, "linux,code", &key_code);
+ if(ret)
+ goto fail_parse_keymap;
+ *keymap++ = KEY(row, col, key_code);
+ pr_info("sci_keypad_parse_dt: %d, %d, %d\n",row, col, key_code);
+ }
+
+
+ return pdata;
+
+fail_parse_keymap:
+ dev_err(dev, "failed parsing keymap\n");
+ kfree(keymap);
+ keymap_data->keymap = NULL;
+fail_keymap:
+ kfree(keymap_data);
+ pdata->keymap_data = NULL;
+fail:
+ kfree(pdata);
+ return NULL;
+}
+#else
+static struct sci_keypad_platdata *sci_keypad_parse_dt(
+ struct device *dev)
+{
+ return NULL;
+}
+#endif
+
+/*****************/
+static int sprd_eic_button_state(void)
+{
+ int button_state = 0;
+ int gpio_value = 0;
+
+ gpio_value = !!gpio_get_value(PB_INT);
+
+ //if(1 == button->active_low) {
+ if(0) {
+ if(0 == gpio_value)
+ button_state = 1;
+ else
+ button_state = 0;
+ } else {
+ if(1 == gpio_value)
+ button_state = 1;
+ else
+ button_state = 0;
+ }
+
+ printk("GPIO_%d=%d, button_state=%d\n", PB_INT, gpio_value, button_state);
+
+ return button_state; //0==released, 1==pressed
+}
+
+static ssize_t sprd_eic_button_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ int status = 0;
+
+ status = sprd_eic_button_state();
+ printk("button status = %d (0 == released, 1 == pressed)\n", status);
+ return sprintf(buff, "%d\n", status);
+}
+
+static struct kobject *sprd_eic_button_kobj = NULL;
+static struct kobj_attribute sprd_eic_button_attr =
+ __ATTR(status, 0644, sprd_eic_button_show, NULL);
+
+static int sprd_eic_button_sysfs_init(void)
+{
+ int ret = -1;
+
+ sprd_eic_button_kobj = kobject_create_and_add("sprd_eic_button", kernel_kobj);
+ if (sprd_eic_button_kobj == NULL) {
+ ret = -ENOMEM;
+ printk("register sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = sysfs_create_file(sprd_eic_button_kobj, &sprd_eic_button_attr.attr);
+ if (ret) {
+ printk("create sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+
+ printk("sprd_eic_button_sysfs_init success\n");
+ return ret;
+}
+/*****************/
+
+static int sci_keypad_probe(struct platform_device *pdev)
+{
+ struct sci_keypad_t *sci_kpd;
+ struct input_dev *input_dev;
+ struct sci_keypad_platform_data *pdata = pdev->dev.platform_data;
+ int error;
+ unsigned long value;
+ unsigned int row_shift, keycodemax;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (pdev->dev.of_node && !pdata){
+ pdata = sci_keypad_parse_dt(&pdev->dev);
+ if(pdata)
+ pdev->dev.platform_data = pdata;
+ }
+
+ if (!pdata) {
+ printk(KERN_WARNING "sci_keypad_probe get platform_data NULL\n");
+ error = -EINVAL;
+ goto out0;
+ }
+ row_shift = get_count_order(pdata->cols_number);
+ keycodemax = pdata->rows_number << row_shift;
+
+ sci_kpd = kzalloc(sizeof(struct sci_keypad_t) +
+ keycodemax * sizeof(unsigned short), GFP_KERNEL);
+ input_dev = input_allocate_device();
+
+ if (!sci_kpd || !input_dev) {
+ error = -ENOMEM;
+ goto out1;
+ }
+ platform_set_drvdata(pdev, sci_kpd);
+
+ sci_kpd->input_dev = input_dev;
+ sci_kpd->rows = pdata->rows_number;
+ sci_kpd->cols = pdata->cols_number;
+
+ __keypad_enable();
+
+ __raw_writel(KPD_INT_ALL, KPD_INT_CLR);
+ __raw_writel(CFG_ROW_POLARITY | CFG_COL_POLARITY, KPD_POLARITY);
+ __raw_writel(1, KPD_CLK_DIV_CNT);
+ __raw_writel(0xc, KPD_LONG_KEY_CNT);
+ __raw_writel(0x5, KPD_DEBOUNCE_CNT);
+
+ sci_kpd->irq = platform_get_irq(pdev, 0);
+ if (sci_kpd->irq < 0) {
+ error = -ENODEV;
+ dev_err(&pdev->dev, "Get irq number error,Keypad Module\n");
+ goto out2;
+ }
+ error =
+ request_irq(sci_kpd->irq, sci_keypad_isr, IRQF_NO_SUSPEND, "sci-keypad", sci_kpd);
+ if (error) {
+ dev_err(&pdev->dev, "unable to claim irq %d\n", sci_kpd->irq);
+ goto out2;
+ }
+#ifndef CONFIG_OF
+ input_dev->name = pdev->name;
+#else
+ input_dev->name = "sci-keypad";
+#endif
+ input_dev->phys = "sci-key/input0";
+ input_dev->dev.parent = &pdev->dev;
+ input_set_drvdata(input_dev, sci_kpd);
+
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+
+ input_dev->keycode = &sci_kpd[1];
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = keycodemax;
+
+ matrix_keypad_build_keymap(pdata->keymap_data, NULL, pdata->rows_number, pdata->cols_number,
+ input_dev->keycode, input_dev);
+
+ /* there are keys from hw other than keypad controller */
+ __set_bit(KEY_POWER, input_dev->keybit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ if (pdata->repeat)
+ __set_bit(EV_REP, input_dev->evbit);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "unable to register input device\n");
+ goto out3;
+ }
+ device_init_wakeup(&pdev->dev, 1);
+
+ value = KPD_INT_DOWNUP;
+ if (pdata->support_long_key)
+ value |= KPD_INT_LONG;
+ __raw_writel(value, KPD_INT_EN);
+ value = KPD_SLEEP_CNT_VALUE(1000);
+ __raw_writel(value, KPD_SLEEP_CNT);
+
+ if (__keypad_controller_ver() == 0) {
+ if ((pdata->rows_choose_hw & ~KPDCTL_ROW_MSK_V0)
+ || (pdata->cols_choose_hw & ~KPDCTL_COL_MSK_V0)) {
+ pr_warn("Error rows_choose_hw Or cols_choose_hw\n");
+ } else {
+ pdata->rows_choose_hw &= KPDCTL_ROW_MSK_V0;
+ pdata->cols_choose_hw &= KPDCTL_COL_MSK_V0;
+ }
+ } else if (__keypad_controller_ver() == 1) {
+ if ((pdata->rows_choose_hw & ~KPDCTL_ROW_MSK_V1)
+ || (pdata->cols_choose_hw & ~KPDCTL_COL_MSK_V1)) {
+ pr_warn("Error rows_choose_hw\n");
+ } else {
+ pdata->rows_choose_hw &= KPDCTL_ROW_MSK_V1;
+ pdata->cols_choose_hw &= KPDCTL_COL_MSK_V1;
+ }
+ } else {
+ pr_warn
+ ("This driver don't support this keypad controller version Now\n");
+ }
+ value =
+ KPD_EN | KPD_SLEEP_EN | pdata->
+ rows_choose_hw | pdata->cols_choose_hw;
+ if (pdata->support_long_key)
+ value |= KPD_LONG_KEY_EN;
+ __raw_writel(value, KPD_CTRL);
+
+ gpio_request(PB_INT, "powerkey");
+ gpio_direction_input(PB_INT);
+ error = request_irq(gpio_to_irq(PB_INT), sci_powerkey_isr,
+ IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND,
+ "powerkey", sci_kpd);
+ if (error) {
+ dev_err(&pdev->dev, "unable to claim irq %d\n",
+ gpio_to_irq(PB_INT));
+ goto out3;
+ }
+
+ dump_keypad_register();
+ sprd_eic_button_sysfs_init();
+ return 0;
+
+ free_irq(gpio_to_irq(PB_INT), pdev);
+out3:
+ input_free_device(input_dev);
+ free_irq(sci_kpd->irq, pdev);
+out2:
+ platform_set_drvdata(pdev, NULL);
+out1:
+ kfree(sci_kpd);
+out0:
+ return error;
+}
+
+static int sci_keypad_remove(struct
+ platform_device
+ *pdev)
+{
+ unsigned long value;
+ struct sci_keypad_t *sci_kpd = platform_get_drvdata(pdev);
+ struct sci_keypad_platform_data *pdata = pdev->dev.platform_data;
+ /* disable sci keypad controller */
+ __raw_writel(KPD_INT_ALL, KPD_INT_CLR);
+ value = __raw_readl(KPD_CTRL);
+ value &= ~(1 << 0);
+ __raw_writel(value, KPD_CTRL);
+
+ __keypad_disable();
+
+ free_irq(sci_kpd->irq, pdev);
+ input_unregister_device(sci_kpd->input_dev);
+ kfree(sci_kpd);
+ platform_set_drvdata(pdev, NULL);
+ if(pdev->dev.of_node){
+ kfree(pdata->keymap_data->keymap);
+ kfree(pdata->keymap_data);
+ kfree(pdata);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sci_keypad_suspend(struct platform_device *dev, pm_message_t state)
+{
+ return 0;
+}
+
+static int sci_keypad_resume(struct platform_device *dev)
+{
+ struct sci_keypad_platform_data *pdata = dev->dev.platform_data;
+ unsigned long value;
+
+ __keypad_enable();
+ __raw_writel(KPD_INT_ALL, KPD_INT_CLR);
+ __raw_writel(CFG_ROW_POLARITY | CFG_COL_POLARITY, KPD_POLARITY);
+ __raw_writel(1, KPD_CLK_DIV_CNT);
+ __raw_writel(0xc, KPD_LONG_KEY_CNT);
+ __raw_writel(0x5, KPD_DEBOUNCE_CNT);
+
+ value = KPD_INT_DOWNUP;
+ if (pdata->support_long_key)
+ value |= KPD_INT_LONG;
+ __raw_writel(value, KPD_INT_EN);
+ value = KPD_SLEEP_CNT_VALUE(1000);
+ __raw_writel(value, KPD_SLEEP_CNT);
+
+ value =
+ KPD_EN | KPD_SLEEP_EN | pdata->
+ rows_choose_hw | pdata->cols_choose_hw;
+ if (pdata->support_long_key)
+ value |= KPD_LONG_KEY_EN;
+ __raw_writel(value, KPD_CTRL);
+
+ return 0;
+}
+#else
+#define sci_keypad_suspend NULL
+#define sci_keypad_resume NULL
+#endif
+
+static struct of_device_id keypad_match_table[] = {
+ { .compatible = "sprd,sci-keypad", },
+ { },
+};
+struct platform_driver sci_keypad_driver = {
+ .probe = sci_keypad_probe,
+ .remove = sci_keypad_remove,
+ .suspend = sci_keypad_suspend,
+ .resume = sci_keypad_resume,
+ .driver = {
+ .name = "sci-keypad",.owner = THIS_MODULE,
+ .of_match_table = keypad_match_table,
+ },
+};
+
+static int __init sci_keypad_init(void)
+{
+#ifdef HOOK_POWER_KEY
+ input_hook_init();
+#endif
+ return platform_driver_register(&sci_keypad_driver);
+}
+
+static void __exit sci_keypad_exit(void)
+{
+#ifdef HOOK_POWER_KEY
+ input_hook_exit();
+#endif
+ platform_driver_unregister(&sci_keypad_driver);
+}
+
+module_init(sci_keypad_init);
+module_exit(sci_keypad_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("spreadtrum.com");
+MODULE_DESCRIPTION("Keypad driver for spreadtrum:questions contact steve zhan");
+MODULE_ALIAS("platform:sci-keypad");