1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * TI Touch Screen / ADC MFD driver |
4 | * |
5 | * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/err.h> |
11 | #include <linux/io.h> |
12 | #include <linux/clk.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/mfd/core.h> |
15 | #include <linux/pm_runtime.h> |
16 | #include <linux/of.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/sched.h> |
19 | |
20 | #include <linux/mfd/ti_am335x_tscadc.h> |
21 | |
22 | static const struct regmap_config tscadc_regmap_config = { |
23 | .name = "ti_tscadc" , |
24 | .reg_bits = 32, |
25 | .reg_stride = 4, |
26 | .val_bits = 32, |
27 | }; |
28 | |
29 | void am335x_tsc_se_set_cache(struct ti_tscadc_dev *tscadc, u32 val) |
30 | { |
31 | unsigned long flags; |
32 | |
33 | spin_lock_irqsave(&tscadc->reg_lock, flags); |
34 | tscadc->reg_se_cache |= val; |
35 | if (tscadc->adc_waiting) |
36 | wake_up(&tscadc->reg_se_wait); |
37 | else if (!tscadc->adc_in_use) |
38 | regmap_write(map: tscadc->regmap, REG_SE, val: tscadc->reg_se_cache); |
39 | |
40 | spin_unlock_irqrestore(lock: &tscadc->reg_lock, flags); |
41 | } |
42 | EXPORT_SYMBOL_GPL(am335x_tsc_se_set_cache); |
43 | |
44 | static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tscadc) |
45 | { |
46 | DEFINE_WAIT(wait); |
47 | u32 reg; |
48 | |
49 | regmap_read(map: tscadc->regmap, REG_ADCFSM, val: ®); |
50 | if (reg & SEQ_STATUS) { |
51 | tscadc->adc_waiting = true; |
52 | prepare_to_wait(wq_head: &tscadc->reg_se_wait, wq_entry: &wait, |
53 | TASK_UNINTERRUPTIBLE); |
54 | spin_unlock_irq(lock: &tscadc->reg_lock); |
55 | |
56 | schedule(); |
57 | |
58 | spin_lock_irq(lock: &tscadc->reg_lock); |
59 | finish_wait(wq_head: &tscadc->reg_se_wait, wq_entry: &wait); |
60 | |
61 | /* |
62 | * Sequencer should either be idle or |
63 | * busy applying the charge step. |
64 | */ |
65 | regmap_read(map: tscadc->regmap, REG_ADCFSM, val: ®); |
66 | WARN_ON((reg & SEQ_STATUS) && !(reg & CHARGE_STEP)); |
67 | tscadc->adc_waiting = false; |
68 | } |
69 | tscadc->adc_in_use = true; |
70 | } |
71 | |
72 | void am335x_tsc_se_set_once(struct ti_tscadc_dev *tscadc, u32 val) |
73 | { |
74 | spin_lock_irq(lock: &tscadc->reg_lock); |
75 | am335x_tscadc_need_adc(tscadc); |
76 | |
77 | regmap_write(map: tscadc->regmap, REG_SE, val); |
78 | spin_unlock_irq(lock: &tscadc->reg_lock); |
79 | } |
80 | EXPORT_SYMBOL_GPL(am335x_tsc_se_set_once); |
81 | |
82 | void am335x_tsc_se_adc_done(struct ti_tscadc_dev *tscadc) |
83 | { |
84 | unsigned long flags; |
85 | |
86 | spin_lock_irqsave(&tscadc->reg_lock, flags); |
87 | tscadc->adc_in_use = false; |
88 | regmap_write(map: tscadc->regmap, REG_SE, val: tscadc->reg_se_cache); |
89 | spin_unlock_irqrestore(lock: &tscadc->reg_lock, flags); |
90 | } |
91 | EXPORT_SYMBOL_GPL(am335x_tsc_se_adc_done); |
92 | |
93 | void am335x_tsc_se_clr(struct ti_tscadc_dev *tscadc, u32 val) |
94 | { |
95 | unsigned long flags; |
96 | |
97 | spin_lock_irqsave(&tscadc->reg_lock, flags); |
98 | tscadc->reg_se_cache &= ~val; |
99 | regmap_write(map: tscadc->regmap, REG_SE, val: tscadc->reg_se_cache); |
100 | spin_unlock_irqrestore(lock: &tscadc->reg_lock, flags); |
101 | } |
102 | EXPORT_SYMBOL_GPL(am335x_tsc_se_clr); |
103 | |
104 | static void tscadc_idle_config(struct ti_tscadc_dev *tscadc) |
105 | { |
106 | unsigned int idleconfig; |
107 | |
108 | idleconfig = STEPCONFIG_INM_ADCREFM | STEPCONFIG_INP_ADCREFM; |
109 | if (ti_adc_with_touchscreen(tscadc)) |
110 | idleconfig |= STEPCONFIG_YNN | STEPCONFIG_YPN; |
111 | |
112 | regmap_write(map: tscadc->regmap, REG_IDLECONFIG, val: idleconfig); |
113 | } |
114 | |
115 | static int ti_tscadc_probe(struct platform_device *pdev) |
116 | { |
117 | struct ti_tscadc_dev *tscadc; |
118 | struct resource *res; |
119 | struct clk *clk; |
120 | struct device_node *node; |
121 | struct mfd_cell *cell; |
122 | struct property *prop; |
123 | const __be32 *cur; |
124 | bool use_tsc = false, use_mag = false; |
125 | u32 val; |
126 | int err; |
127 | int tscmag_wires = 0, adc_channels = 0, cell_idx = 0, total_channels; |
128 | int readouts = 0, mag_tracks = 0; |
129 | |
130 | /* Allocate memory for device */ |
131 | tscadc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*tscadc), GFP_KERNEL); |
132 | if (!tscadc) |
133 | return -ENOMEM; |
134 | |
135 | tscadc->dev = &pdev->dev; |
136 | |
137 | if (!pdev->dev.of_node) { |
138 | dev_err(&pdev->dev, "Could not find valid DT data.\n" ); |
139 | return -EINVAL; |
140 | } |
141 | |
142 | tscadc->data = of_device_get_match_data(dev: &pdev->dev); |
143 | |
144 | if (ti_adc_with_touchscreen(tscadc)) { |
145 | node = of_get_child_by_name(node: pdev->dev.of_node, name: "tsc" ); |
146 | of_property_read_u32(np: node, propname: "ti,wires" , out_value: &tscmag_wires); |
147 | err = of_property_read_u32(np: node, propname: "ti,coordinate-readouts" , |
148 | out_value: &readouts); |
149 | if (err < 0) |
150 | of_property_read_u32(np: node, propname: "ti,coordiante-readouts" , |
151 | out_value: &readouts); |
152 | |
153 | of_node_put(node); |
154 | |
155 | if (tscmag_wires) |
156 | use_tsc = true; |
157 | } else { |
158 | /* |
159 | * When adding support for the magnetic stripe reader, here is |
160 | * the place to look for the number of tracks used from device |
161 | * tree. Let's default to 0 for now. |
162 | */ |
163 | mag_tracks = 0; |
164 | tscmag_wires = mag_tracks * 2; |
165 | if (tscmag_wires) |
166 | use_mag = true; |
167 | } |
168 | |
169 | node = of_get_child_by_name(node: pdev->dev.of_node, name: "adc" ); |
170 | of_property_for_each_u32(node, "ti,adc-channels" , prop, cur, val) { |
171 | adc_channels++; |
172 | if (val > 7) { |
173 | dev_err(&pdev->dev, " PIN numbers are 0..7 (not %d)\n" , |
174 | val); |
175 | of_node_put(node); |
176 | return -EINVAL; |
177 | } |
178 | } |
179 | |
180 | of_node_put(node); |
181 | |
182 | total_channels = tscmag_wires + adc_channels; |
183 | if (total_channels > 8) { |
184 | dev_err(&pdev->dev, "Number of i/p channels more than 8\n" ); |
185 | return -EINVAL; |
186 | } |
187 | |
188 | if (total_channels == 0) { |
189 | dev_err(&pdev->dev, "Need at least one channel.\n" ); |
190 | return -EINVAL; |
191 | } |
192 | |
193 | if (use_tsc && (readouts * 2 + 2 + adc_channels > 16)) { |
194 | dev_err(&pdev->dev, "Too many step configurations requested\n" ); |
195 | return -EINVAL; |
196 | } |
197 | |
198 | err = platform_get_irq(pdev, 0); |
199 | if (err < 0) |
200 | return err; |
201 | else |
202 | tscadc->irq = err; |
203 | |
204 | tscadc->tscadc_base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
205 | if (IS_ERR(ptr: tscadc->tscadc_base)) |
206 | return PTR_ERR(ptr: tscadc->tscadc_base); |
207 | |
208 | tscadc->tscadc_phys_base = res->start; |
209 | tscadc->regmap = devm_regmap_init_mmio(&pdev->dev, |
210 | tscadc->tscadc_base, |
211 | &tscadc_regmap_config); |
212 | if (IS_ERR(ptr: tscadc->regmap)) { |
213 | dev_err(&pdev->dev, "regmap init failed\n" ); |
214 | return PTR_ERR(ptr: tscadc->regmap); |
215 | } |
216 | |
217 | spin_lock_init(&tscadc->reg_lock); |
218 | init_waitqueue_head(&tscadc->reg_se_wait); |
219 | |
220 | pm_runtime_enable(dev: &pdev->dev); |
221 | pm_runtime_get_sync(dev: &pdev->dev); |
222 | |
223 | /* |
224 | * The TSC_ADC_Subsystem has 2 clock domains: OCP_CLK and ADC_CLK. |
225 | * ADCs produce a 12-bit sample every 15 ADC_CLK cycles. |
226 | * am33xx ADCs expect to capture 200ksps. |
227 | * am47xx ADCs expect to capture 867ksps. |
228 | * We need ADC clocks respectively running at 3MHz and 13MHz. |
229 | * These frequencies are valid since TSC_ADC_SS controller design |
230 | * assumes the OCP clock is at least 6x faster than the ADC clock. |
231 | */ |
232 | clk = devm_clk_get(dev: &pdev->dev, NULL); |
233 | if (IS_ERR(ptr: clk)) { |
234 | dev_err(&pdev->dev, "failed to get fck\n" ); |
235 | err = PTR_ERR(ptr: clk); |
236 | goto err_disable_clk; |
237 | } |
238 | |
239 | tscadc->clk_div = (clk_get_rate(clk) / tscadc->data->target_clk_rate) - 1; |
240 | regmap_write(map: tscadc->regmap, REG_CLKDIV, val: tscadc->clk_div); |
241 | |
242 | /* |
243 | * Set the control register bits. tscadc->ctrl stores the configuration |
244 | * of the CTRL register but not the subsystem enable bit which must be |
245 | * added manually when timely. |
246 | */ |
247 | tscadc->ctrl = CNTRLREG_STEPID; |
248 | if (ti_adc_with_touchscreen(tscadc)) { |
249 | tscadc->ctrl |= CNTRLREG_TSC_STEPCONFIGWRT; |
250 | if (use_tsc) { |
251 | tscadc->ctrl |= CNTRLREG_TSC_ENB; |
252 | if (tscmag_wires == 5) |
253 | tscadc->ctrl |= CNTRLREG_TSC_5WIRE; |
254 | else |
255 | tscadc->ctrl |= CNTRLREG_TSC_4WIRE; |
256 | } |
257 | } else { |
258 | tscadc->ctrl |= CNTRLREG_MAG_PREAMP_PWRDOWN | |
259 | CNTRLREG_MAG_PREAMP_BYPASS; |
260 | } |
261 | regmap_write(map: tscadc->regmap, REG_CTRL, val: tscadc->ctrl); |
262 | |
263 | tscadc_idle_config(tscadc); |
264 | |
265 | /* Enable the TSC module enable bit */ |
266 | regmap_write(map: tscadc->regmap, REG_CTRL, val: tscadc->ctrl | CNTRLREG_SSENB); |
267 | |
268 | /* TSC or MAG Cell */ |
269 | if (use_tsc || use_mag) { |
270 | cell = &tscadc->cells[cell_idx++]; |
271 | cell->name = tscadc->data->secondary_feature_name; |
272 | cell->of_compatible = tscadc->data->secondary_feature_compatible; |
273 | cell->platform_data = &tscadc; |
274 | cell->pdata_size = sizeof(tscadc); |
275 | } |
276 | |
277 | /* ADC Cell */ |
278 | if (adc_channels > 0) { |
279 | cell = &tscadc->cells[cell_idx++]; |
280 | cell->name = tscadc->data->adc_feature_name; |
281 | cell->of_compatible = tscadc->data->adc_feature_compatible; |
282 | cell->platform_data = &tscadc; |
283 | cell->pdata_size = sizeof(tscadc); |
284 | } |
285 | |
286 | err = mfd_add_devices(parent: &pdev->dev, PLATFORM_DEVID_AUTO, |
287 | cells: tscadc->cells, n_devs: cell_idx, NULL, irq_base: 0, NULL); |
288 | if (err < 0) |
289 | goto err_disable_clk; |
290 | |
291 | platform_set_drvdata(pdev, data: tscadc); |
292 | return 0; |
293 | |
294 | err_disable_clk: |
295 | pm_runtime_put_sync(dev: &pdev->dev); |
296 | pm_runtime_disable(dev: &pdev->dev); |
297 | |
298 | return err; |
299 | } |
300 | |
301 | static int ti_tscadc_remove(struct platform_device *pdev) |
302 | { |
303 | struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev); |
304 | |
305 | regmap_write(map: tscadc->regmap, REG_SE, val: 0x00); |
306 | |
307 | pm_runtime_put_sync(dev: &pdev->dev); |
308 | pm_runtime_disable(dev: &pdev->dev); |
309 | |
310 | mfd_remove_devices(parent: tscadc->dev); |
311 | |
312 | return 0; |
313 | } |
314 | |
315 | static int __maybe_unused ti_tscadc_can_wakeup(struct device *dev, void *data) |
316 | { |
317 | return device_may_wakeup(dev); |
318 | } |
319 | |
320 | static int __maybe_unused tscadc_suspend(struct device *dev) |
321 | { |
322 | struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev); |
323 | |
324 | regmap_write(map: tscadc->regmap, REG_SE, val: 0x00); |
325 | if (device_for_each_child(dev, NULL, fn: ti_tscadc_can_wakeup)) { |
326 | u32 ctrl; |
327 | |
328 | regmap_read(map: tscadc->regmap, REG_CTRL, val: &ctrl); |
329 | ctrl &= ~(CNTRLREG_POWERDOWN); |
330 | ctrl |= CNTRLREG_SSENB; |
331 | regmap_write(map: tscadc->regmap, REG_CTRL, val: ctrl); |
332 | } |
333 | pm_runtime_put_sync(dev); |
334 | |
335 | return 0; |
336 | } |
337 | |
338 | static int __maybe_unused tscadc_resume(struct device *dev) |
339 | { |
340 | struct ti_tscadc_dev *tscadc = dev_get_drvdata(dev); |
341 | |
342 | pm_runtime_get_sync(dev); |
343 | |
344 | regmap_write(map: tscadc->regmap, REG_CLKDIV, val: tscadc->clk_div); |
345 | regmap_write(map: tscadc->regmap, REG_CTRL, val: tscadc->ctrl); |
346 | tscadc_idle_config(tscadc); |
347 | regmap_write(map: tscadc->regmap, REG_CTRL, val: tscadc->ctrl | CNTRLREG_SSENB); |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | static SIMPLE_DEV_PM_OPS(tscadc_pm_ops, tscadc_suspend, tscadc_resume); |
353 | |
354 | static const struct ti_tscadc_data tscdata = { |
355 | .adc_feature_name = "TI-am335x-adc" , |
356 | .adc_feature_compatible = "ti,am3359-adc" , |
357 | .secondary_feature_name = "TI-am335x-tsc" , |
358 | .secondary_feature_compatible = "ti,am3359-tsc" , |
359 | .target_clk_rate = TSC_ADC_CLK, |
360 | }; |
361 | |
362 | static const struct ti_tscadc_data magdata = { |
363 | .adc_feature_name = "TI-am43xx-adc" , |
364 | .adc_feature_compatible = "ti,am4372-adc" , |
365 | .secondary_feature_name = "TI-am43xx-mag" , |
366 | .secondary_feature_compatible = "ti,am4372-mag" , |
367 | .target_clk_rate = MAG_ADC_CLK, |
368 | }; |
369 | |
370 | static const struct of_device_id ti_tscadc_dt_ids[] = { |
371 | { .compatible = "ti,am3359-tscadc" , .data = &tscdata }, |
372 | { .compatible = "ti,am4372-magadc" , .data = &magdata }, |
373 | { } |
374 | }; |
375 | MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids); |
376 | |
377 | static struct platform_driver ti_tscadc_driver = { |
378 | .driver = { |
379 | .name = "ti_am3359-tscadc" , |
380 | .pm = &tscadc_pm_ops, |
381 | .of_match_table = ti_tscadc_dt_ids, |
382 | }, |
383 | .probe = ti_tscadc_probe, |
384 | .remove = ti_tscadc_remove, |
385 | |
386 | }; |
387 | |
388 | module_platform_driver(ti_tscadc_driver); |
389 | |
390 | MODULE_DESCRIPTION("TI touchscreen/magnetic stripe reader/ADC MFD controller driver" ); |
391 | MODULE_AUTHOR("Rachna Patil <rachna@ti.com>" ); |
392 | MODULE_LICENSE("GPL" ); |
393 | |