Commit | Line | Data |
---|---|---|
fcd6eb50 JRO |
1 | /* |
2 | * Hisilicon PMIC powerkey driver | |
3 | * | |
4 | * Copyright (C) 2013 Hisilicon Ltd. | |
5 | * Copyright (C) 2015, 2016 Linaro Ltd. | |
6 | * | |
7 | * This file is subject to the terms and conditions of the GNU General | |
8 | * Public License. See the file "COPYING" in the main directory of this | |
9 | * archive for more details. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/platform_device.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/reboot.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/of_irq.h> | |
23 | #include <linux/input.h> | |
24 | #include <linux/slab.h> | |
25 | ||
26 | /* the held interrupt will trigger after 4 seconds */ | |
27 | #define MAX_HELD_TIME (4 * MSEC_PER_SEC) | |
28 | ||
29 | static irqreturn_t hi65xx_power_press_isr(int irq, void *q) | |
30 | { | |
31 | struct input_dev *input = q; | |
32 | ||
33 | pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); | |
34 | input_report_key(input, KEY_POWER, 1); | |
35 | input_sync(input); | |
36 | ||
37 | return IRQ_HANDLED; | |
38 | } | |
39 | ||
40 | static irqreturn_t hi65xx_power_release_isr(int irq, void *q) | |
41 | { | |
42 | struct input_dev *input = q; | |
43 | ||
44 | pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); | |
45 | input_report_key(input, KEY_POWER, 0); | |
46 | input_sync(input); | |
47 | ||
48 | return IRQ_HANDLED; | |
49 | } | |
50 | ||
51 | static irqreturn_t hi65xx_restart_toggle_isr(int irq, void *q) | |
52 | { | |
53 | struct input_dev *input = q; | |
54 | int value = test_bit(KEY_RESTART, input->key); | |
55 | ||
56 | pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); | |
57 | input_report_key(input, KEY_RESTART, !value); | |
58 | input_sync(input); | |
59 | ||
60 | return IRQ_HANDLED; | |
61 | } | |
62 | ||
63 | static const struct { | |
64 | const char *name; | |
65 | irqreturn_t (*handler)(int irq, void *q); | |
66 | } hi65xx_irq_info[] = { | |
67 | { "down", hi65xx_power_press_isr }, | |
68 | { "up", hi65xx_power_release_isr }, | |
69 | { "hold 4s", hi65xx_restart_toggle_isr }, | |
70 | }; | |
71 | ||
72 | static int hi65xx_powerkey_probe(struct platform_device *pdev) | |
73 | { | |
74 | struct device *dev = &pdev->dev; | |
75 | struct input_dev *input; | |
76 | int irq, i, error; | |
77 | ||
78 | input = devm_input_allocate_device(&pdev->dev); | |
79 | if (!input) { | |
80 | dev_err(&pdev->dev, "failed to allocate input device\n"); | |
81 | return -ENOMEM; | |
82 | } | |
83 | ||
84 | input->phys = "hisi_on/input0"; | |
85 | input->name = "HISI 65xx PowerOn Key"; | |
86 | ||
87 | input_set_capability(input, EV_KEY, KEY_POWER); | |
88 | input_set_capability(input, EV_KEY, KEY_RESTART); | |
89 | ||
90 | for (i = 0; i < ARRAY_SIZE(hi65xx_irq_info); i++) { | |
91 | ||
92 | irq = platform_get_irq_byname(pdev, hi65xx_irq_info[i].name); | |
93 | if (irq < 0) { | |
94 | error = irq; | |
95 | dev_err(dev, "couldn't get irq %s: %d\n", | |
96 | hi65xx_irq_info[i].name, error); | |
97 | return error; | |
98 | } | |
99 | ||
100 | error = devm_request_any_context_irq(dev, irq, | |
101 | hi65xx_irq_info[i].handler, | |
102 | IRQF_ONESHOT, | |
103 | hi65xx_irq_info[i].name, | |
104 | input); | |
105 | if (error < 0) { | |
106 | dev_err(dev, "couldn't request irq %s: %d\n", | |
107 | hi65xx_irq_info[i].name, error); | |
108 | return error; | |
109 | } | |
110 | } | |
111 | ||
112 | error = input_register_device(input); | |
113 | if (error) { | |
114 | dev_err(&pdev->dev, "failed to register input device: %d\n", | |
115 | error); | |
116 | return error; | |
117 | } | |
118 | ||
119 | device_init_wakeup(&pdev->dev, 1); | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static int hi65xx_powerkey_remove(struct platform_device *pdev) | |
125 | { | |
126 | device_init_wakeup(&pdev->dev, 0); | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | static struct platform_driver hi65xx_powerkey_driver = { | |
132 | .driver = { | |
133 | .name = "hi65xx-powerkey", | |
134 | }, | |
135 | .probe = hi65xx_powerkey_probe, | |
136 | .remove = hi65xx_powerkey_remove, | |
137 | }; | |
138 | module_platform_driver(hi65xx_powerkey_driver); | |
139 | ||
140 | MODULE_AUTHOR("Zhiliang Xue <xuezhiliang@huawei.com"); | |
141 | MODULE_DESCRIPTION("Hisi PMIC Power key driver"); | |
142 | MODULE_LICENSE("GPL v2"); |