From c666c1459d0e540c1788585d9b672d1ae89f347f Mon Sep 17 00:00:00 2001 From: wdc Date: Thu, 20 Feb 2014 14:30:02 +0800 Subject: [PATCH] linux3.10: support saradc key driver --- arch/arm/boot/dts/rk3188-tb.dts | 53 +++ arch/arm/boot/dts/rk3188.dtsi | 13 + arch/arm/configs/rockchip_defconfig | 2 + drivers/iio/adc/Kconfig | 6 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/rk_adc.c | 360 ++++++++++++++++++++ drivers/input/keyboard/Kconfig | 7 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/rk_keys.c | 502 ++++++++++++++++++++++++++++ 9 files changed, 945 insertions(+) create mode 100755 drivers/iio/adc/rk_adc.c create mode 100755 drivers/input/keyboard/rk_keys.c diff --git a/arch/arm/boot/dts/rk3188-tb.dts b/arch/arm/boot/dts/rk3188-tb.dts index 680a86944d8a..d1470dbcbff5 100644 --- a/arch/arm/boot/dts/rk3188-tb.dts +++ b/arch/arm/boot/dts/rk3188-tb.dts @@ -134,6 +134,59 @@ &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"; }; diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi index a8aaeaeb8356..76c10266e8f2 100755 --- a/arch/arm/boot/dts/rk3188.dtsi +++ b/arch/arm/boot/dts/rk3188.dtsi @@ -407,6 +407,19 @@ status = "disabled"; }; + adc: adc@2006c000 { + compatible = "rockchip,saradc"; + reg = <0x2006c000 0x100>; + interrupts = ; + #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>; diff --git a/arch/arm/configs/rockchip_defconfig b/arch/arm/configs/rockchip_defconfig index d7448cc32eca..10f57efc19bc 100755 --- a/arch/arm/configs/rockchip_defconfig +++ b/arch/arm/configs/rockchip_defconfig @@ -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 diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index ab0767e6727e..9a34f1f39ed6 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -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 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 0a825bed43f6..a3e4ca12be90 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -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 index 000000000000..58c726643182 --- /dev/null +++ b/drivers/iio/adc/rk_adc.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#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); + + diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 292f3240e67b..84646dcc6aa4 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -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 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 0c43e8cf8d0e..5ed829fb867e 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -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 index 000000000000..5a8b53631e44 --- /dev/null +++ b/drivers/input/keyboard/rk_keys.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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); + -- 2.34.1