2 * Driver for an envelope detector using a DAC and a comparator
4 * Copyright (C) 2016 Axentia Technologies AB
6 * Author: Peter Rosin <peda@axentia.se>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
14 * The DAC is used to find the peak level of an alternating voltage input
15 * signal by a binary search using the output of a comparator wired to
16 * an interrupt pin. Like so:
19 * input +------>-------|+ \
27 * | irq|------<-------'
32 #include <linux/completion.h>
33 #include <linux/device.h>
34 #include <linux/err.h>
35 #include <linux/kernel.h>
36 #include <linux/module.h>
37 #include <linux/mutex.h>
38 #include <linux/iio/consumer.h>
39 #include <linux/iio/iio.h>
40 #include <linux/iio/sysfs.h>
41 #include <linux/interrupt.h>
42 #include <linux/irq.h>
44 #include <linux/of_device.h>
45 #include <linux/platform_device.h>
46 #include <linux/spinlock.h>
47 #include <linux/workqueue.h>
50 spinlock_t comp_lock
; /* protects comp */
53 struct mutex read_lock
; /* protects everything else */
57 u32 comp_irq_trigger_inv
;
59 struct iio_channel
*dac
;
60 struct delayed_work comp_timeout
;
62 unsigned int comp_interval
;
70 struct completion done
;
74 * The envelope_detector_comp_latch function works together with the compare
75 * interrupt service routine below (envelope_detector_comp_isr) as a latch
76 * (one-bit memory) for if the interrupt has triggered since last calling
78 * The ..._comp_isr function disables the interrupt so that the cpu does not
79 * need to service a possible interrupt flood from the comparator when no-one
80 * cares anyway, and this ..._comp_latch function reenables them again if
83 static int envelope_detector_comp_latch(struct envelope
*env
)
87 spin_lock_irq(&env
->comp_lock
);
90 spin_unlock_irq(&env
->comp_lock
);
96 * The irq was disabled, and is reenabled just now.
97 * But there might have been a pending irq that
98 * happened while the irq was disabled that fires
99 * just as the irq is reenabled. That is not what
102 enable_irq(env
->comp_irq
);
104 /* So, synchronize this possibly pending irq... */
105 synchronize_irq(env
->comp_irq
);
107 /* ...and redo the whole dance. */
108 spin_lock_irq(&env
->comp_lock
);
111 spin_unlock_irq(&env
->comp_lock
);
114 enable_irq(env
->comp_irq
);
119 static irqreturn_t
envelope_detector_comp_isr(int irq
, void *ctx
)
121 struct envelope
*env
= ctx
;
123 spin_lock(&env
->comp_lock
);
125 disable_irq_nosync(env
->comp_irq
);
126 spin_unlock(&env
->comp_lock
);
131 static void envelope_detector_setup_compare(struct envelope
*env
)
136 * Do a binary search for the peak input level, and stop
137 * when that level is "trapped" between two adjacent DAC
139 * When invert is active, use the midpoint floor so that
140 * env->level ends up as env->low when the termination
141 * criteria below is fulfilled, and use the midpoint
142 * ceiling when invert is not active so that env->level
143 * ends up as env->high in that case.
145 env
->level
= (env
->high
+ env
->low
+ !env
->invert
) / 2;
147 if (env
->high
== env
->low
+ 1) {
148 complete(&env
->done
);
152 /* Set a "safe" DAC level (if there is such a thing)... */
153 ret
= iio_write_channel_raw(env
->dac
, env
->invert
? 0 : env
->dac_max
);
157 /* ...clear the comparison result... */
158 envelope_detector_comp_latch(env
);
160 /* ...set the real DAC level... */
161 ret
= iio_write_channel_raw(env
->dac
, env
->level
);
165 /* ...and wait for a bit to see if the latch catches anything. */
166 schedule_delayed_work(&env
->comp_timeout
,
167 msecs_to_jiffies(env
->comp_interval
));
172 complete(&env
->done
);
175 static void envelope_detector_timeout(struct work_struct
*work
)
177 struct envelope
*env
= container_of(work
, struct envelope
,
180 /* Adjust low/high depending on the latch content... */
181 if (!envelope_detector_comp_latch(env
) ^ !env
->invert
)
182 env
->low
= env
->level
;
184 env
->high
= env
->level
;
186 /* ...and continue the search. */
187 envelope_detector_setup_compare(env
);
190 static int envelope_detector_read_raw(struct iio_dev
*indio_dev
,
191 struct iio_chan_spec
const *chan
,
192 int *val
, int *val2
, long mask
)
194 struct envelope
*env
= iio_priv(indio_dev
);
198 case IIO_CHAN_INFO_RAW
:
200 * When invert is active, start with high=max+1 and low=0
201 * since we will end up with the low value when the
202 * termination criteria is fulfilled (rounding down). And
203 * start with high=max and low=-1 when invert is not active
204 * since we will end up with the high value in that case.
205 * This ensures that the returned value in both cases are
206 * in the same range as the DAC and is a value that has not
207 * triggered the comparator.
209 mutex_lock(&env
->read_lock
);
210 env
->high
= env
->dac_max
+ env
->invert
;
211 env
->low
= -1 + env
->invert
;
212 envelope_detector_setup_compare(env
);
213 wait_for_completion(&env
->done
);
214 if (env
->level
< 0) {
218 *val
= env
->invert
? env
->dac_max
- env
->level
: env
->level
;
219 mutex_unlock(&env
->read_lock
);
223 case IIO_CHAN_INFO_SCALE
:
224 return iio_read_channel_scale(env
->dac
, val
, val2
);
230 mutex_unlock(&env
->read_lock
);
234 static ssize_t
envelope_show_invert(struct iio_dev
*indio_dev
,
236 struct iio_chan_spec
const *ch
, char *buf
)
238 struct envelope
*env
= iio_priv(indio_dev
);
240 return sprintf(buf
, "%u\n", env
->invert
);
243 static ssize_t
envelope_store_invert(struct iio_dev
*indio_dev
,
245 struct iio_chan_spec
const *ch
,
246 const char *buf
, size_t len
)
248 struct envelope
*env
= iio_priv(indio_dev
);
249 unsigned long invert
;
253 ret
= kstrtoul(buf
, 0, &invert
);
259 trigger
= invert
? env
->comp_irq_trigger_inv
: env
->comp_irq_trigger
;
261 mutex_lock(&env
->read_lock
);
262 if (invert
!= env
->invert
)
263 ret
= irq_set_irq_type(env
->comp_irq
, trigger
);
265 env
->invert
= invert
;
268 mutex_unlock(&env
->read_lock
);
273 static ssize_t
envelope_show_comp_interval(struct iio_dev
*indio_dev
,
275 struct iio_chan_spec
const *ch
,
278 struct envelope
*env
= iio_priv(indio_dev
);
280 return sprintf(buf
, "%u\n", env
->comp_interval
);
283 static ssize_t
envelope_store_comp_interval(struct iio_dev
*indio_dev
,
285 struct iio_chan_spec
const *ch
,
286 const char *buf
, size_t len
)
288 struct envelope
*env
= iio_priv(indio_dev
);
289 unsigned long interval
;
292 ret
= kstrtoul(buf
, 0, &interval
);
298 mutex_lock(&env
->read_lock
);
299 env
->comp_interval
= interval
;
300 mutex_unlock(&env
->read_lock
);
305 static const struct iio_chan_spec_ext_info envelope_detector_ext_info
[] = {
307 .read
= envelope_show_invert
,
308 .write
= envelope_store_invert
, },
309 { .name
= "compare_interval",
310 .read
= envelope_show_comp_interval
,
311 .write
= envelope_store_comp_interval
, },
315 static const struct iio_chan_spec envelope_detector_iio_channel
= {
316 .type
= IIO_ALTVOLTAGE
,
317 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
)
318 | BIT(IIO_CHAN_INFO_SCALE
),
319 .ext_info
= envelope_detector_ext_info
,
323 static const struct iio_info envelope_detector_info
= {
324 .read_raw
= &envelope_detector_read_raw
,
325 .driver_module
= THIS_MODULE
,
328 static int envelope_detector_probe(struct platform_device
*pdev
)
330 struct device
*dev
= &pdev
->dev
;
331 struct iio_dev
*indio_dev
;
332 struct envelope
*env
;
333 enum iio_chan_type type
;
336 indio_dev
= devm_iio_device_alloc(dev
, sizeof(*env
));
340 platform_set_drvdata(pdev
, indio_dev
);
341 env
= iio_priv(indio_dev
);
342 env
->comp_interval
= 50; /* some sensible default? */
344 spin_lock_init(&env
->comp_lock
);
345 mutex_init(&env
->read_lock
);
346 init_completion(&env
->done
);
347 INIT_DELAYED_WORK(&env
->comp_timeout
, envelope_detector_timeout
);
349 indio_dev
->name
= dev_name(dev
);
350 indio_dev
->dev
.parent
= dev
;
351 indio_dev
->dev
.of_node
= dev
->of_node
;
352 indio_dev
->info
= &envelope_detector_info
;
353 indio_dev
->channels
= &envelope_detector_iio_channel
;
354 indio_dev
->num_channels
= 1;
356 env
->dac
= devm_iio_channel_get(dev
, "dac");
357 if (IS_ERR(env
->dac
)) {
358 if (PTR_ERR(env
->dac
) != -EPROBE_DEFER
)
359 dev_err(dev
, "failed to get dac input channel\n");
360 return PTR_ERR(env
->dac
);
363 env
->comp_irq
= platform_get_irq_byname(pdev
, "comp");
364 if (env
->comp_irq
< 0) {
365 if (env
->comp_irq
!= -EPROBE_DEFER
)
366 dev_err(dev
, "failed to get compare interrupt\n");
367 return env
->comp_irq
;
370 ret
= devm_request_irq(dev
, env
->comp_irq
, envelope_detector_comp_isr
,
371 0, "envelope-detector", env
);
373 if (ret
!= -EPROBE_DEFER
)
374 dev_err(dev
, "failed to request interrupt\n");
377 env
->comp_irq_trigger
= irq_get_trigger_type(env
->comp_irq
);
378 if (env
->comp_irq_trigger
& IRQF_TRIGGER_RISING
)
379 env
->comp_irq_trigger_inv
|= IRQF_TRIGGER_FALLING
;
380 if (env
->comp_irq_trigger
& IRQF_TRIGGER_FALLING
)
381 env
->comp_irq_trigger_inv
|= IRQF_TRIGGER_RISING
;
382 if (env
->comp_irq_trigger
& IRQF_TRIGGER_HIGH
)
383 env
->comp_irq_trigger_inv
|= IRQF_TRIGGER_LOW
;
384 if (env
->comp_irq_trigger
& IRQF_TRIGGER_LOW
)
385 env
->comp_irq_trigger_inv
|= IRQF_TRIGGER_HIGH
;
387 ret
= iio_get_channel_type(env
->dac
, &type
);
391 if (type
!= IIO_VOLTAGE
) {
392 dev_err(dev
, "dac is of the wrong type\n");
396 ret
= iio_read_max_channel_raw(env
->dac
, &env
->dac_max
);
398 dev_err(dev
, "dac does not indicate its raw maximum value\n");
402 return devm_iio_device_register(dev
, indio_dev
);
405 static const struct of_device_id envelope_detector_match
[] = {
406 { .compatible
= "axentia,tse850-envelope-detector", },
409 MODULE_DEVICE_TABLE(of
, envelope_detector_match
);
411 static struct platform_driver envelope_detector_driver
= {
412 .probe
= envelope_detector_probe
,
414 .name
= "iio-envelope-detector",
415 .of_match_table
= envelope_detector_match
,
418 module_platform_driver(envelope_detector_driver
);
420 MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
421 MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
422 MODULE_LICENSE("GPL v2");