linux3.10: support saradc key driver
authorwdc <wdc@rock-chips.com>
Thu, 20 Feb 2014 06:30:02 +0000 (14:30 +0800)
committerwdc <wdc@rock-chips.com>
Thu, 20 Feb 2014 06:33:50 +0000 (14:33 +0800)
arch/arm/boot/dts/rk3188-tb.dts
arch/arm/boot/dts/rk3188.dtsi
arch/arm/configs/rockchip_defconfig
drivers/iio/adc/Kconfig
drivers/iio/adc/Makefile
drivers/iio/adc/rk_adc.c [new file with mode: 0755]
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/rk_keys.c [new file with mode: 0755]

index 680a86944d8a2d2d26db6defc0cfc78511525941..d1470dbcbff50f168f2bcf5dedcf06b6317097dd 100644 (file)
 &lcdc1 {
        status = "okay";
 };
+
+&adc {
+        status = "okay";
+
+        key {
+                compatible = "rockchip,key";
+               io-channels = <&adc 1>; 
+
+                vol-up-key {
+                        linux,code = <115>;
+                        label = "volume up";
+                        rockchip,adc_value = <1>;
+                };
+
+                vol-down-key {
+                        linux,code = <114>;
+                        label = "volume down";
+                        rockchip,adc_value = <170>;
+                };
+
+                power-key {
+                        gpios = <&gpio0 GPIO_A4 GPIO_ACTIVE_LOW>;
+                        linux,code = <116>;
+                        label = "power";
+                        gpio-key,wakeup;
+                };
+
+                menu-key {
+                        linux,code = <139>;
+                        label = "menu";
+                        rockchip,adc_value = <355>;
+                };
+
+                home-key {
+                        linux,code = <102>;
+                        label = "home";
+                        rockchip,adc_value = <746>;
+                };
+
+                back-key {
+                        linux,code = <158>;
+                        label = "back";
+                        rockchip,adc_value = <560>;
+                };
+
+                camera-key {
+                        linux,code = <212>;
+                        label = "camera";
+                        rockchip,adc_value = <450>;
+                };       
+        };
+};
+
 &pwm3 {
         status = "okay";
 };
index a8aaeaeb835634135d9240b443f27b14e3f5b128..76c10266e8f2baf24fa331ed0d682988228694ca 100755 (executable)
                status = "disabled";
         };
 
