Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6df2e98c MP |
2 | /* |
3 | * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de> | |
4 | * | |
6df2e98c MP |
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]; | |
8a0f412f SC |
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; | |
6df2e98c MP |
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(priv->regs, MX25_ADCQ_SR, &stats); | |
87 | ||
88 | if (stats & MX25_ADCQ_SR_EOQ) { | |
89 | regmap_update_bits(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(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, 0); | |
96 | ||
97 | /* Acknowledge all possible irqs */ | |
98 | regmap_write(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(priv->regs, MX25_ADCQ_ITEM_7_0, | |
115 | MX25_ADCQ_ITEM(0, chan->channel)); | |
116 | ||
117 | regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ, 0); | |
118 | ||
119 | /* Trigger queue for one run */ | |
120 | regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, | |
121 | MX25_ADCQ_CR_FQS); | |
122 | ||
123 | timeout = wait_for_completion_interruptible_timeout( | |
124 | &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(priv->regs, MX25_ADCQ_FIFO, &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: | |
8a0f412f | 149 | mutex_lock(&priv->lock); |
6df2e98c | 150 | ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val); |
8a0f412f | 151 | mutex_unlock(&priv->lock); |
6df2e98c MP |
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 | ||
c5fd034a AA |
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(reg_name, sizeof(reg_name), "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, reg_name); | |
190 | if (IS_ERR(priv->vref[refp])) | |
191 | return dev_err_probe(dev, PTR_ERR(priv->vref[refp]), | |
192 | "Error, trying to use external voltage reference without a %s regulator.", | |
193 | reg_name); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
6df2e98c MP |
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; | |
6df2e98c MP |
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(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 | ||
6df2e98c MP |
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(child, "reg", ®); | |
225 | if (ret) { | |
226 | dev_err(dev, "Failed to get reg property\n"); | |
d3fa21c7 | 227 | of_node_put(child); |
6df2e98c MP |
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"); | |
d3fa21c7 | 234 | of_node_put(child); |
6df2e98c MP |
235 | return -EINVAL; |
236 | } | |
237 | ||
238 | of_property_read_u32(child, "fsl,adc-refp", &refp); | |
239 | of_property_read_u32(child, "fsl,adc-refn", &refn); | |
240 | ||
241 | switch (refp) { | |
242 | case MX25_ADC_REFP_EXT: | |
243 | case MX25_ADC_REFP_XP: | |
244 | case MX25_ADC_REFP_YP: | |
c5fd034a AA |
245 | ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp); |
246 | if (ret) { | |
d3fa21c7 | 247 | of_node_put(child); |
c5fd034a | 248 | return ret; |
6df2e98c MP |
249 | } |
250 | priv->channel_vref_mv[reg] = | |
251 | regulator_get_voltage(priv->vref[refp]); | |
252 | /* Conversion from uV to mV */ | |
07086775 | 253 | priv->channel_vref_mv[reg] /= 1000; |
6df2e98c MP |
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); | |
d3fa21c7 | 260 | of_node_put(child); |
6df2e98c MP |
261 | return -EINVAL; |
262 | } | |
263 | ||
6df2e98c MP |
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"); | |
d3fa21c7 | 273 | of_node_put(child); |
6df2e98c MP |
274 | return -EINVAL; |
275 | } | |
276 | if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) { | |
277 | dev_err(dev, "Invalid fsl,adc-refn property value\n"); | |
d3fa21c7 | 278 | of_node_put(child); |
6df2e98c MP |
279 | return -EINVAL; |
280 | } | |
281 | ||
282 | regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg), | |
283 | MX25_ADCQ_CFG_REFP_MASK | | |
284 | MX25_ADCQ_CFG_REFN_MASK, | |
285 | refp | refn); | |
286 | } | |
287 | regmap_update_bits(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(priv->regs, MX25_ADCQ_CR, | |
292 | MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS); | |
293 | ||
6df2e98c MP |
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(pdev->dev.parent); | |
302 | struct device *dev = &pdev->dev; | |
6df2e98c MP |
303 | void __iomem *mem; |
304 | int ret; | |
305 | int i; | |
306 | ||
78a6af33 | 307 | indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); |
6df2e98c MP |
308 | if (!indio_dev) |
309 | return -ENOMEM; | |
310 | ||
311 | priv = iio_priv(indio_dev); | |
312 | ||
587122cb | 313 | mem = devm_platform_ioremap_resource(pdev, 0); |
6df2e98c MP |
314 | if (IS_ERR(mem)) |
315 | return PTR_ERR(mem); | |
316 | ||
317 | priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig); | |
318 | if (IS_ERR(priv->regs)) { | |
319 | dev_err(dev, "Failed to initialize regmap\n"); | |
320 | return PTR_ERR(priv->regs); | |
321 | } | |
322 | ||
8a0f412f SC |
323 | mutex_init(&priv->lock); |
324 | ||
6df2e98c MP |
325 | init_completion(&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(priv->vref[i]); | |
336 | if (ret) | |
337 | goto err_regulator_disable; | |
338 | } | |
339 | ||
340 | priv->clk = tsadc->clk; | |
341 | ret = clk_prepare_enable(priv->clk); | |
342 | if (ret) { | |
343 | dev_err(dev, "Failed to enable clock\n"); | |
344 | goto err_vref_disable; | |
345 | } | |
346 | ||
f27b1b2a TB |
347 | ret = platform_get_irq(pdev, 0); |
348 | if (ret < 0) | |
6df2e98c | 349 | goto err_clk_unprepare; |
6df2e98c | 350 | |
f27b1b2a | 351 | priv->irq = ret; |
6df2e98c MP |
352 | ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv); |
353 | if (ret) { | |
354 | dev_err(dev, "Failed requesting IRQ\n"); | |
355 | goto err_clk_unprepare; | |
356 | } | |
357 | ||
6df2e98c MP |
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, 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(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(priv->vref[i]); | |
383 | } | |
384 | return ret; | |
385 | } | |
386 | ||
387 | static int 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(priv->clk); | |
396 | for (i = 4; i-- > 0;) { | |
397 | if (priv->vref[i]) | |
398 | regulator_disable(priv->vref[i]); | |
399 | } | |
400 | ||
401 | return 0; | |
402 | } | |
403 | ||
404 | static const struct of_device_id mx25_gcq_ids[] = { | |
405 | { .compatible = "fsl,imx25-gcq", }, | |
406 | { /* Sentinel */ } | |
407 | }; | |
8f0d7daf | 408 | MODULE_DEVICE_TABLE(of, mx25_gcq_ids); |
6df2e98c MP |
409 | |
410 | static struct platform_driver mx25_gcq_driver = { | |
411 | .driver = { | |
412 | .name = "mx25-gcq", | |
413 | .of_match_table = mx25_gcq_ids, | |
414 | }, | |
415 | .probe = mx25_gcq_probe, | |
416 | .remove = mx25_gcq_remove, | |
417 | }; | |
418 | module_platform_driver(mx25_gcq_driver); | |
419 | ||
420 | MODULE_DESCRIPTION("ADC driver for Freescale mx25"); | |
421 | MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>"); | |
422 | MODULE_LICENSE("GPL v2"); |