Commit 1a26c920 authored by Robin van der Gracht's avatar Robin van der Gracht Committed by Dmitry Torokhov

Input: snvs_pwrkey - send key events for i.MX6 S, DL and Q

The first generation i.MX6 processors does not send an interrupt when the
power key is pressed. It sends a power down request interrupt if the key is
released before a hard shutdown (5 second press). This should allow
software to bring down the SoC safely.

For this driver to work as a regular power key with the older SoCs, we need
to send a keypress AND release when we get the power down request irq.
Signed-off-by: default avatarRobin van der Gracht <robin@protonic.nl>
Link: https://lore.kernel.org/r/20191125161210.8275-1-robin@protonic.nlSigned-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 71c296f6
...@@ -450,7 +450,7 @@ config KEYBOARD_SNVS_PWRKEY ...@@ -450,7 +450,7 @@ config KEYBOARD_SNVS_PWRKEY
depends on OF depends on OF
help help
This is the snvs powerkey driver for the Freescale i.MX application This is the snvs powerkey driver for the Freescale i.MX application
processors that are newer than i.MX6 SX. processors.
To compile this driver as a module, choose M here; the To compile this driver as a module, choose M here; the
module will be called snvs_pwrkey. module will be called snvs_pwrkey.
......
...@@ -19,15 +19,16 @@ ...@@ -19,15 +19,16 @@
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#define SNVS_LPSR_REG 0x4C /* LP Status Register */ #define SNVS_HPVIDR1_REG 0xF8
#define SNVS_LPCR_REG 0x38 /* LP Control Register */ #define SNVS_LPSR_REG 0x4C /* LP Status Register */
#define SNVS_HPSR_REG 0x14 #define SNVS_LPCR_REG 0x38 /* LP Control Register */
#define SNVS_HPSR_BTN BIT(6) #define SNVS_HPSR_REG 0x14
#define SNVS_LPSR_SPO BIT(18) #define SNVS_HPSR_BTN BIT(6)
#define SNVS_LPCR_DEP_EN BIT(5) #define SNVS_LPSR_SPO BIT(18)
#define SNVS_LPCR_DEP_EN BIT(5)
#define DEBOUNCE_TIME 30 #define DEBOUNCE_TIME 30
#define REPEAT_INTERVAL 60 #define REPEAT_INTERVAL 60
struct pwrkey_drv_data { struct pwrkey_drv_data {
struct regmap *snvs; struct regmap *snvs;
...@@ -37,6 +38,7 @@ struct pwrkey_drv_data { ...@@ -37,6 +38,7 @@ struct pwrkey_drv_data {
int wakeup; int wakeup;
struct timer_list check_timer; struct timer_list check_timer;
struct input_dev *input; struct input_dev *input;
u8 minor_rev;
}; };
static void imx_imx_snvs_check_for_events(struct timer_list *t) static void imx_imx_snvs_check_for_events(struct timer_list *t)
...@@ -67,13 +69,29 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id) ...@@ -67,13 +69,29 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
{ {
struct platform_device *pdev = dev_id; struct platform_device *pdev = dev_id;
struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
struct input_dev *input = pdata->input;
u32 lp_status; u32 lp_status;
pm_wakeup_event(pdata->input->dev.parent, 0); pm_wakeup_event(input->dev.parent, 0);
regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status); regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
if (lp_status & SNVS_LPSR_SPO) if (lp_status & SNVS_LPSR_SPO) {
mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME)); if (pdata->minor_rev == 0) {
/*
* The first generation i.MX6 SoCs only sends an
* interrupt on button release. To mimic power-key
* usage, we'll prepend a press event.
*/
input_report_key(input, pdata->keycode, 1);
input_sync(input);
input_report_key(input, pdata->keycode, 0);
input_sync(input);
pm_relax(input->dev.parent);
} else {
mod_timer(&pdata->check_timer,
jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
}
}
/* clear SPO status */ /* clear SPO status */
regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO); regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
...@@ -94,6 +112,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) ...@@ -94,6 +112,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
struct input_dev *input = NULL; struct input_dev *input = NULL;
struct device_node *np; struct device_node *np;
int error; int error;
u32 vid;
/* Get SNVS register Page */ /* Get SNVS register Page */
np = pdev->dev.of_node; np = pdev->dev.of_node;
...@@ -121,6 +140,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) ...@@ -121,6 +140,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
if (pdata->irq < 0) if (pdata->irq < 0)
return -EINVAL; return -EINVAL;
regmap_read(pdata->snvs, SNVS_HPVIDR1_REG, &vid);
pdata->minor_rev = vid & 0xff;
regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN); regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
/* clear the unexpected interrupt before driver ready */ /* clear the unexpected interrupt before driver ready */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment