key: fix wakeup issues of gpiokey and adckey in freeze mode [1/1]

PD#SWPL-14597

Problem:
- it is risky to replace directly system_freezable_wq with system_wq
for input poll device.
- the system can't be awakened by gpiokey or adckey when system in
freeze mode.

Solution:
- because of the input poll-device can't compatible freeze mode, we
replace it with input device.
- perfect wakeup operation when system works in freeze mode.

Verify:
test pass on ac200

Change-Id: I81c867da7325480712aa5903289e02ebb20d6f07
Signed-off-by: Xingyu Chen <xingyu.chen@amlogic.com>
This commit is contained in:
Xingyu Chen
2019-09-24 13:15:15 +08:00
committed by Luke Go
parent 5c7241b365
commit c705f69697
6 changed files with 161 additions and 142 deletions

View File

@@ -1670,12 +1670,11 @@ static int __maybe_unused meson_sar_adc_suspend(struct device *dev)
struct iio_dev *indio_dev = dev_get_drvdata(dev);
int ret;
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
#ifdef CONFIG_AMLOGIC_ADC_KEYPADS
if (keep_adc_alive())
if (meson_adc_is_alive_freeze())
return 0;
#endif
#endif
if (iio_buffer_enabled(indio_dev)) {
ret = meson_sar_adc_buffer_predisable(indio_dev);
if (ret)
@@ -1694,12 +1693,11 @@ static int __maybe_unused meson_sar_adc_resume(struct device *dev)
struct iio_dev *indio_dev = dev_get_drvdata(dev);
int ret;
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
#ifdef CONFIG_AMLOGIC_ADC_KEYPADS
if (keep_adc_alive())
if (meson_adc_is_alive_freeze())
return 0;
#endif
#endif
ret = meson_sar_adc_hw_enable(indio_dev);
if (ret)
return ret;

View File

@@ -28,39 +28,32 @@
#include <linux/string.h>
#include <linux/of.h>
#include <linux/iio/consumer.h>
#include <linux/input-polldev.h>
#include <linux/amlogic/scpi_protocol.h>
#include <linux/amlogic/pm.h>
#include <linux/pm_wakeup.h>
#include "adc_keypad.h"
#include <linux/amlogic/scpi_protocol.h>
#define POLL_INTERVAL_DEFAULT 25
#define KEY_JITTER_COUNT 1
#define TMP_BUF_MAX 128
#define ADC_PWRKEY_NAME "power"
static char adc_key_mode_name[MAX_NAME_LEN] = "abcdef";
static char kernelkey_en_name[MAX_NAME_LEN] = "abcdef";
static bool keypad_enable_flag = true;
static char adc_key_mode = 2; /*no key can resume*/
static bool has_adc_power_key;
static bool freeze_mode_ignore_key;
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
bool keep_adc_alive(void)
bool meson_adc_is_alive_freeze(void)
{
return is_pm_freeze_mode() && ((adc_key_mode == 1) ||
((adc_key_mode == 0) && has_adc_power_key));
return has_adc_power_key && is_pm_freeze_mode();
}
EXPORT_SYMBOL(keep_adc_alive);
#endif
EXPORT_SYMBOL(meson_adc_is_alive_freeze);
static int meson_adc_kp_search_key(struct meson_adc_kp *kp)
{
struct adc_key *key;
int value, i;
if (freeze_mode_ignore_key)
return KEY_RESERVED;
mutex_lock(&kp->kp_lock);
for (i = 0; i < kp->chan_num; i++) {
if (iio_read_channel_processed(kp->pchan[kp->chan[i]],
@@ -81,17 +74,20 @@ static int meson_adc_kp_search_key(struct meson_adc_kp *kp)
return KEY_RESERVED;
}
static void meson_adc_kp_poll(struct input_polled_dev *dev)
static void meson_adc_kp_poll(struct work_struct *pwork)
{
struct meson_adc_kp *kp = dev->private;
struct meson_adc_kp *kp =
container_of(pwork, struct meson_adc_kp, work.work);
int code = meson_adc_kp_search_key(kp);
if (kp->report_code && kp->report_code != code) {
dev_info(&kp->poll_dev->input->dev,
"key %d up\n", kp->report_code);
input_report_key(kp->poll_dev->input, kp->report_code, 0);
input_sync(kp->poll_dev->input);
dev_info(&kp->input->dev,
"key %d up\n", kp->report_code);
input_report_key(kp->input, kp->report_code, 0);
input_sync(kp->input);
if (kp->report_code == kp->pwrkey_code)
pm_relax(kp->input->dev.parent);
kp->report_code = 0;
}
@@ -103,10 +99,13 @@ static void meson_adc_kp_poll(struct input_polled_dev *dev)
kp->count++;
else {
if (keypad_enable_flag && kp->report_code != code) {
dev_info(&kp->poll_dev->input->dev,
"key %d down\n", code);
input_report_key(kp->poll_dev->input, code, 1);
input_sync(kp->poll_dev->input);
dev_info(&kp->input->dev,
"key %d down\n", code);
input_report_key(kp->input, code, 1);
input_sync(kp->input);
if (code == kp->pwrkey_code)
pm_stay_awake(kp->input->dev.parent);
kp->report_code = code;
}
kp->count = 0;
@@ -114,6 +113,8 @@ static void meson_adc_kp_poll(struct input_polled_dev *dev)
kp->prev_code = code;
}
queue_delayed_work(system_wq, &kp->work,
msecs_to_jiffies(kp->poll_period));
}
static void send_data_to_bl301(void)
@@ -122,15 +123,12 @@ static void send_data_to_bl301(void)
if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_POWER")) {
val = 0; /*only power key resume*/
scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
adc_key_mode = 0;
} else if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_ANY")) {
val = 1; /*any key resume*/
scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
adc_key_mode = 1;
} else if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_NONE")) {
val = 2; /*no key can resume*/
scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
adc_key_mode = 2;
}
}
@@ -154,6 +152,7 @@ static void meson_adc_kp_get_valid_chan(struct meson_adc_kp *kp)
{
unsigned char incr;
struct adc_key *key;
bool pwrkey_update_flg = false;
mutex_lock(&kp->kp_lock);
kp->chan_num = 0; /*recalculate*/
@@ -168,7 +167,18 @@ static void meson_adc_kp_get_valid_chan(struct meson_adc_kp *kp)
kp->chan[kp->chan_num++] = key->chan;
}
}
if (!strcmp(key->name, ADC_PWRKEY_NAME)) {
kp->pwrkey_code = key->code;
has_adc_power_key = true;
pwrkey_update_flg = true;
}
}
if (!pwrkey_update_flg) {
kp->pwrkey_code = KEY_RESERVED;
has_adc_power_key = false;
}
mutex_unlock(&kp->kp_lock);
}
@@ -263,8 +273,6 @@ static int meson_adc_kp_get_devtree_pdata(struct platform_device *pdev,
state = -EINVAL;
goto err;
}
if (key->code == KEY_POWER)
has_adc_power_key = true;
ret = of_property_read_u32_index(pdev->dev.of_node,
"key_chan", cnt, &key->chan);
@@ -353,7 +361,7 @@ static ssize_t table_store(struct class *cls, struct class_attribute *attr,
{
struct meson_adc_kp *kp = container_of(cls,
struct meson_adc_kp, kp_class);
struct device *dev = kp->poll_dev->input->dev.parent;
struct device *dev = kp->input->dev.parent;
struct adc_key *dkey;
struct adc_key *key;
struct adc_key *key_tmp;
@@ -384,7 +392,6 @@ static ssize_t table_store(struct class *cls, struct class_attribute *attr,
/*write "null" or "NULL" to clean up all key table*/
if (strcasecmp("null", nbuf) == 0) {
meson_adc_kp_list_free(kp);
has_adc_power_key = false;
return count;
}
@@ -410,6 +417,7 @@ static ssize_t table_store(struct class *cls, struct class_attribute *attr,
state = -EINVAL;
goto err;
}
pval = strsep(&pbuf, ":"); /*channel*/
if (pval)
if (kstrtoint(pval, 0, &dkey->chan) < 0) {
@@ -467,17 +475,16 @@ static ssize_t table_store(struct class *cls, struct class_attribute *attr,
dev_info(dev, "del older key => %s:%d:%d:%d:%d\n",
key->name, key->code, key->chan,
key->value, key->tolerance);
clear_bit(key->code, kp->poll_dev->input->keybit);
clear_bit(key->code, kp->input->keybit);
list_del(&key->list);
kfree(key);
}
}
if (dkey->code == KEY_POWER)
has_adc_power_key = true;
set_bit(dkey->code, kp->poll_dev->input->keybit);
set_bit(dkey->code, kp->input->keybit);
list_add_tail(&dkey->list, &kp->adckey_head);
dev_info(dev, "add newer key => %s:%d:%d:%d:%d\n", dkey->name,
dkey->code, dkey->chan, dkey->value, dkey->tolerance);
mutex_unlock(&kp->kp_lock);
meson_adc_kp_get_valid_chan(kp);
@@ -500,13 +507,12 @@ static void meson_adc_kp_init_keybit(struct meson_adc_kp *kp)
list_for_each_entry(key, &kp->adckey_head, list)
set_bit(key->code,
kp->poll_dev->input->keybit); /*set event code*/
kp->input->keybit); /*set event code*/
}
static int meson_adc_kp_probe(struct platform_device *pdev)
{
struct meson_adc_kp *kp;
struct input_dev *input;
int ret = 0;
send_data_to_bl301();
@@ -526,37 +532,31 @@ static int meson_adc_kp_probe(struct platform_device *pdev)
if (ret)
goto err;
/*alloc input poll device*/
kp->poll_dev = devm_input_allocate_polled_device(&pdev->dev);
if (!kp->poll_dev) {
dev_err(&pdev->dev, "alloc input poll device failed!\n");
/*alloc input device*/
kp->input = input_allocate_device();
if (!kp->input) {
dev_err(&pdev->dev, "alloc input device failed!\n");
ret = -ENOMEM;
goto err;
}
kp->poll_dev->poll = meson_adc_kp_poll;
kp->poll_dev->poll_interval = kp->poll_period;
kp->poll_dev->private = kp;
input = kp->poll_dev->input;
set_bit(EV_KEY, input->evbit);
set_bit(EV_REP, input->evbit);
set_bit(EV_KEY, kp->input->evbit);
meson_adc_kp_init_keybit(kp);
input->name = "adc_keypad";
input->phys = "adc_keypad/input0";
input->dev.parent = &pdev->dev;
kp->input->name = "adc_keypad";
kp->input->phys = "adc_keypad/input0";
kp->input->dev.parent = &pdev->dev;
input->id.bustype = BUS_ISA;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
kp->input->id.bustype = BUS_ISA;
kp->input->id.vendor = 0x0001;
kp->input->id.product = 0x0001;
kp->input->id.version = 0x0100;
input->rep[REP_DELAY] = 0xffffffff;
input->rep[REP_PERIOD] = 0xffffffff;
kp->input->rep[REP_DELAY] = 0xffffffff;
kp->input->rep[REP_PERIOD] = 0xffffffff;
input->keycodesize = sizeof(unsigned short);
input->keycodemax = 0x1ff;
kp->input->keycodesize = sizeof(unsigned short);
kp->input->keycodemax = 0x1ff;
/*init class*/
kp->kp_class.name = DRIVE_NAME;
@@ -565,24 +565,33 @@ static int meson_adc_kp_probe(struct platform_device *pdev)
ret = class_register(&kp->kp_class);
if (ret) {
dev_err(&pdev->dev, "fail to create adc keypad class.\n");
goto err;
}
/*register input poll device*/
ret = input_register_polled_device(kp->poll_dev);
if (ret) {
dev_err(&pdev->dev,
"unable to register keypad input poll device.\n");
goto err1;
}
INIT_DELAYED_WORK(&kp->work, meson_adc_kp_poll);
/*register input device*/
ret = input_register_device(kp->input);
if (ret) {
dev_err(&pdev->dev,
"unable to register keypad input device.\n");
goto err2;
}
device_init_wakeup(&pdev->dev, true);
mod_delayed_work(system_wq, &kp->work,
msecs_to_jiffies(kp->poll_period));
return ret;
err1:
err2:
class_unregister(&kp->kp_class);
err1:
input_free_device(kp->input);
err:
meson_adc_kp_list_free(kp);
kfree(kp);
return ret;
}
@@ -591,51 +600,54 @@ static int meson_adc_kp_remove(struct platform_device *pdev)
struct meson_adc_kp *kp = platform_get_drvdata(pdev);
class_unregister(&kp->kp_class);
cancel_delayed_work(&kp->poll_dev->work);
cancel_delayed_work_sync(&kp->work);
input_unregister_device(kp->input);
input_free_device(kp->input);
meson_adc_kp_list_free(kp);
kfree(kp);
return 0;
}
static int meson_adc_kp_suspend(struct platform_device *pdev,
pm_message_t state)
{
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
if (keep_adc_alive())
struct meson_adc_kp *kp = platform_get_drvdata(pdev);
if (meson_adc_is_alive_freeze())
return 0;
#endif
freeze_mode_ignore_key = true;
cancel_delayed_work(&kp->work);
return 0;
}
static int meson_adc_kp_resume(struct platform_device *pdev)
{
struct adc_key *key;
struct meson_adc_kp *kp = platform_get_drvdata(pdev);
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
if (keep_adc_alive())
if (meson_adc_is_alive_freeze())
return 0;
#endif
freeze_mode_ignore_key = false;
mod_delayed_work(system_wq, &kp->work,
msecs_to_jiffies(kp->poll_period));
if (get_resume_method() == POWER_KEY_WAKEUP) {
list_for_each_entry(key, &kp->adckey_head, list) {
if (key->code == KEY_POWER) {
dev_info(&pdev->dev, "adc keypad wakeup\n");
if (kp->pwrkey_code != KEY_RESERVED) {
dev_info(&pdev->dev, "adc keypad wakeup\n");
input_report_key(kp->poll_dev->input,
KEY_POWER, 1);
input_sync(kp->poll_dev->input);
input_report_key(kp->poll_dev->input,
KEY_POWER, 0);
input_sync(kp->poll_dev->input);
if (scpi_clr_wakeup_reason())
pr_debug("clr adc wakeup reason fail.\n");
input_report_key(kp->input,
kp->pwrkey_code, 1);
input_sync(kp->input);
input_report_key(kp->input,
kp->pwrkey_code, 0);
input_sync(kp->input);
break;
}
if (scpi_clr_wakeup_reason())
pr_debug("clr adc wakeup reason fail.\n");
}
}
return 0;
}
@@ -643,7 +655,7 @@ static void meson_adc_kp_shutdown(struct platform_device *pdev)
{
struct meson_adc_kp *kp = platform_get_drvdata(pdev);
cancel_delayed_work(&kp->poll_dev->work);
cancel_delayed_work(&kp->work);
}
static const struct of_device_id key_dt_match[] = {

View File

@@ -25,9 +25,6 @@
#include <linux/mutex.h>
#include <linux/iio/consumer.h>
#include <dt-bindings/iio/adc/amlogic-saradc.h>
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
#include <linux/amlogic/pm.h>
#endif
#define DRIVE_NAME "adc_keypad"
#define MAX_NAME_LEN 20
@@ -58,14 +55,13 @@ struct meson_adc_kp {
unsigned int report_code;
unsigned int prev_code;
unsigned int poll_period; /*key scan period*/
unsigned int pwrkey_code;
struct mutex kp_lock;
struct class kp_class;
struct list_head adckey_head;
struct input_polled_dev *poll_dev;
struct delayed_work work;
struct input_dev *input;
struct iio_channel *pchan[SARADC_CH_NUM];
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
struct early_suspend early_suspend;
#endif
};
#endif

View File

@@ -29,6 +29,7 @@
#undef pr_fmt
#define pr_fmt(fmt) "gpio-keypad: " fmt
#define GPIO_PWRKEY_NAME "power"
#define DEFAULT_SCAN_PERION 20
#define DEFAULT_POLL_MODE 0
#define KEY_JITTER_COUNT 1
@@ -51,6 +52,7 @@ struct gpio_keypad {
int current_irq;
int count;
int index;
u32 pwrkey_code;
struct pin_desc *key;
struct pin_desc *current_key;
struct timer_list polling_timer;
@@ -124,7 +126,12 @@ static void report_key_code(struct gpio_keypad *keypad, int gpio_val)
key->code);
}
input_sync(keypad->input_dev);
if (key->code == keypad->pwrkey_code) {
if (key->current_status)
pm_relax(keypad->input_dev->dev.parent);
else
pm_stay_awake(keypad->input_dev->dev.parent);
}
keypad->count = 0;
}
}
@@ -173,6 +180,7 @@ static int meson_gpio_kp_probe(struct platform_device *pdev)
struct resource *res;
int val, err;
void __iomem *ao_reg;
bool pwrkey_update_flg = false;
if (!(pdev->dev.of_node)) {
dev_err(&pdev->dev,
@@ -265,9 +273,18 @@ static int meson_gpio_kp_probe(struct platform_device *pdev)
"find key_name=%d finished\n", i);
return -EINVAL;
}
if (!strcmp(keypad->key[i].name, GPIO_PWRKEY_NAME)) {
keypad->pwrkey_code = keypad->key[i].code;
pwrkey_update_flg = true;
}
gpiod_direction_input(keypad->key[i].desc);
gpiod_set_pull(keypad->key[i].desc, GPIOD_PULL_UP);
}
if (!pwrkey_update_flg)
keypad->pwrkey_code = KEY_RESERVED;
//input
input_dev = input_allocate_device();
if (!input_dev)
@@ -300,6 +317,9 @@ static int meson_gpio_kp_probe(struct platform_device *pdev)
keypad->index = -1;
setup_timer(&(keypad->polling_timer),
polling_timer_handler, (unsigned long) keypad);
device_init_wakeup(&pdev->dev, true);
if (keypad->use_irq) {
for (i = 0; i < keypad->key_size; i++) {
keypad->key[i].irq_num =
@@ -339,21 +359,20 @@ static const struct of_device_id key_dt_match[] = {
{},
};
static bool meson_gpio_pwrkey_is_valid(struct gpio_keypad *pdata)
{
return (pdata->pwrkey_code != KEY_RESERVED);
}
static int meson_gpio_kp_suspend(struct platform_device *dev,
pm_message_t state)
{
struct gpio_keypad *pdata;
int i;
pdata = (struct gpio_keypad *)platform_get_drvdata(dev);
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
if (is_pm_freeze_mode()) {
for (i = 0; i < pdata->key_size; i++) {
if (pdata->key[i].code == KEY_POWER)
return 0;
}
}
#endif
if (meson_gpio_pwrkey_is_valid(pdata) && is_pm_freeze_mode())
return 0;
if (!pdata->use_irq)
del_timer(&(pdata->polling_timer));
return 0;
@@ -361,37 +380,30 @@ static int meson_gpio_kp_suspend(struct platform_device *dev,
static int meson_gpio_kp_resume(struct platform_device *dev)
{
int i;
struct gpio_keypad *pdata;
pdata = (struct gpio_keypad *)platform_get_drvdata(dev);
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
if (is_pm_freeze_mode()) {
for (i = 0; i < pdata->key_size; i++) {
if (pdata->key[i].code == KEY_POWER)
return 0;
}
}
#endif
if (meson_gpio_pwrkey_is_valid(pdata) && is_pm_freeze_mode())
return 0;
if (!pdata->use_irq)
mod_timer(&(pdata->polling_timer),
jiffies+msecs_to_jiffies(5));
if (get_resume_method() == POWER_KEY_WAKEUP) {
for (i = 0; i < pdata->key_size; i++) {
if (pdata->key[i].code == KEY_POWER) {
pr_info("gpio keypad wakeup\n");
input_report_key(pdata->input_dev,
KEY_POWER, 1);
input_sync(pdata->input_dev);
input_report_key(pdata->input_dev,
KEY_POWER, 0);
input_sync(pdata->input_dev);
break;
}
if (meson_gpio_pwrkey_is_valid(pdata)) {
pr_info("gpio keypad wakeup\n");
input_report_key(pdata->input_dev,
pdata->pwrkey_code, 1);
input_sync(pdata->input_dev);
input_report_key(pdata->input_dev,
pdata->pwrkey_code, 0);
input_sync(pdata->input_dev);
if (scpi_clr_wakeup_reason())
pr_debug("clr gpio kp wakeup reason fail.\n");
}
if (scpi_clr_wakeup_reason())
pr_debug("clr gpio kp wakeup reason fail.\n");
}
return 0;
}

View File

@@ -30,7 +30,7 @@ static void input_polldev_queue_work(struct input_polled_dev *dev)
if (delay >= HZ)
delay = round_jiffies_relative(delay);
queue_delayed_work(system_wq, &dev->work, delay);
queue_delayed_work(system_freezable_wq, &dev->work, delay);
}
static void input_polled_device_work(struct work_struct *work)

View File

@@ -33,6 +33,9 @@
#define ETH_PHY_WAKEUP 10
extern unsigned int get_resume_method(void);
extern unsigned int is_pm_freeze_mode(void);
#ifdef CONFIG_AMLOGIC_ADC_KEYPADS
bool meson_adc_is_alive_freeze(void);
#endif
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
enum {
@@ -52,9 +55,7 @@ extern void register_early_suspend(struct early_suspend *handler);
extern void unregister_early_suspend(struct early_suspend *handler);
extern unsigned int lgcy_early_suspend_init(void);
extern unsigned int is_pm_freeze_mode(void);
#ifdef CONFIG_AMLOGIC_ADC_KEYPADS
extern bool keep_adc_alive(void);
#endif
#endif //CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND