diff options
author | Robin Gong <b38343@freescale.com> | 2014-04-02 04:55:31 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:58:15 -0400 |
commit | 8cf20b3a736feeb814c0d32e15fdb9ee575ddbd3 (patch) | |
tree | f406e952e1ccae44e1103170d3c1a7823f0c193f | |
parent | 6a824e342dfeca4ff3a8648569408878c7d2a52c (diff) |
ENGR00306653-2 input: keyboad: snvs_pwrkey: add snvs power key driver
add snvs power key driver since ic team has fix some issues of SNVS on i.mx6sx
Signed-off-by: Robin Gong <b38343@freescale.com>
-rw-r--r-- | Documentation/devicetree/bindings/input/snvs-pwrkey.txt | 26 | ||||
-rw-r--r-- | drivers/input/keyboard/Kconfig | 7 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/snvs_pwrkey.c | 221 |
4 files changed, 255 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/snvs-pwrkey.txt b/Documentation/devicetree/bindings/input/snvs-pwrkey.txt new file mode 100644 index 000000000000..8f0826f5817f --- /dev/null +++ b/Documentation/devicetree/bindings/input/snvs-pwrkey.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | * Freescale i.MX SNVS powerkey device tree bindings | ||
2 | |||
3 | The snvs-pwrkey is designed to enable POWER key function which controlled | ||
4 | by SNVS ONOFF, the driver can report the status of POWER key and wakeup | ||
5 | system if pressed after system suspend. | ||
6 | |||
7 | Required SoC Specific Properties: | ||
8 | - compatible: Should be "fsl,imx6sx-snvs-pwrkey". | ||
9 | |||
10 | - reg: Physical base address of the SNVS and length of memory mapped | ||
11 | region. | ||
12 | |||
13 | - interrupts: The SNVS interrupt number to the CPU(s). | ||
14 | |||
15 | - fsl,keycode: Keycode to emit, KEY_POWER by default. | ||
16 | |||
17 | - fsl,wakeup: Button can wake-up the system | ||
18 | |||
19 | Example: | ||
20 | snvs-pwrkey@0x020cc000 { | ||
21 | compatible = "fsl,imx6sx-snvs-pwrkey"; | ||
22 | reg = <0x020cc000 0x4000>; | ||
23 | interrupts = <0 4 0x4>; | ||
24 | fsl,keycode = <116>; /* KEY_POWER */ | ||
25 | fsl,wakeup; | ||
26 | }; | ||
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 292f3240e67b..c834583539f9 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig | |||
@@ -388,6 +388,13 @@ config KEYBOARD_MPR121 | |||
388 | To compile this driver as a module, choose M here: the | 388 | To compile this driver as a module, choose M here: the |
389 | module will be called mpr121_touchkey. | 389 | module will be called mpr121_touchkey. |
390 | 390 | ||
391 | config KEYBOARD_SNVS_PWRKEY | ||
392 | tristate "IMX6SX SNVS Power Key Driver" | ||
393 | depends on SOC_IMX6SX | ||
394 | help | ||
395 | This is the snvs powerkey driver for the Freescale i.MX6SX application | ||
396 | processors. | ||
397 | |||
391 | config KEYBOARD_IMX | 398 | config KEYBOARD_IMX |
392 | tristate "IMX keypad support" | 399 | tristate "IMX keypad support" |
393 | depends on ARCH_MXC | 400 | depends on ARCH_MXC |
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 0c43e8cf8d0e..7b5379ffbc55 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile | |||
@@ -46,6 +46,7 @@ obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o | |||
46 | obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o | 46 | obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o |
47 | obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o | 47 | obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o |
48 | obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o | 48 | obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o |
49 | obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY) += snvs_pwrkey.o | ||
49 | obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o | 50 | obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o |
50 | obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o | 51 | obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o |
51 | obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o | 52 | obj-$(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..f00c21290afd --- /dev/null +++ b/drivers/input/keyboard/snvs_pwrkey.c | |||
@@ -0,0 +1,221 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * The code contained herein is licensed under the GNU General Public | ||
5 | * License. You may obtain a copy of the GNU General Public License | ||
6 | * Version 2 or later at the following locations: | ||
7 | * | ||
8 | * http://www.opensource.org/licenses/gpl-license.html | ||
9 | * http://www.gnu.org/copyleft/gpl.html | ||
10 | */ | ||
11 | |||
12 | #include <linux/device.h> | ||
13 | #include <linux/err.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/input.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/jiffies.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/of_address.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | |||
25 | #define SNVS_LPSR_REG 0x4C /* LP Status Register */ | ||
26 | #define SNVS_LPCR_REG 0x38 /* LP Control Register */ | ||
27 | #define SNVS_HPSR_REG 0x14 | ||
28 | #define SNVS_HPSR_BTN (0x1 << 6) | ||
29 | #define SNVS_LPSR_SPO (0x1 << 18) | ||
30 | #define SNVS_LPCR_DEP_EN (0x1 << 5) | ||
31 | |||
32 | struct pwrkey_drv_data { | ||
33 | void __iomem *ioaddr; | ||
34 | int irq; | ||
35 | int keycode; | ||
36 | int keystate; /* 1:pressed */ | ||
37 | int wakeup; | ||
38 | struct timer_list check_timer; | ||
39 | struct input_dev *input; | ||
40 | }; | ||
41 | |||
42 | static void imx_imx_snvs_check_for_events(unsigned long data) | ||
43 | { | ||
44 | struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data; | ||
45 | struct input_dev *input = pdata->input; | ||
46 | void __iomem *ioaddr = pdata->ioaddr; | ||
47 | u32 state; | ||
48 | |||
49 | state = ((readl_relaxed(ioaddr + SNVS_HPSR_REG) & SNVS_HPSR_BTN) ? | ||
50 | 1 : 0); | ||
51 | |||
52 | /* only report new event if status changed */ | ||
53 | if (state ^ pdata->keystate) { | ||
54 | pdata->keystate = state; | ||
55 | input_event(input, EV_KEY, pdata->keycode, state); | ||
56 | input_sync(input); | ||
57 | } | ||
58 | |||
59 | /* repeat check if pressed long */ | ||
60 | if (state) { | ||
61 | mod_timer(&pdata->check_timer, | ||
62 | jiffies + msecs_to_jiffies(60)); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id) | ||
67 | { | ||
68 | struct platform_device *pdev = dev_id; | ||
69 | struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); | ||
70 | void __iomem *ioaddr = pdata->ioaddr; | ||
71 | u32 lp_status; | ||
72 | |||
73 | lp_status = readl_relaxed(ioaddr + SNVS_LPSR_REG); | ||
74 | if (lp_status & SNVS_LPSR_SPO) | ||
75 | mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(2)); | ||
76 | |||
77 | /* clear SPO status */ | ||
78 | writel_relaxed(lp_status, ioaddr + SNVS_LPSR_REG); | ||
79 | |||
80 | return IRQ_HANDLED; | ||
81 | } | ||
82 | |||
83 | static int imx_snvs_pwrkey_probe(struct platform_device *pdev) | ||
84 | { | ||
85 | struct pwrkey_drv_data *pdata = NULL; | ||
86 | struct input_dev *input = NULL; | ||
87 | struct device_node *np; | ||
88 | void __iomem *ioaddr; | ||
89 | u32 lp_cr; | ||
90 | int ret = 0; | ||
91 | |||
92 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | ||
93 | if (!pdata) | ||
94 | return -ENOMEM; | ||
95 | |||
96 | /* Get SNVS register Page */ | ||
97 | np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-snvs-pwrkey"); | ||
98 | if (!np) | ||
99 | return -ENODEV; | ||
100 | pdata->ioaddr = of_iomap(np, 0); | ||
101 | if (IS_ERR(pdata->ioaddr)) | ||
102 | return PTR_ERR(pdata->ioaddr); | ||
103 | |||
104 | if (of_property_read_u32(np, "fsl,keycode", &pdata->keycode)) { | ||
105 | pdata->keycode = KEY_POWER; | ||
106 | dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n"); | ||
107 | } | ||
108 | |||
109 | pdata->wakeup = !!of_get_property(np, "fsl,wakeup", NULL); | ||
110 | |||
111 | ioaddr = pdata->ioaddr; | ||
112 | pdata->irq = platform_get_irq(pdev, 0); | ||
113 | if (pdata->irq < 0) { | ||
114 | dev_err(&pdev->dev, "no irq defined in platform data\n"); | ||
115 | return -EINVAL; | ||
116 | } | ||
117 | |||
118 | ioaddr = pdata->ioaddr; | ||
119 | lp_cr = readl_relaxed(ioaddr + SNVS_LPCR_REG); | ||
120 | lp_cr |= SNVS_LPCR_DEP_EN, | ||
121 | writel_relaxed(lp_cr, ioaddr + SNVS_LPCR_REG); | ||
122 | |||
123 | setup_timer(&pdata->check_timer, | ||
124 | imx_imx_snvs_check_for_events, (unsigned long) pdata); | ||
125 | |||
126 | if (pdata->irq >= 0) { | ||
127 | ret = devm_request_irq(&pdev->dev, pdata->irq, | ||
128 | imx_snvs_pwrkey_interrupt, | ||
129 | IRQF_TRIGGER_HIGH, pdev->name, pdev); | ||
130 | if (ret) { | ||
131 | dev_err(&pdev->dev, "interrupt not available.\n"); | ||
132 | return ret; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | input = devm_input_allocate_device(&pdev->dev); | ||
137 | if (!input) { | ||
138 | dev_err(&pdev->dev, "failed to allocate the input device\n"); | ||
139 | return -ENOMEM; | ||
140 | } | ||
141 | |||
142 | input->name = pdev->name; | ||
143 | input->phys = "snvs-pwrkey/input0"; | ||
144 | input->id.bustype = BUS_HOST; | ||
145 | input->evbit[0] = BIT_MASK(EV_KEY); | ||
146 | |||
147 | input_set_capability(input, EV_KEY, pdata->keycode); | ||
148 | |||
149 | ret = input_register_device(input); | ||
150 | if (ret < 0) { | ||
151 | dev_err(&pdev->dev, "failed to register input device\n"); | ||
152 | input_free_device(input); | ||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | pdata->input = input; | ||
157 | platform_set_drvdata(pdev, pdata); | ||
158 | |||
159 | device_init_wakeup(&pdev->dev, pdata->wakeup); | ||
160 | |||
161 | dev_info(&pdev->dev, "i.MX snvs powerkey probed\n"); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int imx_snvs_pwrkey_remove(struct platform_device *pdev) | ||
167 | { | ||
168 | struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); | ||
169 | |||
170 | input_unregister_device(pdata->input); | ||
171 | del_timer_sync(&pdata->check_timer); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int imx_snvs_pwrkey_suspend(struct device *dev) | ||
177 | { | ||
178 | struct platform_device *pdev = to_platform_device(dev); | ||
179 | struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); | ||
180 | |||
181 | if (device_may_wakeup(&pdev->dev)) | ||
182 | enable_irq_wake(pdata->irq); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int imx_snvs_pwrkey_resume(struct device *dev) | ||
188 | { | ||
189 | struct platform_device *pdev = to_platform_device(dev); | ||
190 | struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); | ||
191 | |||
192 | if (device_may_wakeup(&pdev->dev)) | ||
193 | disable_irq_wake(pdata->irq); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static const struct of_device_id imx_snvs_pwrkey_ids[] = { | ||
199 | { .compatible = "fsl,imx6sx-snvs-pwrkey" }, | ||
200 | { /* sentinel */ } | ||
201 | }; | ||
202 | MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids); | ||
203 | |||
204 | static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend, | ||
205 | imx_snvs_pwrkey_resume); | ||
206 | |||
207 | static struct platform_driver imx_snvs_pwrkey_driver = { | ||
208 | .driver = { | ||
209 | .name = "snvs_pwrkey", | ||
210 | .owner = THIS_MODULE, | ||
211 | .pm = &imx_snvs_pwrkey_pm_ops, | ||
212 | .of_match_table = imx_snvs_pwrkey_ids, | ||
213 | }, | ||
214 | .probe = imx_snvs_pwrkey_probe, | ||
215 | .remove = imx_snvs_pwrkey_remove, | ||
216 | }; | ||
217 | module_platform_driver(imx_snvs_pwrkey_driver); | ||
218 | |||
219 | MODULE_AUTHOR("Freescale Semiconductor"); | ||
220 | MODULE_DESCRIPTION("i.MX snvs power key Driver"); | ||
221 | MODULE_LICENSE("GPL"); | ||