+    adc: adc@2006c000 {
+          compatible = "rockchip,saradc";
+          reg = <0x2006c000 0x100>;
+          interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+           #io-channel-cells = <1>;
+          io-channel-ranges;
+           rockchip,adc-vref = <1800>;
+           clock-frequency = <1000000>;
+           clocks = <&clk_saradc>, <&clk_gates7 14>;
+           clock-names = "saradc", "pclk_saradc"; 
+           status = "disabled";
+    };
+
        spdif: rockchip-spdif@0x1011e000 {
                compatible = "rockchip-spdif";
                reg = <0x1011e000 0x2000>;
index d7448cc32eca788548ed15553bb17c0283765432..10f57efc19bc0d2f9137a7ff082d53a2ef7f5e25 100755 (executable)
@@ -452,6 +452,8 @@ CONFIG_ANDROID_INTF_ALARM_DEV=y
 CONFIG_SYNC=y
 CONFIG_ION=y
 CONFIG_ION_ROCKCHIP=y
+CONFIG_IIO=y
+CONFIG_RK_ADC=y
 CONFIG_PWM=y
 CONFIG_PWM_ROCKCHIP=y
 CONFIG_EXT2_FS=y
index ab0767e6727ec5b5d04ac149b28e0247219c3a55..9a34f1f39ed678d4cd796892249212e7a0d59846 100644 (file)
@@ -157,4 +157,10 @@ config VIPERBOARD_ADC
          Say yes here to access the ADC part of the Nano River
          Technologies Viperboard.
 
+config RK_ADC
+        tristate "Rockchip ADC support"
+        depends on OF
+        help
+          Core support for the ADC block found in the Rockchip series
+          of SoCs for drivers such as the key and battery to use it.
 endmenu
index 0a825bed43f6a1fa56e9901c1cb87520ceaee947..a3e4ca12be90aed198950463cf7b582285de0ec1 100644 (file)
@@ -17,3 +17,4 @@ obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
 obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
+obj-$(CONFIG_RK_ADC) += rk_adc.o
diff --git a/drivers/iio/adc/rk_adc.c b/drivers/iio/adc/rk_adc.c
new file mode 100755 (executable)
index 0000000..58c7266
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ *  rk_adc.c - Support for ADC in rockchip SoCs
+ *
+ *  3channel, 10-bit ADC
+ *
+ *
+ *  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
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+
+#if 0
+#define RK_ADC_DBG(x...)               \
+       printk(KERN_INFO"[rk_adc]: "x)
+#else
+#define RK_ADC_DBG(x...)       
+
+#endif
+
+enum host_chn_shift{
+        SARADC_CHN_SHIFT = 0,
+        CUSTOM_CHN_SHIFT = 28
+};
+
+enum host_chn_mask{
+        SARADC_CHN_MASK = 0x0000000f,  // saradc: 0 -- 15
+        CUSTOM_CHN_MASK = 0xf0000000,
+};
+
+#define ADC_DATA                       0x00
+#define ADC_DATA_MASK          0x3ff
+
+#define ADC_STAS                       0x04
+#define ADC_STAS_BUSY          (1<<0)
+
+#define ADC_CTRL                       0x08
+#define ADC_DELAY_PU_SOC               0x0c
+#define ADC_CTRL_CH(ch)                (ch >> SARADC_CHN_SHIFT) //(0x07 - ((ch)<<0))
+#define ADC_CTRL_POWER_UP          (1<<3)
+#define ADC_CTRL_IRQ_ENABLE    (1<<5)
+#define ADC_CTRL_IRQ_STATUS    (1<<6)
+
+#define ADC_CLK_RATE           1  //1M
+#define SAMPLE_RATE            (20/ADC_CLK_RATE)  //20 CLK
+
+#define MAX_RK_ADC_CHANNELS    3
+#define RK_ADC_TIMEOUT (msecs_to_jiffies(100))
+
+struct rk_adc {
+       void __iomem    *regs;
+       struct clk              *pclk;
+       struct clk              *clk;
+       struct completion       completion;
+       unsigned int            irq;
+       u32                 vref_mv;
+    u32                 num_channels;
+       u32                             value;
+};
+
+static const struct of_device_id rk_adc_match[] = {
+       { .compatible = "rockchip,saradc", .data = NULL},
+       {},
+};
+MODULE_DEVICE_TABLE(of, rk_adc_match);
+
+static int rk_read_raw(struct iio_dev *indio_dev,
+                               struct iio_chan_spec const *chan,
+                               int *val,
+                               int *val2,
+                               long mask)
+{
+       struct rk_adc *info = iio_priv(indio_dev);
+       unsigned long timeout;
+
+       if (mask != IIO_CHAN_INFO_RAW)
+               return -EINVAL;
+
+       mutex_lock(&indio_dev->mlock);
+
+       /* Select the channel to be used and Trigger conversion */
+       //adc_writel(0, dev->regs + ADC_CTRL);
+    writel_relaxed(0x08, info->regs + ADC_DELAY_PU_SOC);
+       writel_relaxed(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chan->channel)|ADC_CTRL_IRQ_ENABLE, info->regs + ADC_CTRL);
+
+       timeout = wait_for_completion_timeout
+                       (&info->completion, RK_ADC_TIMEOUT);
+       *val = info->value;
+       
+       mutex_unlock(&indio_dev->mlock);
+       RK_ADC_DBG("read adc value: %d form channel: %d\n", *val, chan->channel);
+
+       if (timeout == 0)
+               return -ETIMEDOUT;
+
+       return IIO_VAL_INT;
+}
+
+static irqreturn_t rk_adc_isr(int irq, void *dev_id)
+{
+       struct rk_adc *info = (struct rk_adc *)dev_id;
+
+       /* Read value */
+       info->value = readl_relaxed(info->regs + ADC_DATA) & ADC_DATA_MASK;
+       /* clear irq & power down adc*/
+    writel_relaxed(0, info->regs + ADC_CTRL);
+
+       complete(&info->completion);
+
+       return IRQ_HANDLED;
+}
+
+static int rk_adc_reg_access(struct iio_dev *indio_dev,
+                             unsigned reg, unsigned writeval,
+                             unsigned *readval)
+{
+       struct rk_adc *info = iio_priv(indio_dev);
+
+       if (readval == NULL)
+               return -EINVAL;
+
+       *readval = readl_relaxed(info->regs + reg);
+
+       return 0;
+}
+
+static const struct iio_info rk_adc_iio_info = {
+       .read_raw = &rk_read_raw,
+       .debugfs_reg_access = &rk_adc_reg_access,
+       .driver_module = THIS_MODULE,
+};
+
+#define ADC_CHANNEL(_index, _id) {                     \
+       .type = IIO_VOLTAGE,                            \
+       .indexed = 1,                                   \
+       .channel = _index,                              \
+       .address = _index,                              \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
+       .datasheet_name = _id,                          \
+}
+
+static const struct iio_chan_spec rk_adc_iio_channels[] = {
+       ADC_CHANNEL(0, "adc0"),
+       ADC_CHANNEL(1, "adc1"),
+       ADC_CHANNEL(2, "adc2"),
+};
+
+static int rk_adc_remove_devices(struct device *dev, void *c)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+
+       platform_device_unregister(pdev);
+
+       return 0;
+}
+
+static int rk_adc_probe(struct platform_device *pdev)
+{
+       struct rk_adc *info = NULL;
+       struct device_node *np = pdev->dev.of_node;
+       struct iio_dev *indio_dev = NULL;
+       struct resource *mem;
+       int ret = -ENODEV;
+       int irq;
+       u32 rate;
+
+       if (!np)
+               return ret;
+
+       indio_dev = iio_device_alloc(sizeof(struct rk_adc));
+       if (!indio_dev) {
+               dev_err(&pdev->dev, "failed allocating iio device\n");
+               return -ENOMEM;
+       }
+       info = iio_priv(indio_dev);
+
+    //resource ioremap
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       info->regs = devm_request_and_ioremap(&pdev->dev, mem);
+       if (!info->regs) {
+               ret = -ENOMEM;
+               goto err_iio;
+       }
+
+    //irq request      
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "no irq resource?\n");
+               ret = irq;
+               goto err_iio;
+       }
+       info->irq = irq;
+
+       init_completion(&info->completion);
+       ret = devm_request_irq(&pdev->dev, info->irq, rk_adc_isr,
+                                       0, dev_name(&pdev->dev), info);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+                                                       info->irq);
+               goto err_iio;
+       }
+       
+       //clk enable
+       info->pclk = clk_get(&pdev->dev, "pclk_saradc");
+       if (IS_ERR(info->pclk)) {
+           dev_err(&pdev->dev, "failed to get adc pclk\n");
+           ret = PTR_ERR(info->pclk);
+           goto err_iio;
+       }
+       clk_prepare_enable(info->pclk);
+       
+       info->clk = clk_get(&pdev->dev, "saradc");
+       if (IS_ERR(info->clk)) {
+           dev_err(&pdev->dev, "failed to get adc clock\n");
+           ret = PTR_ERR(info->clk);
+           goto err_pclk;
+       }
+
+       if(of_property_read_u32(np, "clock-frequency", &rate)) {
+        dev_err(&pdev->dev, "Missing clock-frequency property in the DT.\n");
+           goto err_clk2;
+       }
+
+       ret = clk_set_rate(info->clk, rate);
+           if(ret < 0) {
+           dev_err(&pdev->dev, "failed to set adc clk\n");
+           goto err_clk2;
+       }
+       clk_prepare_enable(info->clk);
+
+    //device register
+       indio_dev->name = dev_name(&pdev->dev);
+       indio_dev->dev.parent = &pdev->dev;
+       indio_dev->dev.of_node = pdev->dev.of_node;
+       indio_dev->info = &rk_adc_iio_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = rk_adc_iio_channels;
+
+    if (of_property_read_u32(np, "rockchip,adc-vref", &info->vref_mv)) {
+                dev_err(&pdev->dev, "Missing rockchip,adc-vref property in the DT.\n");
+                goto err_clk;
+    }
+    indio_dev->num_channels = sizeof(rk_adc_iio_channels)/sizeof(struct iio_chan_spec);
+
+       ret = iio_device_register(indio_dev);
+       if (ret)
+               goto err_iio_dev;
+
+       platform_set_drvdata(pdev, indio_dev);
+       ret = of_platform_populate(np, rk_adc_match, NULL, &pdev->dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed adding child nodes\n");
+               goto err_of_populate;
+       }
+
+       return 0;
+
+err_of_populate:
+       device_for_each_child(&pdev->dev, NULL,
+                               rk_adc_remove_devices);
+err_iio_dev:
+       iio_device_unregister(indio_dev);
+       
+err_clk:
+       clk_disable(info->clk);
+err_clk2:
+       clk_put(info->clk);
+err_pclk:
+       clk_disable(info->pclk);
+       clk_put(info->pclk);
+       
+err_iio:
+       iio_device_free(indio_dev);
+       return ret;
+}
+
+static int rk_adc_remove(struct platform_device *pdev)
+{
+       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+       struct rk_adc *info = iio_priv(indio_dev);
+
+       device_for_each_child(&pdev->dev, NULL,
+                               rk_adc_remove_devices);
+       clk_disable(info->clk);
+       clk_put(info->clk);
+       clk_disable(info->pclk);
+       clk_put(info->pclk);
+       iio_device_unregister(indio_dev);
+       free_irq(info->irq, info);
+       iio_device_free(indio_dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rk_adc_suspend(struct device *dev)
+{
+       //struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       //struct exynos_adc *info = iio_priv(indio_dev);
+       int ret = 0;
+       
+       return ret;
+}
+
+static int rk_adc_resume(struct device *dev)
+{
+       //struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       //struct rk_adc *info = iio_priv(indio_dev);
+       int ret = 0;
+
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rk_adc_pm_ops,
+                       rk_adc_suspend,
+                       rk_adc_resume);
+
+static struct platform_driver rk_adc_driver = {
+       .probe          = rk_adc_probe,
+       .remove         = rk_adc_remove,
+       .driver         = {
+               .name   = "rockchip-adc",
+               .owner  = THIS_MODULE,
+               .of_match_table = rk_adc_match,
+               .pm     = &rk_adc_pm_ops,
+       },
+};
+
+module_platform_driver(rk_adc_driver);
+
+
index 292f3240e67b8f2f1536c04c1a7498f518b74ca7..84646dcc6aa42a37d9c0cc0fa3fec1e4a09620ef 100644 (file)
@@ -12,6 +12,13 @@ menuconfig INPUT_KEYBOARD
 
 if INPUT_KEYBOARD
 
+config KEYS_RK
+        tristate "rk keyboard"
+        depends on RK_ADC 
+        default y
+        help
+          rk keyboard drivers(gpio and adc)
+
 config KEYBOARD_ADP5520
        tristate "Keypad Support for ADP5520 PMIC"
        depends on PMIC_ADP5520
index 0c43e8cf8d0ec253b78f68ea175985dba09c9244..5ed829fb867e8b3358af6de6b80f5890158f21fa 100644 (file)
@@ -4,6 +4,7 @@
 
 # Each configuration option enables a list of files.
 
+obj-$(CONFIG_KEYS_RK)                   += rk_keys.o
 obj-$(CONFIG_KEYBOARD_ADP5520)         += adp5520-keys.o
 obj-$(CONFIG_KEYBOARD_ADP5588)         += adp5588-keys.o
 obj-$(CONFIG_KEYBOARD_ADP5589)         += adp5589-keys.o
diff --git a/drivers/input/keyboard/rk_keys.c b/drivers/input/keyboard/rk_keys.c
new file mode 100755 (executable)
index 0000000..5a8b536
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * Driver for keys on GPIO lines capable of generating interrupts.
+ *
+ * Copyright 2005 Phil Blundell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/adc.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/consumer.h>
+
+#include <asm/gpio.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+
+#define EMPTY_ADVALUE                                  950
+#define DRIFT_ADVALUE                                  70
+#define INVALID_ADVALUE                                -1
+#define EV_ENCALL                       KEY_F4
+#define EV_MENU                         KEY_F1
+
+
+#if 0
+#define key_dbg(bdata, format, arg...)         \
+       dev_printk(KERN_INFO , &bdata->input->dev , format , ## arg)
+#else
+#define key_dbg(bdata, format, arg...) 
+#endif
+
+#define DEFAULT_DEBOUNCE_INTERVAL       10  //10ms
+#define ADC_SAMPLE_TIME                  100
+
+enum  rk_key_type{
+       TYPE_GPIO = 1,
+          TYPE_ADC
+};
+
+struct rk_keys_button {
+       u32 type;  //TYPE_GPIO, TYPE_ADC
+    u32 code;  // key code
+    char *desc;//key label
+       u32 state; //key up & down state
+    int gpio;  //gpio only
+    int adc_value; //adc only
+    int adc_state; //adc only
+    int active_low;//gpio only
+    int wakeup;    //gpio only
+       struct timer_list timer;
+};
+
+struct rk_keys_drvdata {
+       int nbuttons;
+       bool in_suspend;        /* Flag to indicate if we're suspending/resuming */
+       int result;
+    int rep;
+       struct input_dev *input;
+       struct delayed_work adc_poll_work;
+    struct iio_channel *chan;
+       struct rk_keys_button button[0];
+};
+
+static struct input_dev *sinput_dev = NULL;
+static struct rk_keys_drvdata *spdata = NULL;
+static void * rk_key_get_drvdata(void)
+{
+     BUG_ON(!spdata);
+        return spdata;
+}
+
+void rk_send_power_key(int state)
+{
+       if (!sinput_dev)
+               return;
+       if(state)
+       {
+               input_report_key(sinput_dev, KEY_POWER, 1);
+               input_sync(sinput_dev);
+       }
+       else
+       {
+               input_report_key(sinput_dev, KEY_POWER, 0);
+               input_sync(sinput_dev);
+       }
+}
+
+static void keys_timer(unsigned long _data)
+{
+    struct rk_keys_drvdata *pdata = rk_key_get_drvdata();
+       struct rk_keys_button *button = (struct rk_keys_button *)_data;
+       struct input_dev *input = pdata->input;
+       int state;
+       
+       if(button->type == TYPE_GPIO)
+               state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low);
+       else
+               state = !!button->adc_state;
+       
+       if(button->state != state) {
+               button->state = state;          
+               input_event(input, EV_KEY, button->code, button->state);
+               key_dbg(pdata, "%skey[%s]: report event[%d] state[%d]\n", 
+                       (button->type == TYPE_ADC)?"adc":"gpio", button->desc, button->code, button->state);
+               input_event(input, EV_KEY, button->code, button->state);
+               input_sync(input);
+       }
+       
+       if(state)
+               mod_timer(&button->timer,
+                       jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
+       
+}
+
+static irqreturn_t keys_isr(int irq, void *dev_id)
+{
+    struct rk_keys_drvdata *pdata = rk_key_get_drvdata();
+       struct rk_keys_button *button = (struct rk_keys_button *)dev_id;
+       struct input_dev *input = pdata->input;
+       BUG_ON(irq != gpio_to_irq(button->gpio));
+
+    if(button->wakeup == 1 && pdata->in_suspend == true){
+               button->state = 1;
+               key_dbg(pdata, "wakeup: %skey[%s]: report event[%d] state[%d]\n", 
+                       (button->type == TYPE_ADC)?"adc":"gpio", button->desc, button->code, button->state);
+               input_event(input, EV_KEY, button->code, button->state);
+               input_sync(input);
+    }
+       mod_timer(&button->timer,
+                               jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
+       return IRQ_HANDLED;
+}
+
+static ssize_t adc_value_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);
+       return sprintf(buf, "adc_value: %d\n", ddata->result);
+}
+static DEVICE_ATTR(get_adc_value, S_IRUGO | S_IWUSR, adc_value_show, NULL);
+
+static const struct of_device_id rk_key_match[] = {
+       { .compatible = "rockchip,key", .data = NULL},
+       {},
+};
+MODULE_DEVICE_TABLE(of, rk_key_match);
+
+static int rk_key_adc_iio_read(struct rk_keys_drvdata *data)
+{
+        struct iio_channel *channel = data->chan;
+        int val, ret;
+
+        ret = iio_read_channel_raw(channel, &val);
+        if (ret < 0) {
+                pr_err("read channel() error: %d\n", ret);
+                return ret;
+        }
+        return val;
+}
+
+static void adc_key_poll(struct work_struct *work)
+{
+       struct rk_keys_drvdata *ddata;
+       int i, result = -1;
+
+       ddata = container_of(work, struct rk_keys_drvdata, adc_poll_work.work);
+       if (!ddata->in_suspend)
+               result = rk_key_adc_iio_read(ddata);
+
+       if(result > INVALID_ADVALUE && result < EMPTY_ADVALUE)
+               ddata->result = result;
+       for (i = 0; i < ddata->nbuttons; i++) {
+               struct rk_keys_button *button = &ddata->button[i];
+               if(!button->adc_value)
+                       continue;
+                                       
+               if(result < button->adc_value + DRIFT_ADVALUE &&
+                       result > button->adc_value - DRIFT_ADVALUE)
+                       button->adc_state = 1;
+               else
+                       button->adc_state = 0;
+               if(button->state != button->adc_state)
+                       mod_timer(&button->timer,
+                               jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL));
+       }
+    schedule_delayed_work(&ddata->adc_poll_work,
+            msecs_to_jiffies(ADC_SAMPLE_TIME));
+}
+
+static int rk_key_type_get(struct device_node *node, struct rk_keys_button *button)
+{
+       u32 adc_value;
+
+       if (!of_property_read_u32(node, "rockchip,adc_value", &adc_value))
+        return TYPE_ADC;
+    else if(of_get_gpio(node, 0) >= 0)
+           return TYPE_GPIO;
+       else
+               return -1;
+}
+
+static int rk_keys_parse_dt(struct rk_keys_drvdata *pdata,
+                            struct platform_device *pdev)
+{
+    struct device_node *node = pdev->dev.of_node;
+    struct device_node *child_node;
+    struct iio_channel *chan;
+    int ret, gpio, i =0;
+       u32 code, adc_value, flags;;
+       
+       chan = iio_channel_get(&pdev->dev, NULL);
+    if (IS_ERR(chan))
+        goto error_ret;
+    pdata->chan = chan;
+       
+       for_each_child_of_node(node, child_node) {
+               if(of_property_read_u32(child_node, "linux,code", &code)) {
+                       dev_err(&pdev->dev, "Missing linux,code property in the DT.\n");
+                       ret = -EINVAL;
+                       goto error_ret;
+               }
+        pdata->button[i].code = code;
+               pdata->button[i].desc = of_get_property(child_node, "label", NULL);
+               pdata->button[i].type = rk_key_type_get(child_node, &pdata->button[i]);
+               switch(pdata->button[i].type) 
+               {
+                   case TYPE_GPIO:
+                               gpio = of_get_gpio_flags(child_node, 0, &flags);
+                               if (gpio < 0) {
+                                       ret = gpio;
+                                       if (ret != -EPROBE_DEFER)
+                                               dev_err(&pdev->dev,"Failed to get gpio flags, error: %d\n",
+                                                       ret);
+                                       goto error_ret;
+                               }
+
+                               pdata->button[i].gpio = gpio;
+                       pdata->button[i].active_low = flags & OF_GPIO_ACTIVE_LOW;
+                               pdata->button[i].wakeup = !!of_get_property(child_node, "gpio-key,wakeup", NULL);
+                               break;
+
+                       case TYPE_ADC:
+                           if (of_property_read_u32(child_node, "rockchip,adc_value", &adc_value)) {
+                                   dev_err(&pdev->dev, "Missing rockchip,adc_value property in the DT.\n");
+                                   ret = -EINVAL;
+                                   goto error_ret;
+                           }
+                               pdata->button[i].adc_value = adc_value;
+                               break;
+
+                       default:
+                               dev_err(&pdev->dev, "Error rockchip,type property in the DT.\n");
+                           ret = -EINVAL;
+                           goto error_ret;
+               }
+            i++;
+        }
+       
+       return 0;
+       
+error_ret:
+               return ret;
+
+}
+
+static int  keys_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = pdev->dev.of_node;
+       struct rk_keys_drvdata *ddata = NULL;
+       struct input_dev *input = NULL;
+       int i, error = 0;
+       int wakeup, key_num = 0;
+
+       key_num = of_get_child_count(np);
+       if (key_num == 0) {
+               error = -ENODEV;
+               return error;
+       }
+
+    ddata = devm_kzalloc(dev, sizeof(struct rk_keys_drvdata) +
+           key_num * sizeof(struct rk_keys_button),
+               GFP_KERNEL);
+       
+       input = devm_input_allocate_device(dev);
+       if (!ddata || !input) {
+               error = -ENOMEM;
+               return error;
+       }
+       platform_set_drvdata(pdev, ddata);
+
+       input->name = pdev->name;
+       input->phys = "gpio-keys/input0";
+       input->dev.parent = dev;
+
+       input->id.bustype = BUS_HOST;
+       input->id.vendor = 0x0001;
+       input->id.product = 0x0001;
+       input->id.version = 0x0100;
+       ddata->input = input;
+
+       //parse info from dt
+       ddata->nbuttons = key_num;
+       error = rk_keys_parse_dt(ddata, pdev);
+       if(error)
+           goto fail0;
+
+       /* Enable auto repeat feature of Linux input subsystem */
+       if (ddata->rep)
+               __set_bit(EV_REP, input->evbit);
+
+       for (i = 0; i < ddata->nbuttons; i++) {
+               struct rk_keys_button *button = &ddata->button[i];
+
+               if (button->code){
+                       setup_timer(&button->timer,
+                               keys_timer, (unsigned long)button);}
+
+               if (button->wakeup)
+                       wakeup = 1;
+               
+               input_set_capability(input, EV_KEY, button->code);
+       }
+
+       for (i = 0; i < ddata->nbuttons; i++) {
+               struct rk_keys_button *button = &ddata->button[i];
+
+               if(button->type == TYPE_GPIO) {
+                       error = devm_gpio_request(dev, button->gpio, button->desc ?: "keys");
+                       if (error < 0) {
+                               pr_err("gpio-keys: failed to request GPIO %d,"
+                                       " error %d\n", button->gpio, error);
+                               goto fail1;
+                       }
+
+                       error = gpio_direction_input(button->gpio);
+                       if (error < 0) {
+                               pr_err("gpio-keys: failed to configure input"
+                                       " direction for GPIO %d, error %d\n",
+                                       button->gpio, error);
+                               gpio_free(button->gpio);
+                               goto fail1;
+                       }
+
+                       int irq = gpio_to_irq(button->gpio);
+                       if (irq < 0) {
+                               error = irq;
+                               pr_err("gpio-keys: Unable to get irq number"
+                                       " for GPIO %d, error %d\n",
+                                       button->gpio, error);
+                               gpio_free(button->gpio);
+                               goto fail1;
+                       }
+
+                       error = devm_request_irq(dev, irq, keys_isr,
+                                           (button->active_low)?IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING,
+                                           button->desc ? button->desc : "keys",
+                                           button);
+                       if (error) {
+                               pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
+                                       irq, error);
+                               gpio_free(button->gpio);
+                               goto fail1;
+                       }
+               }
+       }
+
+       input_set_capability(input, EV_KEY, KEY_WAKEUP);
+       device_init_wakeup(dev, wakeup);
+
+       error = input_register_device(input);
+       if (error) {
+               pr_err("gpio-keys: Unable to register input device, "
+                       "error: %d\n", error);
+               goto fail2;
+       }
+
+        //adc polling work
+       INIT_DELAYED_WORK(&ddata->adc_poll_work, adc_key_poll);
+       schedule_delayed_work(&ddata->adc_poll_work,
+                                       msecs_to_jiffies(ADC_SAMPLE_TIME));
+
+    spdata = ddata;
+       sinput_dev = input;
+       return error;
+
+ fail2:
+       device_init_wakeup(dev, 0);
+ fail1:
+       while (--i >= 0) {
+               del_timer_sync(&ddata->button[i].timer);
+       }
+ fail0:
+       platform_set_drvdata(pdev, NULL);
+
+       return error;
+}
+
+static int keys_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);
+       struct input_dev *input = ddata->input;
+       int i;
+
+       device_init_wakeup(dev, 0);
+       for (i = 0; i < ddata->nbuttons; i++) {
+               del_timer_sync(&ddata->button[i].timer);
+       }
+       cancel_delayed_work_sync(&ddata->adc_poll_work);
+       input_unregister_device(input);
+       
+       sinput_dev = NULL;
+       spdata = NULL;
+
+       return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int keys_suspend(struct device *dev)
+{
+       struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);
+       int i;
+
+       ddata->in_suspend = true;
+       if (device_may_wakeup(dev)) {
+               for (i = 0; i < ddata->nbuttons; i++) {
+                       struct rk_keys_button *button = ddata->button + i;
+                       if (button->wakeup) {
+                               int irq = gpio_to_irq(button->gpio);
+                               enable_irq_wake(irq);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int keys_resume(struct device *dev)
+{
+       struct rk_keys_drvdata *ddata = dev_get_drvdata(dev);
+       int i;
+
+       if (device_may_wakeup(dev)) {
+               for (i = 0; i < ddata->nbuttons; i++) {
+                       struct rk_keys_button *button = ddata->button + i;
+                       if (button->wakeup) {
+                               int irq = gpio_to_irq(button->gpio);
+                               disable_irq_wake(irq);
+                       }
+               }
+               preempt_disable();
+               if (local_softirq_pending())
+                       do_softirq(); // for call resend_irqs, which may call keys_isr
+               preempt_enable_no_resched();
+       }
+
+       ddata->in_suspend = false;
+
+       return 0;
+}
+
+static const struct dev_pm_ops keys_pm_ops = {
+       .suspend        = keys_suspend,
+       .resume         = keys_resume,
+};
+#endif
+
+static struct platform_driver keys_device_driver = {
+       .probe          = keys_probe,
+       .remove         = keys_remove,
+       .driver         = {
+               .name   = "rk-keypad",
+               .owner  = THIS_MODULE,
+               .of_match_table = rk_key_match,
+#ifdef CONFIG_PM
+               .pm     = &keys_pm_ops,
+#endif
+       }
+};
+
+module_platform_driver(keys_device_driver);
+