From 040354b953a91accf776feac50a08b8f0f680ce1 Mon Sep 17 00:00:00 2001 From: paris_yeh <paris_yeh@asus.com> Date: Thu, 30 Apr 2015 11:23:10 +0800 Subject: [PATCH] keys: notify system preparing to force reset if resetkey is defined On flo/deb hardware platform, the power key button is able to reset whole system forcely via predefined timer at PMIC side. Add notifying call chains to let registered drivers have a chance to prepare data backup before power is cut off by PMIC firmware. Change-Id: I25fe00020950f0d4208caffda172f46396082a4d Signed-off-by: paris_yeh <paris_yeh@asus.com> --- drivers/input/keyboard/gpio_keys.c | 52 ++++++++++++++++++++++++++++++ include/linux/gpio_keys.h | 10 ++++++ 2 files changed, 62 insertions(+) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 9f03500288b1..1d0fb42e2f51 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 a7e977ff4abf..d02ba7b922d3 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 -- GitLab