diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 9f03500288b1638be6466262a7923fea974b32e1..1d0fb42e2f51e2478b4f2376bb5d00ed4acdb1a6 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -40,6 +40,9 @@ struct gpio_button_data { spinlock_t lock; bool disabled; bool key_pressed; + struct work_struct reset_work; + struct timer_list reset_timer; + unsigned int timer_hwreset; /* in msecs */ }; struct gpio_keys_drvdata { @@ -332,6 +335,28 @@ static char *key_descriptions[] = { }; #endif +/* Routines for resetkey-transition notifications */ +static BLOCKING_NOTIFIER_HEAD(resetkey_chain_head); + +int register_resetkey_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&resetkey_chain_head, nb); +} +EXPORT_SYMBOL_GPL(register_resetkey_notifier); + +int unregister_resetkey_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&resetkey_chain_head, nb); +} +EXPORT_SYMBOL_GPL(unregister_resetkey_notifier); + +int resetkey_notifier_call_chain(unsigned long val) +{ + int ret = blocking_notifier_call_chain(&resetkey_chain_head, val, NULL); + + return notifier_to_errno(ret); +} + static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { const struct gpio_keys_button *button = bdata->button; @@ -363,6 +388,13 @@ static void gpio_keys_gpio_work_func(struct work_struct *work) pr_info("gpio_keys: %s %s\n", state ? "Pressed" : "Released", key_descriptions[button->code - KEY_VOLUMEDOWN]); #endif + if (button->can_reset) { + if (state) + mod_timer(&bdata->reset_timer, + jiffies + msecs_to_jiffies(bdata->timer_hwreset)); + else + del_timer_sync(&bdata->reset_timer); + } gpio_keys_gpio_report_event(bdata); } @@ -373,6 +405,19 @@ static void gpio_keys_gpio_timer(unsigned long _data) schedule_work(&bdata->work); } +static void reset_keys_work_func(struct work_struct *work) +{ + pr_info("gpio_keys: notify listeners pmic preparing to reset\n"); + resetkey_notifier_call_chain(RESETKEY_PREPARE_HWREST); +} + +static void powerkey_gpio_timer(unsigned long _data) +{ + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; + + schedule_work(&bdata->reset_work); +} + static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; @@ -493,6 +538,13 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, isr = gpio_keys_gpio_isr; irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + if (button->can_reset) { + INIT_WORK(&bdata->reset_work, reset_keys_work_func); + bdata->timer_hwreset = button->reset_interval; + setup_timer(&bdata->reset_timer, powerkey_gpio_timer, + (unsigned long)bdata); + } + } else { if (!button->irq) { dev_err(dev, "No IRQ specified\n"); diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index a7e977ff4abf060190118884b8a107c826c78feb..d02ba7b922d3f0a291901f41c58e6efa31689342 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -15,6 +15,8 @@ struct gpio_keys_button { bool can_disable; int value; /* axis value for EV_ABS */ unsigned int irq; /* Irq number in case of interrupt keys */ + bool can_reset; /* key is able to reset system */ + int reset_interval; /* reset key interval in msec */ }; struct gpio_keys_platform_data { @@ -28,4 +30,12 @@ struct gpio_keys_platform_data { const char *name; /* input device name */ }; + +#define RESETKEY_PRESS 0x0001 /* power key is pressed */ +#define RESETKEY_RELEASE 0x0002 /* power key is released */ +#define RESETKEY_PREPARE_HWREST 0x0003 /* Going to reset whole system by pmic*/ + +extern int register_resetkey_notifier(struct notifier_block *nb); +extern int unregister_resetkey_notifier(struct notifier_block *nb); + #endif