diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-06-22 09:26:29 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-06-22 09:26:29 -0700 |
commit | f7ebc4dcdeb1be7209d94a8c0dc602ab24e36ae1 (patch) | |
tree | 650c1748df35728e2bce1b8c26c064bfea272f63 /drivers/input | |
parent | 469d7d22cea146e40efe8c330e5164b4d8f13934 (diff) | |
parent | 84c88ef9affb34f10cb5b66b07e2d496845d1c28 (diff) |
Merge branch 'next' into for-linus
Prepare first round of input updates for 4.2 merge window.
Diffstat (limited to 'drivers/input')
43 files changed, 1126 insertions, 276 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index a11ff74a5127..a35532ec00e4 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,19 @@ config INPUT if INPUT +config INPUT_LEDS + tristate "Export input device LEDs in sysfs" + depends on LEDS_CLASS + default INPUT + help + Say Y here if you would like to export LEDs on input devices + as standard LED class devices in sysfs. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called input-leds. + config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 5ca3f631497f..0c9302ca9954 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o +obj-$(CONFIG_INPUT_LEDS) += input-leds.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a18f41b89b6a..9d35499faca4 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -422,10 +422,7 @@ static int evdev_release(struct inode *inode, struct file *file) evdev_detach_client(evdev, client); - if (is_vmalloc_addr(client)) - vfree(client); - else - kfree(client); + kvfree(client); evdev_close_device(evdev); diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index b81c88c43452..8f4a30fccbb6 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -70,7 +70,7 @@ static int compat_effect(struct ff_device *ff, struct ff_effect *effect) return -EINVAL; /* - * calculate manginude of sine wave as average of rumble's + * calculate magnitude of sine wave as average of rumble's * 2/3 of strong magnitude and 1/3 of weak magnitude */ magnitude = effect->u.rumble.strong_magnitude / 3 + @@ -213,7 +213,7 @@ static int erase_effect(struct input_dev *dev, int effect_id, /** * input_ff_erase - erase a force-feedback effect from device * @dev: input device to erase effect from - * @effect_id: id of the ffect to be erased + * @effect_id: id of the effect to be erased * @file: purported owner of the request * * This function erases a force-feedback effect from specified device. diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c new file mode 100644 index 000000000000..074a65ed17bb --- /dev/null +++ b/drivers/input/input-leds.c @@ -0,0 +1,212 @@ +/* + * LED support for the input layer + * + * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org> + * + * 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/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/leds.h> +#include <linux/input.h> + +#if IS_ENABLED(CONFIG_VT) +#define VT_TRIGGER(_name) .trigger = _name +#else +#define VT_TRIGGER(_name) .trigger = NULL +#endif + +static const struct { + const char *name; + const char *trigger; +} input_led_info[LED_CNT] = { + [LED_NUML] = { "numlock", VT_TRIGGER("kbd-numlock") }, + [LED_CAPSL] = { "capslock", VT_TRIGGER("kbd-capslock") }, + [LED_SCROLLL] = { "scrolllock", VT_TRIGGER("kbd-scrolllock") }, + [LED_COMPOSE] = { "compose" }, + [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") }, + [LED_SLEEP] = { "sleep" } , + [LED_SUSPEND] = { "suspend" }, + [LED_MUTE] = { "mute" }, + [LED_MISC] = { "misc" }, + [LED_MAIL] = { "mail" }, + [LED_CHARGING] = { "charging" }, +}; + +struct input_led { + struct led_classdev cdev; + struct input_handle *handle; + unsigned int code; /* One of LED_* constants */ +}; + +struct input_leds { + struct input_handle handle; + unsigned int num_leds; + struct input_led leds[]; +}; + +static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev) +{ + struct input_led *led = container_of(cdev, struct input_led, cdev); + struct input_dev *input = led->handle->dev; + + return test_bit(led->code, input->led) ? cdev->max_brightness : 0; +} + +static void input_leds_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct input_led *led = container_of(cdev, struct input_led, cdev); + + input_inject_event(led->handle, EV_LED, led->code, !!brightness); +} + +static void input_leds_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ +} + +static int input_leds_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_leds *leds; + unsigned int num_leds; + unsigned int led_code; + int led_no; + int error; + + num_leds = bitmap_weight(dev->ledbit, LED_CNT); + if (!num_leds) + return -ENXIO; + + leds = kzalloc(sizeof(*leds) + num_leds * sizeof(*leds->leds), + GFP_KERNEL); + if (!leds) + return -ENOMEM; + + leds->num_leds = num_leds; + + leds->handle.dev = dev; + leds->handle.handler = handler; + leds->handle.name = "leds"; + leds->handle.private = leds; + + error = input_register_handle(&leds->handle); + if (error) + goto err_free_mem; + + error = input_open_device(&leds->handle); + if (error) + goto err_unregister_handle; + + led_no = 0; + for_each_set_bit(led_code, dev->ledbit, LED_CNT) { + struct input_led *led = &leds->leds[led_no]; + + led->handle = &leds->handle; + led->code = led_code; + + if (WARN_ON(!input_led_info[led_code].name)) + continue; + + led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s", + dev_name(&dev->dev), + input_led_info[led_code].name); + if (!led->cdev.name) { + error = -ENOMEM; + goto err_unregister_leds; + } + + led->cdev.max_brightness = 1; + led->cdev.brightness_get = input_leds_brightness_get; + led->cdev.brightness_set = input_leds_brightness_set; + led->cdev.default_trigger = input_led_info[led_code].trigger; + + error = led_classdev_register(&dev->dev, &led->cdev); + if (error) { + dev_err(&dev->dev, "failed to register LED %s: %d\n", + led->cdev.name, error); + kfree(led->cdev.name); + goto err_unregister_leds; + } + + led_no++; + } + + return 0; + +err_unregister_leds: + while (--led_no >= 0) { + struct input_led *led = &leds->leds[led_no]; + + led_classdev_unregister(&led->cdev); + kfree(led->cdev.name); + } + + input_close_device(&leds->handle); + +err_unregister_handle: + input_unregister_handle(&leds->handle); + +err_free_mem: + kfree(leds); + return error; +} + +static void input_leds_disconnect(struct input_handle *handle) +{ + struct input_leds *leds = handle->private; + int i; + + for (i = 0; i < leds->num_leds; i++) { + struct input_led *led = &leds->leds[i]; + + led_classdev_unregister(&led->cdev); + kfree(led->cdev.name); + } + + input_close_device(handle); + input_unregister_handle(handle); + + kfree(leds); +} + +static const struct input_device_id input_leds_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_LED) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, input_leds_ids); + +static struct input_handler input_leds_handler = { + .event = input_leds_event, + .connect = input_leds_connect, + .disconnect = input_leds_disconnect, + .name = "leds", + .id_table = input_leds_ids, +}; + +static int __init input_leds_init(void) +{ + return input_register_handler(&input_leds_handler); +} +module_init(input_leds_init); + +static void __exit input_leds_exit(void) +{ + input_unregister_handler(&input_leds_handler); +} +module_exit(input_leds_exit); + +MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>"); +MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>"); +MODULE_DESCRIPTION("Input -> LEDs Bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/input.c b/drivers/input/input.c index cc357f1516a7..f31578423636 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -2262,7 +2262,7 @@ EXPORT_SYMBOL(input_unregister_handler); * * Iterate over @bus's list of devices, and call @fn for each, passing * it @data and stop when @fn returns a non-zero value. The function is - * using RCU to traverse the list and therefore may be usind in atonic + * using RCU to traverse the list and therefore may be using in atomic * contexts. The @fn callback is invoked from RCU critical section and * thus must not sleep. */ diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 106fbac7f8c5..4d75062a6206 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -367,6 +367,7 @@ config KEYBOARD_MAPLE config KEYBOARD_MAX7359 tristate "Maxim MAX7359 Key Switch Controller" + select INPUT_MATRIXKMAP depends on I2C help If you say yes here you get support for the Maxim MAX7359 Key diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index a45267729dfc..6ed83cf8b74e 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -180,7 +180,7 @@ #define LOGIC2_STAT (1 << 7) /* ADP5589 only */ #define LOGIC1_STAT (1 << 6) #define LOCK_STAT (1 << 5) /* ADP5589 only */ -#define KEC 0xF +#define KEC 0x1F /* PIN_CONFIG_D Register */ #define C4_EXTEND_CFG (1 << 6) /* RESET2 */ @@ -726,7 +726,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad) pull_mask |= val << (2 * (i & 0x3)); - if (i == 3 || i == kpad->var->max_row_num) { + if (i % 4 == 3 || i == kpad->var->max_row_num) { ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A) + (i >> 2), pull_mask); pull_mask = 0; @@ -746,7 +746,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad) pull_mask |= val << (2 * (i & 0x3)); - if (i == 3 || i == kpad->var->max_col_num) { + if (i % 4 == 3 || i == kpad->var->max_col_num) { ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_C) + (i >> 2), pull_mask); diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c index 27ef29f8fe6a..b637f1af842e 100644 --- a/drivers/input/keyboard/clps711x-keypad.c +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -120,14 +120,9 @@ static int clps711x_keypad_probe(struct platform_device *pdev) for (i = 0; i < priv->row_count; i++) { struct clps711x_gpio_data *data = &priv->gpio_data[i]; - data->desc = devm_gpiod_get_index(dev, "row", i); - if (!data->desc) - return -EINVAL; - + data->desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN); if (IS_ERR(data->desc)) return PTR_ERR(data->desc); - - gpiod_direction_input(data->desc); } err = of_property_read_u32(np, "poll-interval", &poll_interval); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index faa6da53eba8..5091133b7b8e 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -84,26 +84,6 @@ static int max7359_read_reg(struct i2c_client *client, int reg) return ret; } -static void max7359_build_keycode(struct max7359_keypad *keypad, - const struct matrix_keymap_data *keymap_data) -{ - struct input_dev *input_dev = keypad->input_dev; - int i; - - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned int scancode = MATRIX_SCAN_CODE(row, col, - MAX7359_ROW_SHIFT); - unsigned short keycode = KEY_VAL(key); - - keypad->keycodes[scancode] = keycode; - __set_bit(keycode, input_dev->keybit); - } - __clear_bit(KEY_RESERVED, input_dev->keybit); -} - /* runs in an IRQ thread -- can (and will!) sleep */ static irqreturn_t max7359_interrupt(int irq, void *dev_id) { @@ -166,7 +146,6 @@ static void max7359_close(struct input_dev *dev) static void max7359_initialize(struct i2c_client *client) { max7359_write_reg(client, MAX7359_REG_CONFIG, - MAX7359_CFG_INTERRUPT | /* Irq clears after host read */ MAX7359_CFG_KEY_RELEASE | /* Key release enable */ MAX7359_CFG_WAKEUP); /* Key press wakeup enable */ @@ -233,7 +212,15 @@ static int max7359_probe(struct i2c_client *client, input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); - max7359_build_keycode(keypad, keymap_data); + error = matrix_keypad_build_keymap(keymap_data, NULL, + MAX7359_MAX_KEY_ROWS, + MAX7359_MAX_KEY_COLS, + keypad->keycodes, + input_dev); + if (error) { + dev_err(&client->dev, "failed to build keymap\n"); + return error; + } error = devm_request_threaded_irq(&client->dev, client->irq, NULL, max7359_interrupt, diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 6b9fdf6cf8e8..43e48dac7687 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -585,7 +585,7 @@ static const struct of_device_id samsung_keypad_dt_match[] = { MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); #endif -static struct platform_device_id samsung_keypad_driver_ids[] = { +static const struct platform_device_id samsung_keypad_driver_ids[] = { { .name = "samsung-keypad", .driver_data = KEYPAD_TYPE_SAMSUNG, diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index f42a543db043..623d451767e3 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -3,7 +3,7 @@ * Based on omap-keypad driver * * Copyright (C) 2010 ST Microelectronics - * Rajeev Kumar<rajeev-dlh.kumar@st.com> + * Rajeev Kumar <rajeevkumar.linux@gmail.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 4436ab1b9735..d4f0a817e858 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -610,6 +610,16 @@ config INPUT_DA9055_ONKEY To compile this driver as a module, choose M here: the module will be called da9055_onkey. +config INPUT_DA9063_ONKEY + tristate "Dialog DA9063 OnKey" + depends on MFD_DA9063 + help + Support the ONKEY of Dialog DA9063 Power Management IC as an + input device reporting power button statue. + + To compile this driver as a module, choose M here: the module + will be called da9063_onkey. + config INPUT_DM355EVM tristate "TI DaVinci DM355 EVM Keypad and IR Remote" depends on MFD_DM355EVM_MSP @@ -775,6 +785,17 @@ config INPUT_DRV260X_HAPTICS To compile this driver as a module, choose M here: the module will be called drv260x-haptics. +config INPUT_DRV2665_HAPTICS + tristate "TI DRV2665 haptics support" + depends on INPUT && I2C + select INPUT_FF_MEMLESS + select REGMAP_I2C + help + Say Y to enable support for the TI DRV2665 haptics driver. + + To compile this driver as a module, choose M here: the + module will be called drv2665-haptics. + config INPUT_DRV2667_HAPTICS tristate "TI DRV2667 haptics support" depends on INPUT && I2C @@ -784,6 +805,6 @@ config INPUT_DRV2667_HAPTICS Say Y to enable support for the TI DRV2667 haptics driver. To compile this driver as a module, choose M here: the - module will be called drv260x-haptics. + module will be called drv2667-haptics. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 78ba4c1b8532..53df07dcc23c 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -25,9 +25,11 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o +obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o +obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index 470bfd6f0830..bdb5d03b296e 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -10,6 +10,7 @@ #include <linux/input.h> /* BUS_I2C */ #include <linux/i2c.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/types.h> #include <linux/pm.h> #include "adxl34x.h" @@ -135,11 +136,31 @@ static const struct i2c_device_id adxl34x_id[] = { MODULE_DEVICE_TABLE(i2c, adxl34x_id); +#ifdef CONFIG_OF +static const struct of_device_id adxl34x_of_id[] = { + /* + * The ADXL346 is backward-compatible with the ADXL345. Differences are + * handled by runtime detection of the device model, there's thus no + * need for listing the "adi,adxl346" compatible value explicitly. + */ + { .compatible = "adi,adxl345", }, + /* + * Deprecated, DT nodes should use one or more of the device-specific + * compatible values "adi,adxl345" and "adi,adxl346". + */ + { .compatible = "adi,adxl34x", }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl34x_of_id); +#endif + static struct i2c_driver adxl34x_driver = { .driver = { .name = "adxl34x", .owner = THIS_MODULE, .pm = &adxl34x_i2c_pm, + .of_match_table = of_match_ptr(adxl34x_of_id), }, .probe = adxl34x_i2c_probe, .remove = adxl34x_i2c_remove, diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c new file mode 100644 index 000000000000..f577585ef999 --- /dev/null +++ b/drivers/input/misc/da9063_onkey.c @@ -0,0 +1,226 @@ +/* + * OnKey device driver for DA9063 + * Copyright (C) 2015 Dialog Semiconductor Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <linux/mfd/da9063/core.h> +#include <linux/mfd/da9063/pdata.h> +#include <linux/mfd/da9063/registers.h> + +struct da9063_onkey { + struct da9063 *hw; + struct delayed_work work; + struct input_dev *input; + struct device *dev; + bool key_power; +}; + +static void da9063_poll_on(struct work_struct *work) +{ + struct da9063_onkey *onkey = container_of(work, struct da9063_onkey, + work.work); + unsigned int val; + int fault_log = 0; + bool poll = true; + int error; + + /* Poll to see when the pin is released */ + error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); + if (error) { + dev_err(onkey->dev, + "Failed to read ON status: %d\n", error); + goto err_poll; + } + + if (!(val & DA9063_NONKEY)) { + error = regmap_update_bits(onkey->hw->regmap, + DA9063_REG_CONTROL_B, + DA9063_NONKEY_LOCK, 0); + if (error) { + dev_err(onkey->dev, + "Failed to reset the Key Delay %d\n", error); + goto err_poll; + } + + input_report_key(onkey->input, KEY_POWER, 0); + input_sync(onkey->input); + + poll = false; + } + + /* + * If the fault log KEY_RESET is detected, then clear it + * and shut down the system. + */ + error = regmap_read(onkey->hw->regmap, + DA9063_REG_FAULT_LOG, &fault_log); + if (error) { + dev_warn(&onkey->input->dev, + "Cannot read FAULT_LOG: %d\n", error); + } else if (fault_log & DA9063_KEY_RESET) { + error = regmap_write(onkey->hw->regmap, + DA9063_REG_FAULT_LOG, + DA9063_KEY_RESET); + if (error) { + dev_warn(&onkey->input->dev, + "Cannot reset KEY_RESET fault log: %d\n", + error); + } else { + /* at this point we do any S/W housekeeping + * and then send shutdown command + */ + dev_dbg(&onkey->input->dev, + "Sending SHUTDOWN to DA9063 ...\n"); + error = regmap_write(onkey->hw->regmap, + DA9063_REG_CONTROL_F, + DA9063_SHUTDOWN); + if (error) + dev_err(&onkey->input->dev, + "Cannot SHUTDOWN DA9063: %d\n", + error); + } + } + +err_poll: + if (poll) + schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); +} + +static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) +{ + struct da9063_onkey *onkey = data; + unsigned int val; + int error; + + error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); + if (onkey->key_power && !error && (val & DA9063_NONKEY)) { + input_report_key(onkey->input, KEY_POWER, 1); + input_sync(onkey->input); + schedule_delayed_work(&onkey->work, 0); + dev_dbg(onkey->dev, "KEY_POWER pressed.\n"); + } else { + input_report_key(onkey->input, KEY_SLEEP, 1); + input_sync(onkey->input); + input_report_key(onkey->input, KEY_SLEEP, 0); + input_sync(onkey->input); + dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n"); + } + + return IRQ_HANDLED; +} + +static void da9063_cancel_poll(void *data) +{ + struct da9063_onkey *onkey = data; + + cancel_delayed_work_sync(&onkey->work); +} + +static int da9063_onkey_probe(struct platform_device *pdev) +{ + struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); + struct da9063_pdata *pdata = dev_get_platdata(da9063->dev); + struct da9063_onkey *onkey; + int irq; + int error; + + onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey), + GFP_KERNEL); + if (!onkey) { + dev_err(&pdev->dev, "Failed to allocate memory.\n"); + return -ENOMEM; + } + + onkey->dev = &pdev->dev; + onkey->hw = da9063; + + if (pdata) + onkey->key_power = pdata->key_power; + else + onkey->key_power = + |