1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Allwinner sun4i low res adc attached tablet keys driver |
4 | * |
5 | * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com> |
6 | */ |
7 | |
8 | /* |
9 | * Allwinnner sunxi SoCs have a lradc which is specifically designed to have |
10 | * various (tablet) keys (ie home, back, search, etc). attached to it using |
11 | * a resistor network. This driver is for the keys on such boards. |
12 | * |
13 | * There are 2 channels, currently this driver only supports channel 0 since |
14 | * there are no boards known to use channel 1. |
15 | */ |
16 | |
17 | #include <linux/clk.h> |
18 | #include <linux/err.h> |
19 | #include <linux/init.h> |
20 | #include <linux/input.h> |
21 | #include <linux/interrupt.h> |
22 | #include <linux/io.h> |
23 | #include <linux/module.h> |
24 | #include <linux/of.h> |
25 | #include <linux/platform_device.h> |
26 | #include <linux/pm_wakeirq.h> |
27 | #include <linux/pm_wakeup.h> |
28 | #include <linux/property.h> |
29 | #include <linux/regulator/consumer.h> |
30 | #include <linux/reset.h> |
31 | #include <linux/slab.h> |
32 | |
33 | #define LRADC_CTRL 0x00 |
34 | #define LRADC_INTC 0x04 |
35 | #define LRADC_INTS 0x08 |
36 | #define LRADC_DATA0 0x0c |
37 | #define LRADC_DATA1 0x10 |
38 | |
39 | /* LRADC_CTRL bits */ |
40 | #define FIRST_CONVERT_DLY(x) ((x) << 24) /* 8 bits */ |
41 | #define CHAN_SELECT(x) ((x) << 22) /* 2 bits */ |
42 | #define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits */ |
43 | #define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits */ |
44 | #define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits */ |
45 | #define HOLD_KEY_EN(x) ((x) << 7) |
46 | #define HOLD_EN(x) ((x) << 6) |
47 | #define LEVELB_VOL(x) ((x) << 4) /* 2 bits */ |
48 | #define SAMPLE_RATE(x) ((x) << 2) /* 2 bits */ |
49 | #define ENABLE(x) ((x) << 0) |
50 | |
51 | /* LRADC_INTC and LRADC_INTS bits */ |
52 | #define CHAN1_KEYUP_IRQ BIT(12) |
53 | #define CHAN1_ALRDY_HOLD_IRQ BIT(11) |
54 | #define CHAN1_HOLD_IRQ BIT(10) |
55 | #define CHAN1_KEYDOWN_IRQ BIT(9) |
56 | #define CHAN1_DATA_IRQ BIT(8) |
57 | #define CHAN0_KEYUP_IRQ BIT(4) |
58 | #define CHAN0_ALRDY_HOLD_IRQ BIT(3) |
59 | #define CHAN0_HOLD_IRQ BIT(2) |
60 | #define CHAN0_KEYDOWN_IRQ BIT(1) |
61 | #define CHAN0_DATA_IRQ BIT(0) |
62 | |
63 | /* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant |
64 | * @divisor_numerator: The numerator of lradc Vref internally divisor |
65 | * @divisor_denominator: The denominator of lradc Vref internally divisor |
66 | * @has_clock_reset: If the binding requires a clock and reset |
67 | */ |
68 | struct lradc_variant { |
69 | u8 divisor_numerator; |
70 | u8 divisor_denominator; |
71 | bool has_clock_reset; |
72 | }; |
73 | |
74 | static const struct lradc_variant lradc_variant_a10 = { |
75 | .divisor_numerator = 2, |
76 | .divisor_denominator = 3 |
77 | }; |
78 | |
79 | static const struct lradc_variant r_lradc_variant_a83t = { |
80 | .divisor_numerator = 3, |
81 | .divisor_denominator = 4 |
82 | }; |
83 | |
84 | static const struct lradc_variant lradc_variant_r329 = { |
85 | .divisor_numerator = 3, |
86 | .divisor_denominator = 4, |
87 | .has_clock_reset = true, |
88 | }; |
89 | |
90 | struct sun4i_lradc_keymap { |
91 | u32 voltage; |
92 | u32 keycode; |
93 | }; |
94 | |
95 | struct sun4i_lradc_data { |
96 | struct device *dev; |
97 | struct input_dev *input; |
98 | void __iomem *base; |
99 | struct clk *clk; |
100 | struct reset_control *reset; |
101 | struct regulator *vref_supply; |
102 | struct sun4i_lradc_keymap *chan0_map; |
103 | const struct lradc_variant *variant; |
104 | u32 chan0_map_count; |
105 | u32 chan0_keycode; |
106 | u32 vref; |
107 | }; |
108 | |
109 | static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id) |
110 | { |
111 | struct sun4i_lradc_data *lradc = dev_id; |
112 | u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff; |
113 | |
114 | ints = readl(addr: lradc->base + LRADC_INTS); |
115 | |
116 | /* |
117 | * lradc supports only one keypress at a time, release does not give |
118 | * any info as to which key was released, so we cache the keycode. |
119 | */ |
120 | |
121 | if (ints & CHAN0_KEYUP_IRQ) { |
122 | input_report_key(dev: lradc->input, code: lradc->chan0_keycode, value: 0); |
123 | lradc->chan0_keycode = 0; |
124 | } |
125 | |
126 | if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) { |
127 | val = readl(addr: lradc->base + LRADC_DATA0) & 0x3f; |
128 | voltage = val * lradc->vref / 63; |
129 | |
130 | for (i = 0; i < lradc->chan0_map_count; i++) { |
131 | diff = abs(lradc->chan0_map[i].voltage - voltage); |
132 | if (diff < closest) { |
133 | closest = diff; |
134 | keycode = lradc->chan0_map[i].keycode; |
135 | } |
136 | } |
137 | |
138 | lradc->chan0_keycode = keycode; |
139 | input_report_key(dev: lradc->input, code: lradc->chan0_keycode, value: 1); |
140 | } |
141 | |
142 | input_sync(dev: lradc->input); |
143 | |
144 | writel(val: ints, addr: lradc->base + LRADC_INTS); |
145 | |
146 | return IRQ_HANDLED; |
147 | } |
148 | |
149 | static int sun4i_lradc_open(struct input_dev *dev) |
150 | { |
151 | struct sun4i_lradc_data *lradc = input_get_drvdata(dev); |
152 | int error; |
153 | |
154 | error = regulator_enable(regulator: lradc->vref_supply); |
155 | if (error) |
156 | return error; |
157 | |
158 | error = reset_control_deassert(rstc: lradc->reset); |
159 | if (error) |
160 | goto err_disable_reg; |
161 | |
162 | error = clk_prepare_enable(clk: lradc->clk); |
163 | if (error) |
164 | goto err_assert_reset; |
165 | |
166 | lradc->vref = regulator_get_voltage(regulator: lradc->vref_supply) * |
167 | lradc->variant->divisor_numerator / |
168 | lradc->variant->divisor_denominator; |
169 | /* |
170 | * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to |
171 | * stabilize on press, wait (1 + 1) * 4 ms for key release |
172 | */ |
173 | writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | |
174 | SAMPLE_RATE(0) | ENABLE(1), addr: lradc->base + LRADC_CTRL); |
175 | |
176 | writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, addr: lradc->base + LRADC_INTC); |
177 | |
178 | return 0; |
179 | |
180 | err_assert_reset: |
181 | reset_control_assert(rstc: lradc->reset); |
182 | err_disable_reg: |
183 | regulator_disable(regulator: lradc->vref_supply); |
184 | |
185 | return error; |
186 | } |
187 | |
188 | static void sun4i_lradc_close(struct input_dev *dev) |
189 | { |
190 | struct sun4i_lradc_data *lradc = input_get_drvdata(dev); |
191 | |
192 | /* Disable lradc, leave other settings unchanged */ |
193 | writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | |
194 | SAMPLE_RATE(2), addr: lradc->base + LRADC_CTRL); |
195 | writel(val: 0, addr: lradc->base + LRADC_INTC); |
196 | |
197 | clk_disable_unprepare(clk: lradc->clk); |
198 | reset_control_assert(rstc: lradc->reset); |
199 | regulator_disable(regulator: lradc->vref_supply); |
200 | } |
201 | |
202 | static int sun4i_lradc_load_dt_keymap(struct device *dev, |
203 | struct sun4i_lradc_data *lradc) |
204 | { |
205 | struct device_node *np, *pp; |
206 | int i; |
207 | int error; |
208 | |
209 | np = dev->of_node; |
210 | if (!np) |
211 | return -EINVAL; |
212 | |
213 | lradc->chan0_map_count = of_get_child_count(np); |
214 | if (lradc->chan0_map_count == 0) { |
215 | dev_err(dev, "keymap is missing in device tree\n" ); |
216 | return -EINVAL; |
217 | } |
218 | |
219 | lradc->chan0_map = devm_kmalloc_array(dev, n: lradc->chan0_map_count, |
220 | size: sizeof(struct sun4i_lradc_keymap), |
221 | GFP_KERNEL); |
222 | if (!lradc->chan0_map) |
223 | return -ENOMEM; |
224 | |
225 | i = 0; |
226 | for_each_child_of_node(np, pp) { |
227 | struct sun4i_lradc_keymap *map = &lradc->chan0_map[i]; |
228 | u32 channel; |
229 | |
230 | error = of_property_read_u32(np: pp, propname: "channel" , out_value: &channel); |
231 | if (error || channel != 0) { |
232 | dev_err(dev, "%pOFn: Inval channel prop\n" , pp); |
233 | of_node_put(node: pp); |
234 | return -EINVAL; |
235 | } |
236 | |
237 | error = of_property_read_u32(np: pp, propname: "voltage" , out_value: &map->voltage); |
238 | if (error) { |
239 | dev_err(dev, "%pOFn: Inval voltage prop\n" , pp); |
240 | of_node_put(node: pp); |
241 | return -EINVAL; |
242 | } |
243 | |
244 | error = of_property_read_u32(np: pp, propname: "linux,code" , out_value: &map->keycode); |
245 | if (error) { |
246 | dev_err(dev, "%pOFn: Inval linux,code prop\n" , pp); |
247 | of_node_put(node: pp); |
248 | return -EINVAL; |
249 | } |
250 | |
251 | i++; |
252 | } |
253 | |
254 | return 0; |
255 | } |
256 | |
257 | static int sun4i_lradc_probe(struct platform_device *pdev) |
258 | { |
259 | struct sun4i_lradc_data *lradc; |
260 | struct device *dev = &pdev->dev; |
261 | int error, i, irq; |
262 | |
263 | lradc = devm_kzalloc(dev, size: sizeof(struct sun4i_lradc_data), GFP_KERNEL); |
264 | if (!lradc) |
265 | return -ENOMEM; |
266 | |
267 | error = sun4i_lradc_load_dt_keymap(dev, lradc); |
268 | if (error) |
269 | return error; |
270 | |
271 | lradc->variant = of_device_get_match_data(dev: &pdev->dev); |
272 | if (!lradc->variant) { |
273 | dev_err(&pdev->dev, "Missing sun4i-a10-lradc-keys variant\n" ); |
274 | return -EINVAL; |
275 | } |
276 | |
277 | if (lradc->variant->has_clock_reset) { |
278 | lradc->clk = devm_clk_get(dev, NULL); |
279 | if (IS_ERR(ptr: lradc->clk)) |
280 | return PTR_ERR(ptr: lradc->clk); |
281 | |
282 | lradc->reset = devm_reset_control_get_exclusive(dev, NULL); |
283 | if (IS_ERR(ptr: lradc->reset)) |
284 | return PTR_ERR(ptr: lradc->reset); |
285 | } |
286 | |
287 | lradc->vref_supply = devm_regulator_get(dev, id: "vref" ); |
288 | if (IS_ERR(ptr: lradc->vref_supply)) |
289 | return PTR_ERR(ptr: lradc->vref_supply); |
290 | |
291 | lradc->dev = dev; |
292 | lradc->input = devm_input_allocate_device(dev); |
293 | if (!lradc->input) |
294 | return -ENOMEM; |
295 | |
296 | lradc->input->name = pdev->name; |
297 | lradc->input->phys = "sun4i_lradc/input0" ; |
298 | lradc->input->open = sun4i_lradc_open; |
299 | lradc->input->close = sun4i_lradc_close; |
300 | lradc->input->id.bustype = BUS_HOST; |
301 | lradc->input->id.vendor = 0x0001; |
302 | lradc->input->id.product = 0x0001; |
303 | lradc->input->id.version = 0x0100; |
304 | |
305 | __set_bit(EV_KEY, lradc->input->evbit); |
306 | for (i = 0; i < lradc->chan0_map_count; i++) |
307 | __set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit); |
308 | |
309 | input_set_drvdata(dev: lradc->input, data: lradc); |
310 | |
311 | lradc->base = devm_platform_ioremap_resource(pdev, index: 0); |
312 | if (IS_ERR(ptr: lradc->base)) |
313 | return PTR_ERR(ptr: lradc->base); |
314 | |
315 | irq = platform_get_irq(pdev, 0); |
316 | if (irq < 0) |
317 | return irq; |
318 | |
319 | error = devm_request_irq(dev, irq, handler: sun4i_lradc_irq, irqflags: 0, |
320 | devname: "sun4i-a10-lradc-keys" , dev_id: lradc); |
321 | if (error) |
322 | return error; |
323 | |
324 | error = input_register_device(lradc->input); |
325 | if (error) |
326 | return error; |
327 | |
328 | if (device_property_read_bool(dev, propname: "wakeup-source" )) { |
329 | error = dev_pm_set_wake_irq(dev, irq); |
330 | if (error) |
331 | dev_warn(dev, |
332 | "Failed to set IRQ %d as a wake IRQ: %d\n" , |
333 | irq, error); |
334 | else |
335 | device_set_wakeup_capable(dev, capable: true); |
336 | } |
337 | |
338 | return 0; |
339 | } |
340 | |
341 | static const struct of_device_id sun4i_lradc_of_match[] = { |
342 | { .compatible = "allwinner,sun4i-a10-lradc-keys" , |
343 | .data = &lradc_variant_a10 }, |
344 | { .compatible = "allwinner,sun8i-a83t-r-lradc" , |
345 | .data = &r_lradc_variant_a83t }, |
346 | { .compatible = "allwinner,sun50i-r329-lradc" , |
347 | .data = &lradc_variant_r329 }, |
348 | { /* sentinel */ } |
349 | }; |
350 | MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match); |
351 | |
352 | static struct platform_driver sun4i_lradc_driver = { |
353 | .driver = { |
354 | .name = "sun4i-a10-lradc-keys" , |
355 | .of_match_table = of_match_ptr(sun4i_lradc_of_match), |
356 | }, |
357 | .probe = sun4i_lradc_probe, |
358 | }; |
359 | |
360 | module_platform_driver(sun4i_lradc_driver); |
361 | |
362 | MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver" ); |
363 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
364 | MODULE_LICENSE("GPL" ); |
365 | |