1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de> |
4 | * |
5 | * This is the driver for the imx25 GCQ (Generic Conversion Queue) |
6 | * connected to the imx25 ADC. |
7 | */ |
8 | |
9 | #include <dt-bindings/iio/adc/fsl-imx25-gcq.h> |
10 | #include <linux/clk.h> |
11 | #include <linux/iio/iio.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/mfd/imx25-tsadc.h> |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/regulator/consumer.h> |
19 | |
20 | #define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000)) |
21 | |
22 | static const char * const driver_name = "mx25-gcq" ; |
23 | |
24 | enum mx25_gcq_cfgs { |
25 | MX25_CFG_XP = 0, |
26 | MX25_CFG_YP, |
27 | MX25_CFG_XN, |
28 | MX25_CFG_YN, |
29 | MX25_CFG_WIPER, |
30 | MX25_CFG_INAUX0, |
31 | MX25_CFG_INAUX1, |
32 | MX25_CFG_INAUX2, |
33 | MX25_NUM_CFGS, |
34 | }; |
35 | |
36 | struct mx25_gcq_priv { |
37 | struct regmap *regs; |
38 | struct completion completed; |
39 | struct clk *clk; |
40 | int irq; |
41 | struct regulator *vref[4]; |
42 | u32 channel_vref_mv[MX25_NUM_CFGS]; |
43 | /* |
44 | * Lock to protect the device state during a potential concurrent |
45 | * read access from userspace. Reading a raw value requires a sequence |
46 | * of register writes, then a wait for a completion callback, |
47 | * and finally a register read, during which userspace could issue |
48 | * another read request. This lock protects a read access from |
49 | * ocurring before another one has finished. |
50 | */ |
51 | struct mutex lock; |
52 | }; |
53 | |
54 | #define MX25_CQG_CHAN(chan, id) {\ |
55 | .type = IIO_VOLTAGE,\ |
56 | .indexed = 1,\ |
57 | .channel = chan,\ |
58 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
59 | BIT(IIO_CHAN_INFO_SCALE),\ |
60 | .datasheet_name = id,\ |
61 | } |
62 | |
63 | static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = { |
64 | MX25_CQG_CHAN(MX25_CFG_XP, "xp" ), |
65 | MX25_CQG_CHAN(MX25_CFG_YP, "yp" ), |
66 | MX25_CQG_CHAN(MX25_CFG_XN, "xn" ), |
67 | MX25_CQG_CHAN(MX25_CFG_YN, "yn" ), |
68 | MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper" ), |
69 | MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0" ), |
70 | MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1" ), |
71 | MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2" ), |
72 | }; |
73 | |
74 | static const char * const mx25_gcq_refp_names[] = { |
75 | [MX25_ADC_REFP_YP] = "yp" , |
76 | [MX25_ADC_REFP_XP] = "xp" , |
77 | [MX25_ADC_REFP_INT] = "int" , |
78 | [MX25_ADC_REFP_EXT] = "ext" , |
79 | }; |
80 | |
81 | static irqreturn_t mx25_gcq_irq(int irq, void *data) |
82 | { |
83 | struct mx25_gcq_priv *priv = data; |
84 | u32 stats; |
85 | |
86 | regmap_read(map: priv->regs, MX25_ADCQ_SR, val: &stats); |
87 | |
88 | if (stats & MX25_ADCQ_SR_EOQ) { |
89 | regmap_update_bits(map: priv->regs, MX25_ADCQ_MR, |
90 | MX25_ADCQ_MR_EOQ_IRQ, MX25_ADCQ_MR_EOQ_IRQ); |
91 | complete(&priv->completed); |
92 | } |
93 | |
94 | /* Disable conversion queue run */ |
95 | regmap_update_bits(map: priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, val: 0); |
96 | |
97 | /* Acknowledge all possible irqs */ |
98 | regmap_write(map: priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR | |
99 | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | |
100 | MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD); |
101 | |
102 | return IRQ_HANDLED; |
103 | } |
104 | |
105 | static int mx25_gcq_get_raw_value(struct device *dev, |
106 | struct iio_chan_spec const *chan, |
107 | struct mx25_gcq_priv *priv, |
108 | int *val) |
109 | { |
110 | long timeout; |
111 | u32 data; |
112 | |
113 | /* Setup the configuration we want to use */ |
114 | regmap_write(map: priv->regs, MX25_ADCQ_ITEM_7_0, |
115 | MX25_ADCQ_ITEM(0, chan->channel)); |
116 | |
117 | regmap_update_bits(map: priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ, val: 0); |
118 | |
119 | /* Trigger queue for one run */ |
120 | regmap_update_bits(map: priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, |
121 | MX25_ADCQ_CR_FQS); |
122 | |
123 | timeout = wait_for_completion_interruptible_timeout( |
124 | x: &priv->completed, MX25_GCQ_TIMEOUT); |
125 | if (timeout < 0) { |
126 | dev_err(dev, "ADC wait for measurement failed\n" ); |
127 | return timeout; |
128 | } else if (timeout == 0) { |
129 | dev_err(dev, "ADC timed out\n" ); |
130 | return -ETIMEDOUT; |
131 | } |
132 | |
133 | regmap_read(map: priv->regs, MX25_ADCQ_FIFO, val: &data); |
134 | |
135 | *val = MX25_ADCQ_FIFO_DATA(data); |
136 | |
137 | return IIO_VAL_INT; |
138 | } |
139 | |
140 | static int mx25_gcq_read_raw(struct iio_dev *indio_dev, |
141 | struct iio_chan_spec const *chan, int *val, |
142 | int *val2, long mask) |
143 | { |
144 | struct mx25_gcq_priv *priv = iio_priv(indio_dev); |
145 | int ret; |
146 | |
147 | switch (mask) { |
148 | case IIO_CHAN_INFO_RAW: |
149 | mutex_lock(&priv->lock); |
150 | ret = mx25_gcq_get_raw_value(dev: &indio_dev->dev, chan, priv, val); |
151 | mutex_unlock(lock: &priv->lock); |
152 | return ret; |
153 | |
154 | case IIO_CHAN_INFO_SCALE: |
155 | *val = priv->channel_vref_mv[chan->channel]; |
156 | *val2 = 12; |
157 | return IIO_VAL_FRACTIONAL_LOG2; |
158 | |
159 | default: |
160 | return -EINVAL; |
161 | } |
162 | } |
163 | |
164 | static const struct iio_info mx25_gcq_iio_info = { |
165 | .read_raw = mx25_gcq_read_raw, |
166 | }; |
167 | |
168 | static const struct regmap_config mx25_gcq_regconfig = { |
169 | .max_register = 0x5c, |
170 | .reg_bits = 32, |
171 | .val_bits = 32, |
172 | .reg_stride = 4, |
173 | }; |
174 | |
175 | static int mx25_gcq_ext_regulator_setup(struct device *dev, |
176 | struct mx25_gcq_priv *priv, u32 refp) |
177 | { |
178 | char reg_name[12]; |
179 | int ret; |
180 | |
181 | if (priv->vref[refp]) |
182 | return 0; |
183 | |
184 | ret = snprintf(buf: reg_name, size: sizeof(reg_name), fmt: "vref-%s" , |
185 | mx25_gcq_refp_names[refp]); |
186 | if (ret < 0) |
187 | return ret; |
188 | |
189 | priv->vref[refp] = devm_regulator_get_optional(dev, id: reg_name); |
190 | if (IS_ERR(ptr: priv->vref[refp])) |
191 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->vref[refp]), |
192 | fmt: "Error, trying to use external voltage reference without a %s regulator." , |
193 | reg_name); |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static int mx25_gcq_setup_cfgs(struct platform_device *pdev, |
199 | struct mx25_gcq_priv *priv) |
200 | { |
201 | struct device_node *np = pdev->dev.of_node; |
202 | struct device_node *child; |
203 | struct device *dev = &pdev->dev; |
204 | int ret, i; |
205 | |
206 | /* |
207 | * Setup all configurations registers with a default conversion |
208 | * configuration for each input |
209 | */ |
210 | for (i = 0; i < MX25_NUM_CFGS; ++i) |
211 | regmap_write(map: priv->regs, MX25_ADCQ_CFG(i), |
212 | MX25_ADCQ_CFG_YPLL_OFF | |
213 | MX25_ADCQ_CFG_XNUR_OFF | |
214 | MX25_ADCQ_CFG_XPUL_OFF | |
215 | MX25_ADCQ_CFG_REFP_INT | |
216 | MX25_ADCQ_CFG_IN(i) | |
217 | MX25_ADCQ_CFG_REFN_NGND2); |
218 | |
219 | for_each_child_of_node(np, child) { |
220 | u32 reg; |
221 | u32 refp = MX25_ADCQ_CFG_REFP_INT; |
222 | u32 refn = MX25_ADCQ_CFG_REFN_NGND2; |
223 | |
224 | ret = of_property_read_u32(np: child, propname: "reg" , out_value: ®); |
225 | if (ret) { |
226 | dev_err(dev, "Failed to get reg property\n" ); |
227 | of_node_put(node: child); |
228 | return ret; |
229 | } |
230 | |
231 | if (reg >= MX25_NUM_CFGS) { |
232 | dev_err(dev, |
233 | "reg value is greater than the number of available configuration registers\n" ); |
234 | of_node_put(node: child); |
235 | return -EINVAL; |
236 | } |
237 | |
238 | of_property_read_u32(np: child, propname: "fsl,adc-refp" , out_value: &refp); |
239 | of_property_read_u32(np: child, propname: "fsl,adc-refn" , out_value: &refn); |
240 | |
241 | switch (refp) { |
242 | case MX25_ADC_REFP_EXT: |
243 | case MX25_ADC_REFP_XP: |
244 | case MX25_ADC_REFP_YP: |
245 | ret = mx25_gcq_ext_regulator_setup(dev: &pdev->dev, priv, refp); |
246 | if (ret) { |
247 | of_node_put(node: child); |
248 | return ret; |
249 | } |
250 | priv->channel_vref_mv[reg] = |
251 | regulator_get_voltage(regulator: priv->vref[refp]); |
252 | /* Conversion from uV to mV */ |
253 | priv->channel_vref_mv[reg] /= 1000; |
254 | break; |
255 | case MX25_ADC_REFP_INT: |
256 | priv->channel_vref_mv[reg] = 2500; |
257 | break; |
258 | default: |
259 | dev_err(dev, "Invalid positive reference %d\n" , refp); |
260 | of_node_put(node: child); |
261 | return -EINVAL; |
262 | } |
263 | |
264 | /* |
265 | * Shift the read values to the correct positions within the |
266 | * register. |
267 | */ |
268 | refp = MX25_ADCQ_CFG_REFP(refp); |
269 | refn = MX25_ADCQ_CFG_REFN(refn); |
270 | |
271 | if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) { |
272 | dev_err(dev, "Invalid fsl,adc-refp property value\n" ); |
273 | of_node_put(node: child); |
274 | return -EINVAL; |
275 | } |
276 | if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) { |
277 | dev_err(dev, "Invalid fsl,adc-refn property value\n" ); |
278 | of_node_put(node: child); |
279 | return -EINVAL; |
280 | } |
281 | |
282 | regmap_update_bits(map: priv->regs, MX25_ADCQ_CFG(reg), |
283 | MX25_ADCQ_CFG_REFP_MASK | |
284 | MX25_ADCQ_CFG_REFN_MASK, |
285 | val: refp | refn); |
286 | } |
287 | regmap_update_bits(map: priv->regs, MX25_ADCQ_CR, |
288 | MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST, |
289 | MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST); |
290 | |
291 | regmap_write(map: priv->regs, MX25_ADCQ_CR, |
292 | MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS); |
293 | |
294 | return 0; |
295 | } |
296 | |
297 | static int mx25_gcq_probe(struct platform_device *pdev) |
298 | { |
299 | struct iio_dev *indio_dev; |
300 | struct mx25_gcq_priv *priv; |
301 | struct mx25_tsadc *tsadc = dev_get_drvdata(dev: pdev->dev.parent); |
302 | struct device *dev = &pdev->dev; |
303 | void __iomem *mem; |
304 | int ret; |
305 | int i; |
306 | |
307 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*priv)); |
308 | if (!indio_dev) |
309 | return -ENOMEM; |
310 | |
311 | priv = iio_priv(indio_dev); |
312 | |
313 | mem = devm_platform_ioremap_resource(pdev, index: 0); |
314 | if (IS_ERR(ptr: mem)) |
315 | return PTR_ERR(ptr: mem); |
316 | |
317 | priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig); |
318 | if (IS_ERR(ptr: priv->regs)) { |
319 | dev_err(dev, "Failed to initialize regmap\n" ); |
320 | return PTR_ERR(ptr: priv->regs); |
321 | } |
322 | |
323 | mutex_init(&priv->lock); |
324 | |
325 | init_completion(x: &priv->completed); |
326 | |
327 | ret = mx25_gcq_setup_cfgs(pdev, priv); |
328 | if (ret) |
329 | return ret; |
330 | |
331 | for (i = 0; i != 4; ++i) { |
332 | if (!priv->vref[i]) |
333 | continue; |
334 | |
335 | ret = regulator_enable(regulator: priv->vref[i]); |
336 | if (ret) |
337 | goto err_regulator_disable; |
338 | } |
339 | |
340 | priv->clk = tsadc->clk; |
341 | ret = clk_prepare_enable(clk: priv->clk); |
342 | if (ret) { |
343 | dev_err(dev, "Failed to enable clock\n" ); |
344 | goto err_vref_disable; |
345 | } |
346 | |
347 | ret = platform_get_irq(pdev, 0); |
348 | if (ret < 0) |
349 | goto err_clk_unprepare; |
350 | |
351 | priv->irq = ret; |
352 | ret = request_irq(irq: priv->irq, handler: mx25_gcq_irq, flags: 0, name: pdev->name, dev: priv); |
353 | if (ret) { |
354 | dev_err(dev, "Failed requesting IRQ\n" ); |
355 | goto err_clk_unprepare; |
356 | } |
357 | |
358 | indio_dev->channels = mx25_gcq_channels; |
359 | indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels); |
360 | indio_dev->info = &mx25_gcq_iio_info; |
361 | indio_dev->name = driver_name; |
362 | |
363 | ret = iio_device_register(indio_dev); |
364 | if (ret) { |
365 | dev_err(dev, "Failed to register iio device\n" ); |
366 | goto err_irq_free; |
367 | } |
368 | |
369 | platform_set_drvdata(pdev, data: indio_dev); |
370 | |
371 | return 0; |
372 | |
373 | err_irq_free: |
374 | free_irq(priv->irq, priv); |
375 | err_clk_unprepare: |
376 | clk_disable_unprepare(clk: priv->clk); |
377 | err_vref_disable: |
378 | i = 4; |
379 | err_regulator_disable: |
380 | for (; i-- > 0;) { |
381 | if (priv->vref[i]) |
382 | regulator_disable(regulator: priv->vref[i]); |
383 | } |
384 | return ret; |
385 | } |
386 | |
387 | static void mx25_gcq_remove(struct platform_device *pdev) |
388 | { |
389 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); |
390 | struct mx25_gcq_priv *priv = iio_priv(indio_dev); |
391 | int i; |
392 | |
393 | iio_device_unregister(indio_dev); |
394 | free_irq(priv->irq, priv); |
395 | clk_disable_unprepare(clk: priv->clk); |
396 | for (i = 4; i-- > 0;) { |
397 | if (priv->vref[i]) |
398 | regulator_disable(regulator: priv->vref[i]); |
399 | } |
400 | } |
401 | |
402 | static const struct of_device_id mx25_gcq_ids[] = { |
403 | { .compatible = "fsl,imx25-gcq" , }, |
404 | { /* Sentinel */ } |
405 | }; |
406 | MODULE_DEVICE_TABLE(of, mx25_gcq_ids); |
407 | |
408 | static struct platform_driver mx25_gcq_driver = { |
409 | .driver = { |
410 | .name = "mx25-gcq" , |
411 | .of_match_table = mx25_gcq_ids, |
412 | }, |
413 | .probe = mx25_gcq_probe, |
414 | .remove_new = mx25_gcq_remove, |
415 | }; |
416 | module_platform_driver(mx25_gcq_driver); |
417 | |
418 | MODULE_DESCRIPTION("ADC driver for Freescale mx25" ); |
419 | MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>" ); |
420 | MODULE_LICENSE("GPL v2" ); |
421 | |