aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/input/keyboard/Kconfig11
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/snvs_pwrkey.c227
3 files changed, 239 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 4cd94fd6cbad..82a8fb50afac 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -401,6 +401,17 @@ config KEYBOARD_MPR121
401 To compile this driver as a module, choose M here: the 401 To compile this driver as a module, choose M here: the
402 module will be called mpr121_touchkey. 402 module will be called mpr121_touchkey.
403 403
404config KEYBOARD_SNVS_PWRKEY
405 tristate "IMX SNVS Power Key Driver"
406 depends on SOC_IMX6SX
407 depends on OF
408 help
409 This is the snvs powerkey driver for the Freescale i.MX application
410 processors that are newer than i.MX6 SX.
411
412 To compile this driver as a module, choose M here; the
413 module will be called snvs_pwrkey.
414
404config KEYBOARD_IMX 415config KEYBOARD_IMX
405 tristate "IMX keypad support" 416 tristate "IMX keypad support"
406 depends on ARCH_MXC 417 depends on ARCH_MXC
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index df28d5553c05..1d416ddf84e4 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
51obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o 51obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
52obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o 52obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
53obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o 53obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
54obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY) += snvs_pwrkey.o
54obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o 55obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
55obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o 56obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
56obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o 57obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
new file mode 100644
index 000000000000..512a1fc2a864
--- /dev/null
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -0,0 +1,227 @@
1/*
2 * Driver for the IMX SNVS ON/OFF Power Key
3 * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved.
4 *
5 * The code contained herein is licensed under the GNU General Public
6 * License. You may obtain a copy of the GNU General Public License
7 * Version 2 or later at the following locations:
8 *
9 * http://www.opensource.org/licenses/gpl-license.html
10 * http://www.gnu.org/copyleft/gpl.html
11 */
12
13#include <linux/device.h>
14#include <linux/err.h>
15#include <linux/init.h>
16#include <linux/input.h>
17#include <linux/interrupt.h>
18#include <linux/io.h>
19#include <linux/jiffies.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/of.h>
23#include <linux/of_address.h>
24#include <linux/platform_device.h>
25#include <linux/mfd/syscon.h>
26#include <linux/regmap.h>
27
28#define SNVS_LPSR_REG 0x4C /* LP Status Register */
29#define SNVS_LPCR_REG 0x38 /* LP Control Register */
30#define SNVS_HPSR_REG 0x14
31#define SNVS_HPSR_BTN BIT(6)
32#define SNVS_LPSR_SPO BIT(18)
33#define SNVS_LPCR_DEP_EN BIT(5)
34
35#define DEBOUNCE_TIME 30
36#define REPEAT_INTERVAL 60
37
38struct pwrkey_drv_data {
39 struct regmap *snvs;
40 int irq;
41 int keycode;
42 int keystate; /* 1:pressed */
43 int wakeup;
44 struct timer_list check_timer;
45 struct input_dev *input;
46};
47
48static void imx_imx_snvs_check_for_events(unsigned long data)
49{
50 struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data;
51 struct input_dev *input = pdata->input;
52 u32 state;
53
54 regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
55 state = state & SNVS_HPSR_BTN ? 1 : 0;
56
57 /* only report new event if status changed */
58 if (state ^ pdata->keystate) {
59 pdata->keystate = state;
60 input_event(input, EV_KEY, pdata->keycode, state);
61 input_sync(input);
62 pm_relax(pdata->input->dev.parent);
63 }
64
65 /* repeat check if pressed long */
66 if (state) {
67 mod_timer(&pdata->check_timer,
68 jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
69 }
70}
71
72static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
73{
74 struct platform_device *pdev = dev_id;
75 struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
76 u32 lp_status;
77
78 pm_wakeup_event(pdata->input->dev.parent, 0);
79
80 regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
81 if (lp_status & SNVS_LPSR_SPO)
82 mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
83
84 /* clear SPO status */
85 regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
86
87 return IRQ_HANDLED;
88}
89
90static void imx_snvs_pwrkey_act(void *pdata)
91{
92 struct pwrkey_drv_data *pd = pdata;
93
94 del_timer_sync(&pd->check_timer);
95}
96
97static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
98{
99 struct pwrkey_drv_data *pdata = NULL;
100 struct input_dev *input = NULL;
101 struct device_node *np;
102 int error;
103
104 /* Get SNVS register Page */
105 np = pdev->dev.of_node;
106 if (!np)
107 return -ENODEV;
108
109 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
110 if (!pdata)
111 return -ENOMEM;
112
113 pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");;
114
115 if (!pdata->snvs) {
116 dev_err(&pdev->dev, "Can't get snvs syscon\n");
117 return -ENODEV;
118 }
119
120 if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
121 pdata->keycode = KEY_POWER;
122 dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
123 }
124
125 pdata->wakeup = of_property_read_bool(np, "wakeup");
126
127 pdata->irq = platform_get_irq(pdev, 0);
128 if (pdata->irq < 0) {
129 dev_err(&pdev->dev, "no irq defined in platform data\n");
130 return -EINVAL;
131 }
132
133 regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
134
135 /* clear the unexpected interrupt before driver ready */
136 regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
137
138 setup_timer(&pdata->check_timer,
139 imx_imx_snvs_check_for_events, (unsigned long) pdata);
140
141 input = devm_input_allocate_device(&pdev->dev);
142 if (!input) {
143 dev_err(&pdev->dev, "failed to allocate the input device\n");
144 return -ENOMEM;
145 }
146
147 input->name = pdev->name;
148 input->phys = "snvs-pwrkey/input0";
149 input->id.bustype = BUS_HOST;
150
151 input_set_capability(input, EV_KEY, pdata->keycode);
152
153 /* input customer action to cancel release timer */
154 error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
155 if (error) {
156 dev_err(&pdev->dev, "failed to register remove action\n");
157 return error;
158 }
159
160 error = devm_request_irq(&pdev->dev, pdata->irq,
161 imx_snvs_pwrkey_interrupt,
162 0, pdev->name, pdev);
163
164 if (error) {
165 dev_err(&pdev->dev, "interrupt not available.\n");
166 return error;
167 }
168
169 error = input_register_device(input);
170 if (error < 0) {
171 dev_err(&pdev->dev, "failed to register input device\n");