]>
Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
af6a5af8 HG |
2 | /* |
3 | * Allwinner sun4i low res adc attached tablet keys driver | |
4 | * | |
5 | * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com> | |
af6a5af8 HG |
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/err.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/input.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/io.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/of_platform.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/regulator/consumer.h> | |
26 | #include <linux/slab.h> | |
27 | ||
28 | #define LRADC_CTRL 0x00 | |
29 | #define LRADC_INTC 0x04 | |
30 | #define LRADC_INTS 0x08 | |
31 | #define LRADC_DATA0 0x0c | |
32 | #define LRADC_DATA1 0x10 | |
33 | ||
34 | /* LRADC_CTRL bits */ | |
35 | #define FIRST_CONVERT_DLY(x) ((x) << 24) /* 8 bits */ | |
36 | #define CHAN_SELECT(x) ((x) << 22) /* 2 bits */ | |
37 | #define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits */ | |
38 | #define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits */ | |
39 | #define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits */ | |
4c459117 | 40 | #define HOLD_KEY_EN(x) ((x) << 7) |
af6a5af8 HG |
41 | #define HOLD_EN(x) ((x) << 6) |
42 | #define LEVELB_VOL(x) ((x) << 4) /* 2 bits */ | |
43 | #define SAMPLE_RATE(x) ((x) << 2) /* 2 bits */ | |
44 | #define ENABLE(x) ((x) << 0) | |
45 | ||
46 | /* LRADC_INTC and LRADC_INTS bits */ | |
47 | #define CHAN1_KEYUP_IRQ BIT(12) | |
48 | #define CHAN1_ALRDY_HOLD_IRQ BIT(11) | |
49 | #define CHAN1_HOLD_IRQ BIT(10) | |
50 | #define CHAN1_KEYDOWN_IRQ BIT(9) | |
51 | #define CHAN1_DATA_IRQ BIT(8) | |
52 | #define CHAN0_KEYUP_IRQ BIT(4) | |
53 | #define CHAN0_ALRDY_HOLD_IRQ BIT(3) | |
54 | #define CHAN0_HOLD_IRQ BIT(2) | |
55 | #define CHAN0_KEYDOWN_IRQ BIT(1) | |
56 | #define CHAN0_DATA_IRQ BIT(0) | |
57 | ||
4c459117 ZC |
58 | /* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant |
59 | * @divisor_numerator: The numerator of lradc Vref internally divisor | |
60 | * @divisor_denominator: The denominator of lradc Vref internally divisor | |
61 | */ | |
62 | struct lradc_variant { | |
63 | u8 divisor_numerator; | |
64 | u8 divisor_denominator; | |
65 | }; | |
66 | ||
67 | static const struct lradc_variant lradc_variant_a10 = { | |
68 | .divisor_numerator = 2, | |
69 | .divisor_denominator = 3 | |
70 | }; | |
71 | ||
72 | static const struct lradc_variant r_lradc_variant_a83t = { | |
73 | .divisor_numerator = 3, | |
74 | .divisor_denominator = 4 | |
75 | }; | |
76 | ||
af6a5af8 HG |
77 | struct sun4i_lradc_keymap { |
78 | u32 voltage; | |
79 | u32 keycode; | |
80 | }; | |
81 | ||
82 | struct sun4i_lradc_data { | |
83 | struct device *dev; | |
84 | struct input_dev *input; | |
85 | void __iomem *base; | |
86 | struct regulator *vref_supply; | |
87 | struct sun4i_lradc_keymap *chan0_map; | |
4c459117 | 88 | const struct lradc_variant *variant; |
af6a5af8 HG |
89 | u32 chan0_map_count; |
90 | u32 chan0_keycode; | |
91 | u32 vref; | |
92 | }; | |
93 | ||
94 | static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id) | |
95 | { | |
96 | struct sun4i_lradc_data *lradc = dev_id; | |
97 | u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff; | |
98 | ||
99 | ints = readl(lradc->base + LRADC_INTS); | |
100 | ||
101 | /* | |
102 | * lradc supports only one keypress at a time, release does not give | |
103 | * any info as to which key was released, so we cache the keycode. | |
104 | */ | |
105 | ||
106 | if (ints & CHAN0_KEYUP_IRQ) { | |
107 | input_report_key(lradc->input, lradc->chan0_keycode, 0); | |
108 | lradc->chan0_keycode = 0; | |
109 | } | |
110 | ||
111 | if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) { | |
112 | val = readl(lradc->base + LRADC_DATA0) & 0x3f; | |
113 | voltage = val * lradc->vref / 63; | |
114 | ||
115 | for (i = 0; i < lradc->chan0_map_count; i++) { | |
116 | diff = abs(lradc->chan0_map[i].voltage - voltage); | |
117 | if (diff < closest) { | |
118 | closest = diff; | |
119 | keycode = lradc->chan0_map[i].keycode; | |
120 | } | |
121 | } | |
122 | ||
123 | lradc->chan0_keycode = keycode; | |
124 | input_report_key(lradc->input, lradc->chan0_keycode, 1); | |
125 | } | |
126 | ||
127 | input_sync(lradc->input); | |
128 | ||
129 | writel(ints, lradc->base + LRADC_INTS); | |
130 | ||
131 | return IRQ_HANDLED; | |
132 | } | |
133 | ||
134 | static int sun4i_lradc_open(struct input_dev *dev) | |
135 | { | |
136 | struct sun4i_lradc_data *lradc = input_get_drvdata(dev); | |
137 | int error; | |
138 | ||
139 | error = regulator_enable(lradc->vref_supply); | |
140 | if (error) | |
141 | return error; | |
142 | ||
4c459117 ZC |
143 | lradc->vref = regulator_get_voltage(lradc->vref_supply) * |
144 | lradc->variant->divisor_numerator / | |
145 | lradc->variant->divisor_denominator; | |
af6a5af8 HG |
146 | /* |
147 | * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to | |
148 | * stabilize on press, wait (1 + 1) * 4 ms for key release | |
149 | */ | |
150 | writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | | |
151 | SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL); | |
152 | ||
153 | writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC); | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | static void sun4i_lradc_close(struct input_dev *dev) | |
159 | { | |
160 | struct sun4i_lradc_data *lradc = input_get_drvdata(dev); | |
161 | ||
162 | /* Disable lradc, leave other settings unchanged */ | |
163 | writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | | |
164 | SAMPLE_RATE(2), lradc->base + LRADC_CTRL); | |
165 | writel(0, lradc->base + LRADC_INTC); | |
166 | ||
167 | regulator_disable(lradc->vref_supply); | |
168 | } | |
169 | ||
170 | static int sun4i_lradc_load_dt_keymap(struct device *dev, | |
171 | struct sun4i_lradc_data *lradc) | |
172 | { | |
173 | struct device_node *np, *pp; | |
174 | int i; | |
175 | int error; | |
176 | ||
177 | np = dev->of_node; | |
178 | if (!np) | |
179 | return -EINVAL; | |
180 | ||
181 | lradc->chan0_map_count = of_get_child_count(np); | |
182 | if (lradc->chan0_map_count == 0) { | |
183 | dev_err(dev, "keymap is missing in device tree\n"); | |
184 | return -EINVAL; | |
185 | } | |
186 | ||
187 | lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count, | |
188 | sizeof(struct sun4i_lradc_keymap), | |
189 | GFP_KERNEL); | |
190 | if (!lradc->chan0_map) | |
191 | return -ENOMEM; | |
192 | ||
193 | i = 0; | |
194 | for_each_child_of_node(np, pp) { | |
195 | struct sun4i_lradc_keymap *map = &lradc->chan0_map[i]; | |
196 | u32 channel; | |
197 | ||
198 | error = of_property_read_u32(pp, "channel", &channel); | |
199 | if (error || channel != 0) { | |
b297b728 | 200 | dev_err(dev, "%pOFn: Inval channel prop\n", pp); |
af6a5af8 HG |
201 | return -EINVAL; |
202 | } | |
203 | ||
204 | error = of_property_read_u32(pp, "voltage", &map->voltage); | |
205 | if (error) { | |
b297b728 | 206 | dev_err(dev, "%pOFn: Inval voltage prop\n", pp); |
af6a5af8 HG |
207 | return -EINVAL; |
208 | } | |
209 | ||
210 | error = of_property_read_u32(pp, "linux,code", &map->keycode); | |
211 | if (error) { | |
b297b728 | 212 | dev_err(dev, "%pOFn: Inval linux,code prop\n", pp); |
af6a5af8 HG |
213 | return -EINVAL; |
214 | } | |
215 | ||
216 | i++; | |
217 | } | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
222 | static int sun4i_lradc_probe(struct platform_device *pdev) | |
223 | { | |
224 | struct sun4i_lradc_data *lradc; | |
225 | struct device *dev = &pdev->dev; | |
226 | int i; | |
227 | int error; | |
228 | ||
229 | lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL); | |
230 | if (!lradc) | |
231 | return -ENOMEM; | |
232 | ||
233 | error = sun4i_lradc_load_dt_keymap(dev, lradc); | |
234 | if (error) | |
235 | return error; | |
236 | ||
4c459117 ZC |
237 | lradc->variant = of_device_get_match_data(&pdev->dev); |
238 | if (!lradc->variant) { | |
239 | dev_err(&pdev->dev, "Missing sun4i-a10-lradc-keys variant\n"); | |
240 | return -EINVAL; | |
241 | } | |
242 | ||
af6a5af8 HG |
243 | lradc->vref_supply = devm_regulator_get(dev, "vref"); |
244 | if (IS_ERR(lradc->vref_supply)) | |
245 | return PTR_ERR(lradc->vref_supply); | |
246 | ||
247 | lradc->dev = dev; | |
248 | lradc->input = devm_input_allocate_device(dev); | |
249 | if (!lradc->input) | |
250 | return -ENOMEM; | |
251 | ||
252 | lradc->input->name = pdev->name; | |
253 | lradc->input->phys = "sun4i_lradc/input0"; | |
254 | lradc->input->open = sun4i_lradc_open; | |
255 | lradc->input->close = sun4i_lradc_close; | |
256 | lradc->input->id.bustype = BUS_HOST; | |
257 | lradc->input->id.vendor = 0x0001; | |
258 | lradc->input->id.product = 0x0001; | |
259 | lradc->input->id.version = 0x0100; | |
260 | ||
261 | __set_bit(EV_KEY, lradc->input->evbit); | |
262 | for (i = 0; i < lradc->chan0_map_count; i++) | |
263 | __set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit); | |
264 | ||
265 | input_set_drvdata(lradc->input, lradc); | |
266 | ||
267 | lradc->base = devm_ioremap_resource(dev, | |
268 | platform_get_resource(pdev, IORESOURCE_MEM, 0)); | |
269 | if (IS_ERR(lradc->base)) | |
270 | return PTR_ERR(lradc->base); | |
271 | ||
272 | error = devm_request_irq(dev, platform_get_irq(pdev, 0), | |
273 | sun4i_lradc_irq, 0, | |
274 | "sun4i-a10-lradc-keys", lradc); | |
275 | if (error) | |
276 | return error; | |
277 | ||
278 | error = input_register_device(lradc->input); | |
279 | if (error) | |
280 | return error; | |
281 | ||
af6a5af8 HG |
282 | return 0; |
283 | } | |
284 | ||
285 | static const struct of_device_id sun4i_lradc_of_match[] = { | |
4c459117 ZC |
286 | { .compatible = "allwinner,sun4i-a10-lradc-keys", |
287 | .data = &lradc_variant_a10 }, | |
288 | { .compatible = "allwinner,sun8i-a83t-r-lradc", | |
289 | .data = &r_lradc_variant_a83t }, | |
af6a5af8 HG |
290 | { /* sentinel */ } |
291 | }; | |
292 | MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match); | |
293 | ||
294 | static struct platform_driver sun4i_lradc_driver = { | |
295 | .driver = { | |
296 | .name = "sun4i-a10-lradc-keys", | |
297 | .of_match_table = of_match_ptr(sun4i_lradc_of_match), | |
298 | }, | |
299 | .probe = sun4i_lradc_probe, | |
300 | }; | |
301 | ||
302 | module_platform_driver(sun4i_lradc_driver); | |
303 | ||
304 | MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver"); | |
305 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | |
306 | MODULE_LICENSE("GPL"); |