diff options
author | Steve Twiss <stwiss.opensource@diasemi.com> | 2015-06-08 19:26:20 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-06-09 14:23:24 -0400 |
commit | 5179f0ce2f96f155e3bda93b3b82f912dbaddad2 (patch) | |
tree | 4edcad7286a5bdd329605646eaeae1c550628ab1 | |
parent | 7b9f1830745cb3ad9e0f3774e83900610cedd39c (diff) |
Input: add OnKey driver for DA9063 MFD part
This adds OnKey driver support for DA9063.
Signed-off-by: Steve Twiss <stwiss.opensource@diasemi.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r-- | drivers/input/misc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/da9063_onkey.c | 226 | ||||
-rw-r--r-- | include/linux/mfd/da9063/pdata.h | 1 |
4 files changed, 238 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index e5c4de279001..d4f0a817e858 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig | |||
@@ -610,6 +610,16 @@ config INPUT_DA9055_ONKEY | |||
610 | To compile this driver as a module, choose M here: the module | 610 | To compile this driver as a module, choose M here: the module |
611 | will be called da9055_onkey. | 611 | will be called da9055_onkey. |
612 | 612 | ||
613 | config INPUT_DA9063_ONKEY | ||
614 | tristate "Dialog DA9063 OnKey" | ||
615 | depends on MFD_DA9063 | ||
616 | help | ||
617 | Support the ONKEY of Dialog DA9063 Power Management IC as an | ||
618 | input device reporting power button statue. | ||
619 | |||
620 | To compile this driver as a module, choose M here: the module | ||
621 | will be called da9063_onkey. | ||
622 | |||
613 | config INPUT_DM355EVM | 623 | config INPUT_DM355EVM |
614 | tristate "TI DaVinci DM355 EVM Keypad and IR Remote" | 624 | tristate "TI DaVinci DM355 EVM Keypad and IR Remote" |
615 | depends on MFD_DM355EVM_MSP | 625 | depends on MFD_DM355EVM_MSP |
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index d3179472474d..53df07dcc23c 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile | |||
@@ -25,6 +25,7 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o | |||
25 | obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o | 25 | obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o |
26 | obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o | 26 | obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o |
27 | obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o | 27 | obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o |
28 | obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o | ||
28 | obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o | 29 | obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o |
29 | obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o | 30 | obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o |
30 | obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o | 31 | obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o |
diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c new file mode 100644 index 000000000000..f577585ef999 --- /dev/null +++ b/drivers/input/misc/da9063_onkey.c | |||
@@ -0,0 +1,226 @@ | |||
1 | /* | ||
2 | * OnKey device driver for DA9063 | ||
3 | * Copyright (C) 2015 Dialog Semiconductor Ltd. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version 2 | ||
8 | * of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/input.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | #include <linux/regmap.h> | ||
23 | #include <linux/of.h> | ||
24 | #include <linux/mfd/da9063/core.h> | ||
25 | #include <linux/mfd/da9063/pdata.h> | ||
26 | #include <linux/mfd/da9063/registers.h> | ||
27 | |||
28 | struct da9063_onkey { | ||
29 | struct da9063 *hw; | ||
30 | struct delayed_work work; | ||
31 | struct input_dev *input; | ||
32 | struct device *dev; | ||
33 | bool key_power; | ||
34 | }; | ||
35 | |||
36 | static void da9063_poll_on(struct work_struct *work) | ||
37 | { | ||
38 | struct da9063_onkey *onkey = container_of(work, struct da9063_onkey, | ||
39 | work.work); | ||
40 | unsigned int val; | ||
41 | int fault_log = 0; | ||
42 | bool poll = true; | ||
43 | int error; | ||
44 | |||
45 | /* Poll to see when the pin is released */ | ||
46 | error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); | ||
47 | if (error) { | ||
48 | dev_err(onkey->dev, | ||
49 | "Failed to read ON status: %d\n", error); | ||
50 | goto err_poll; | ||
51 | } | ||
52 | |||
53 | if (!(val & DA9063_NONKEY)) { | ||
54 | error = regmap_update_bits(onkey->hw->regmap, | ||
55 | DA9063_REG_CONTROL_B, | ||
56 | DA9063_NONKEY_LOCK, 0); | ||
57 | if (error) { | ||
58 | dev_err(onkey->dev, | ||
59 | "Failed to reset the Key Delay %d\n", error); | ||
60 | goto err_poll; | ||
61 | } | ||
62 | |||
63 | input_report_key(onkey->input, KEY_POWER, 0); | ||
64 | input_sync(onkey->input); | ||
65 | |||
66 | poll = false; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * If the fault log KEY_RESET is detected, then clear it | ||
71 | * and shut down the system. | ||
72 | */ | ||
73 | error = regmap_read(onkey->hw->regmap, | ||
74 | DA9063_REG_FAULT_LOG, &fault_log); | ||
75 | if (error) { | ||
76 | dev_warn(&onkey->input->dev, | ||
77 | "Cannot read FAULT_LOG: %d\n", error); | ||
78 | } else if (fault_log & DA9063_KEY_RESET) { | ||
79 | error = regmap_write(onkey->hw->regmap, | ||
80 | DA9063_REG_FAULT_LOG, | ||
81 | DA9063_KEY_RESET); | ||
82 | if (error) { | ||
83 | dev_warn(&onkey->input->dev, | ||
84 | "Cannot reset KEY_RESET fault log: %d\n", | ||
85 | error); | ||
86 | } else { | ||
87 | /* at this point we do any S/W housekeeping | ||
88 | * and then send shutdown command | ||
89 | */ | ||
90 | dev_dbg(&onkey->input->dev, | ||
91 | "Sending SHUTDOWN to DA9063 ...\n"); | ||
92 | error = regmap_write(onkey->hw->regmap, | ||
93 | DA9063_REG_CONTROL_F, | ||
94 | DA9063_SHUTDOWN); | ||
95 | if (error) | ||
96 | dev_err(&onkey->input->dev, | ||
97 | "Cannot SHUTDOWN DA9063: %d\n", | ||
98 | error); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | err_poll: | ||
103 | if (poll) | ||
104 | schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); | ||
105 | } | ||
106 | |||
107 | static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) | ||
108 | { | ||
109 | struct da9063_onkey *onkey = data; | ||
110 | unsigned int val; | ||
111 | int error; | ||
112 | |||
113 | error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); | ||
114 | if (onkey->key_power && !error && (val & DA9063_NONKEY)) { | ||
115 | input_report_key(onkey->input, KEY_POWER, 1); | ||
116 | input_sync(onkey->input); | ||
117 | schedule_delayed_work(&onkey->work, 0); | ||
118 | dev_dbg(onkey->dev, "KEY_POWER pressed.\n"); | ||
119 | } else { | ||
120 | input_report_key(onkey->input, KEY_SLEEP, 1); | ||
121 | input_sync(onkey->input); | ||
122 | input_report_key(onkey->input, KEY_SLEEP, 0); | ||
123 | input_sync(onkey->input); | ||
124 | dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n"); | ||
125 | } | ||
126 | |||
127 | return IRQ_HANDLED; | ||
128 | } | ||
129 | |||
130 | static void da9063_cancel_poll(void *data) | ||
131 | { | ||
132 | struct da9063_onkey *onkey = data; | ||
133 | |||
134 | cancel_delayed_work_sync(&onkey->work); | ||
135 | } | ||
136 | |||
137 | static int da9063_onkey_probe(struct platform_device *pdev) | ||
138 | { | ||
139 | struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); | ||
140 | struct da9063_pdata *pdata = dev_get_platdata(da9063->dev); | ||
141 | struct da9063_onkey *onkey; | ||
142 | int irq; | ||
143 | int error; | ||
144 | |||
145 | onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey), | ||
146 | GFP_KERNEL); | ||
147 | if (!onkey) { | ||
148 | dev_err(&pdev->dev, "Failed to allocate memory.\n"); | ||
149 | return -ENOMEM; | ||
150 | } | ||
151 | |||
152 | onkey->dev = &pdev->dev; | ||
153 | onkey->hw = da9063; | ||
154 | |||
155 | if (pdata) | ||
156 | onkey->key_power = pdata->key_power; | ||
157 | else | ||
158 | onkey->key_power = | ||
159 | !of_property_read_bool(pdev->dev.of_node, | ||
160 | "dlg,disable-key-power"); | ||
161 | |||
162 | onkey->input = devm_input_allocate_device(&pdev->dev); | ||
163 | if (!onkey->input) { | ||
164 | dev_err(&pdev->dev, "Failed to allocated input device.\n"); | ||
165 | return -ENOMEM; | ||
166 | } | ||
167 | |||
168 | onkey->input->name = DA9063_DRVNAME_ONKEY; | ||
169 | onkey->input->phys = DA9063_DRVNAME_ONKEY "/input0"; | ||
170 | onkey->input->dev.parent = &pdev->dev; | ||
171 | |||
172 | if (onkey->key_power) | ||
173 | input_set_capability(onkey->input, EV_KEY, KEY_POWER); | ||
174 | |||
175 | input_set_capability(onkey->input, EV_KEY, KEY_SLEEP); | ||
176 | |||
177 | INIT_DELAYED_WORK(&onkey->work, da9063_poll_on); | ||
178 | |||
179 | error = devm_add_action(&pdev->dev, da9063_cancel_poll, onkey); | ||
180 | if (error) { | ||
181 | dev_err(&pdev->dev, | ||
182 | "Failed to add cancel poll action: %d\n", | ||
183 | error); | ||
184 | return error; | ||
185 | } | ||
186 | |||
187 | irq = platform_get_irq_byname(pdev, "ONKEY"); | ||
188 | if (irq < 0) { | ||
189 | error = irq; | ||
190 | dev_err(&pdev->dev, "Failed to get platform IRQ: %d\n", error); | ||
191 | return error; | ||
192 | } | ||
193 | |||
194 | error = devm_request_threaded_irq(&pdev->dev, irq, | ||
195 | NULL, da9063_onkey_irq_handler, | ||
196 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | ||
197 | "ONKEY", onkey); | ||
198 | if (error) { | ||
199 | dev_err(&pdev->dev, | ||
200 | "Failed to request IRQ %d: %d\n", irq, error); | ||
201 | return error; | ||
202 | } | ||
203 | |||
204 | error = input_register_device(onkey->input); | ||
205 | if (error) { | ||
206 | dev_err(&pdev->dev, | ||
207 | "Failed to register input device: %d\n", error); | ||
208 | return error; | ||
209 | } | ||
210 | |||
211 | platform_set_drvdata(pdev, onkey); | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static struct platform_driver da9063_onkey_driver = { | ||
216 | .probe = da9063_onkey_probe, | ||
217 | .driver = { | ||
218 | .name = DA9063_DRVNAME_ONKEY, | ||
219 | }, | ||
220 | }; | ||
221 | module_platform_driver(da9063_onkey_driver); | ||
222 | |||
223 | MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); | ||
224 | MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063"); | ||
225 | MODULE_LICENSE("GPL"); | ||
226 | MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY); | ||
diff --git a/include/linux/mfd/da9063/pdata.h b/include/linux/mfd/da9063/pdata.h index 95c8742215a7..612383bd80ae 100644 --- a/include/linux/mfd/da9063/pdata.h +++ b/include/linux/mfd/da9063/pdata.h | |||
@@ -103,6 +103,7 @@ struct da9063; | |||
103 | struct da9063_pdata { | 103 | struct da9063_pdata { |
104 | int (*init)(struct da9063 *da9063); | 104 | int (*init)(struct da9063 *da9063); |
105 | int irq_base; | 105 | int irq_base; |
106 | bool key_power; | ||
106 | unsigned flags; | 107 | unsigned flags; |
107 | struct da9063_regulators_pdata *regulators_pdata; | 108 | struct da9063_regulators_pdata *regulators_pdata; |
108 | struct led_platform_data *leds_pdata; | 109 | struct led_platform_data *leds_pdata; |