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