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>
This commit is contained in:
paris_yeh 2015-04-30 11:23:10 +08:00 committed by Ed Tam
parent b5b6b5c3df
commit 040354b953
2 changed files with 62 additions and 0 deletions

View file

@ -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");

View file

@ -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