110d6e795SPeter Rosin // SPDX-License-Identifier: GPL-2.0
2b475f80bSPeter Rosin /*
3b475f80bSPeter Rosin  * Driver for an envelope detector using a DAC and a comparator
4b475f80bSPeter Rosin  *
5b475f80bSPeter Rosin  * Copyright (C) 2016 Axentia Technologies AB
6b475f80bSPeter Rosin  *
7b475f80bSPeter Rosin  * Author: Peter Rosin <peda@axentia.se>
8b475f80bSPeter Rosin  */
9b475f80bSPeter Rosin 
10b475f80bSPeter Rosin /*
11b475f80bSPeter Rosin  * The DAC is used to find the peak level of an alternating voltage input
12b475f80bSPeter Rosin  * signal by a binary search using the output of a comparator wired to
13b475f80bSPeter Rosin  * an interrupt pin. Like so:
14b475f80bSPeter Rosin  *                           _
15b475f80bSPeter Rosin  *                          | \
16b475f80bSPeter Rosin  *     input +------>-------|+ \
17b475f80bSPeter Rosin  *                          |   \
18b475f80bSPeter Rosin  *            .-------.     |    }---.
19b475f80bSPeter Rosin  *            |       |     |   /    |
20b475f80bSPeter Rosin  *            |    dac|-->--|- /     |
21b475f80bSPeter Rosin  *            |       |     |_/      |
22b475f80bSPeter Rosin  *            |       |              |
23b475f80bSPeter Rosin  *            |       |              |
24b475f80bSPeter Rosin  *            |    irq|------<-------'
25b475f80bSPeter Rosin  *            |       |
26b475f80bSPeter Rosin  *            '-------'
27b475f80bSPeter Rosin  */
28b475f80bSPeter Rosin 
29b475f80bSPeter Rosin #include <linux/completion.h>
30b475f80bSPeter Rosin #include <linux/device.h>
31b475f80bSPeter Rosin #include <linux/err.h>
32b475f80bSPeter Rosin #include <linux/kernel.h>
33b475f80bSPeter Rosin #include <linux/module.h>
34*f346c965SJonathan Cameron #include <linux/mod_devicetable.h>
35b475f80bSPeter Rosin #include <linux/mutex.h>
36b475f80bSPeter Rosin #include <linux/iio/consumer.h>
37b475f80bSPeter Rosin #include <linux/iio/iio.h>
38b475f80bSPeter Rosin #include <linux/iio/sysfs.h>
39b475f80bSPeter Rosin #include <linux/interrupt.h>
40b475f80bSPeter Rosin #include <linux/irq.h>
41b475f80bSPeter Rosin #include <linux/platform_device.h>
42b475f80bSPeter Rosin #include <linux/spinlock.h>
43b475f80bSPeter Rosin #include <linux/workqueue.h>
44b475f80bSPeter Rosin 
45b475f80bSPeter Rosin struct envelope {
46b475f80bSPeter Rosin 	spinlock_t comp_lock; /* protects comp */
47b475f80bSPeter Rosin 	int comp;
48b475f80bSPeter Rosin 
49b475f80bSPeter Rosin 	struct mutex read_lock; /* protects everything else */
50b475f80bSPeter Rosin 
51b475f80bSPeter Rosin 	int comp_irq;
52b475f80bSPeter Rosin 	u32 comp_irq_trigger;
53b475f80bSPeter Rosin 	u32 comp_irq_trigger_inv;
54b475f80bSPeter Rosin 
55b475f80bSPeter Rosin 	struct iio_channel *dac;
56b475f80bSPeter Rosin 	struct delayed_work comp_timeout;
57b475f80bSPeter Rosin 
58b475f80bSPeter Rosin 	unsigned int comp_interval;
59b475f80bSPeter Rosin 	bool invert;
60b475f80bSPeter Rosin 	u32 dac_max;
61b475f80bSPeter Rosin 
62b475f80bSPeter Rosin 	int high;
63b475f80bSPeter Rosin 	int level;
64b475f80bSPeter Rosin 	int low;
65b475f80bSPeter Rosin 
66b475f80bSPeter Rosin 	struct completion done;
67b475f80bSPeter Rosin };
68b475f80bSPeter Rosin 
69b475f80bSPeter Rosin /*
70b475f80bSPeter Rosin  * The envelope_detector_comp_latch function works together with the compare
71b475f80bSPeter Rosin  * interrupt service routine below (envelope_detector_comp_isr) as a latch
72b475f80bSPeter Rosin  * (one-bit memory) for if the interrupt has triggered since last calling
73b475f80bSPeter Rosin  * this function.
74b475f80bSPeter Rosin  * The ..._comp_isr function disables the interrupt so that the cpu does not
75b475f80bSPeter Rosin  * need to service a possible interrupt flood from the comparator when no-one
76b475f80bSPeter Rosin  * cares anyway, and this ..._comp_latch function reenables them again if
77b475f80bSPeter Rosin  * needed.
78b475f80bSPeter Rosin  */
envelope_detector_comp_latch(struct envelope * env)79b475f80bSPeter Rosin static int envelope_detector_comp_latch(struct envelope *env)
80b475f80bSPeter Rosin {
81b475f80bSPeter Rosin 	int comp;
82b475f80bSPeter Rosin 
83b475f80bSPeter Rosin 	spin_lock_irq(&env->comp_lock);
84b475f80bSPeter Rosin 	comp = env->comp;
85b475f80bSPeter Rosin 	env->comp = 0;
86b475f80bSPeter Rosin 	spin_unlock_irq(&env->comp_lock);
87b475f80bSPeter Rosin 
88b475f80bSPeter Rosin 	if (!comp)
89b475f80bSPeter Rosin 		return 0;
90b475f80bSPeter Rosin 
91b475f80bSPeter Rosin 	/*
92b475f80bSPeter Rosin 	 * The irq was disabled, and is reenabled just now.
93b475f80bSPeter Rosin 	 * But there might have been a pending irq that
94b475f80bSPeter Rosin 	 * happened while the irq was disabled that fires
95b475f80bSPeter Rosin 	 * just as the irq is reenabled. That is not what
96b475f80bSPeter Rosin 	 * is desired.
97b475f80bSPeter Rosin 	 */
98b475f80bSPeter Rosin 	enable_irq(env->comp_irq);
99b475f80bSPeter Rosin 
100b475f80bSPeter Rosin 	/* So, synchronize this possibly pending irq... */
101b475f80bSPeter Rosin 	synchronize_irq(env->comp_irq);
102b475f80bSPeter Rosin 
103b475f80bSPeter Rosin 	/* ...and redo the whole dance. */
104b475f80bSPeter Rosin 	spin_lock_irq(&env->comp_lock);
105b475f80bSPeter Rosin 	comp = env->comp;
106b475f80bSPeter Rosin 	env->comp = 0;
107b475f80bSPeter Rosin 	spin_unlock_irq(&env->comp_lock);
108b475f80bSPeter Rosin 
109b475f80bSPeter Rosin 	if (comp)
110b475f80bSPeter Rosin 		enable_irq(env->comp_irq);
111b475f80bSPeter Rosin 
112b475f80bSPeter Rosin 	return 1;
113b475f80bSPeter Rosin }
114b475f80bSPeter Rosin 
envelope_detector_comp_isr(int irq,void * ctx)115b475f80bSPeter Rosin static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx)
116b475f80bSPeter Rosin {
117b475f80bSPeter Rosin 	struct envelope *env = ctx;
118b475f80bSPeter Rosin 
119b475f80bSPeter Rosin 	spin_lock(&env->comp_lock);
120b475f80bSPeter Rosin 	env->comp = 1;
121b475f80bSPeter Rosin 	disable_irq_nosync(env->comp_irq);
122b475f80bSPeter Rosin 	spin_unlock(&env->comp_lock);
123b475f80bSPeter Rosin 
124b475f80bSPeter Rosin 	return IRQ_HANDLED;
125b475f80bSPeter Rosin }
126b475f80bSPeter Rosin 
envelope_detector_setup_compare(struct envelope * env)127b475f80bSPeter Rosin static void envelope_detector_setup_compare(struct envelope *env)
128b475f80bSPeter Rosin {
129b475f80bSPeter Rosin 	int ret;
130b475f80bSPeter Rosin 
131b475f80bSPeter Rosin 	/*
132b475f80bSPeter Rosin 	 * Do a binary search for the peak input level, and stop
133b475f80bSPeter Rosin 	 * when that level is "trapped" between two adjacent DAC
134b475f80bSPeter Rosin 	 * values.
135b475f80bSPeter Rosin 	 * When invert is active, use the midpoint floor so that
136b475f80bSPeter Rosin 	 * env->level ends up as env->low when the termination
137b475f80bSPeter Rosin 	 * criteria below is fulfilled, and use the midpoint
138b475f80bSPeter Rosin 	 * ceiling when invert is not active so that env->level
139b475f80bSPeter Rosin 	 * ends up as env->high in that case.
140b475f80bSPeter Rosin 	 */
141b475f80bSPeter Rosin 	env->level = (env->high + env->low + !env->invert) / 2;
142b475f80bSPeter Rosin 
143b475f80bSPeter Rosin 	if (env->high == env->low + 1) {
144b475f80bSPeter Rosin 		complete(&env->done);
145b475f80bSPeter Rosin 		return;
146b475f80bSPeter Rosin 	}
147b475f80bSPeter Rosin 
148b475f80bSPeter Rosin 	/* Set a "safe" DAC level (if there is such a thing)... */
149b475f80bSPeter Rosin 	ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max);
150b475f80bSPeter Rosin 	if (ret < 0)
151b475f80bSPeter Rosin 		goto err;
152b475f80bSPeter Rosin 
153b475f80bSPeter Rosin 	/* ...clear the comparison result... */
154b475f80bSPeter Rosin 	envelope_detector_comp_latch(env);
155b475f80bSPeter Rosin 
156b475f80bSPeter Rosin 	/* ...set the real DAC level... */
157b475f80bSPeter Rosin 	ret = iio_write_channel_raw(env->dac, env->level);
158b475f80bSPeter Rosin 	if (ret < 0)
159b475f80bSPeter Rosin 		goto err;
160b475f80bSPeter Rosin 
161b475f80bSPeter Rosin 	/* ...and wait for a bit to see if the latch catches anything. */
162b475f80bSPeter Rosin 	schedule_delayed_work(&env->comp_timeout,
163b475f80bSPeter Rosin 			      msecs_to_jiffies(env->comp_interval));
164b475f80bSPeter Rosin 	return;
165b475f80bSPeter Rosin 
166b475f80bSPeter Rosin err:
167b475f80bSPeter Rosin 	env->level = ret;
168b475f80bSPeter Rosin 	complete(&env->done);
169b475f80bSPeter Rosin }
170b475f80bSPeter Rosin 
envelope_detector_timeout(struct work_struct * work)171b475f80bSPeter Rosin static void envelope_detector_timeout(struct work_struct *work)
172b475f80bSPeter Rosin {
173b475f80bSPeter Rosin 	struct envelope *env = container_of(work, struct envelope,
174b475f80bSPeter Rosin 					    comp_timeout.work);
175b475f80bSPeter Rosin 
176b475f80bSPeter Rosin 	/* Adjust low/high depending on the latch content... */
177b475f80bSPeter Rosin 	if (!envelope_detector_comp_latch(env) ^ !env->invert)
178b475f80bSPeter Rosin 		env->low = env->level;
179b475f80bSPeter Rosin 	else
180b475f80bSPeter Rosin 		env->high = env->level;
181b475f80bSPeter Rosin 
182b475f80bSPeter Rosin 	/* ...and continue the search. */
183b475f80bSPeter Rosin 	envelope_detector_setup_compare(env);
184b475f80bSPeter Rosin }
185b475f80bSPeter Rosin 
envelope_detector_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)186b475f80bSPeter Rosin static int envelope_detector_read_raw(struct iio_dev *indio_dev,
187b475f80bSPeter Rosin 				      struct iio_chan_spec const *chan,
188b475f80bSPeter Rosin 				      int *val, int *val2, long mask)
189b475f80bSPeter Rosin {
190b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
191b475f80bSPeter Rosin 	int ret;
192b475f80bSPeter Rosin 
193b475f80bSPeter Rosin 	switch (mask) {
194b475f80bSPeter Rosin 	case IIO_CHAN_INFO_RAW:
195b475f80bSPeter Rosin 		/*
196b475f80bSPeter Rosin 		 * When invert is active, start with high=max+1 and low=0
197b475f80bSPeter Rosin 		 * since we will end up with the low value when the
198b475f80bSPeter Rosin 		 * termination criteria is fulfilled (rounding down). And
199b475f80bSPeter Rosin 		 * start with high=max and low=-1 when invert is not active
200b475f80bSPeter Rosin 		 * since we will end up with the high value in that case.
201b475f80bSPeter Rosin 		 * This ensures that the returned value in both cases are
202b475f80bSPeter Rosin 		 * in the same range as the DAC and is a value that has not
203b475f80bSPeter Rosin 		 * triggered the comparator.
204b475f80bSPeter Rosin 		 */
205b475f80bSPeter Rosin 		mutex_lock(&env->read_lock);
206b475f80bSPeter Rosin 		env->high = env->dac_max + env->invert;
207b475f80bSPeter Rosin 		env->low = -1 + env->invert;
208b475f80bSPeter Rosin 		envelope_detector_setup_compare(env);
209b475f80bSPeter Rosin 		wait_for_completion(&env->done);
210b475f80bSPeter Rosin 		if (env->level < 0) {
211b475f80bSPeter Rosin 			ret = env->level;
212b475f80bSPeter Rosin 			goto err_unlock;
213b475f80bSPeter Rosin 		}
214b475f80bSPeter Rosin 		*val = env->invert ? env->dac_max - env->level : env->level;
215b475f80bSPeter Rosin 		mutex_unlock(&env->read_lock);
216b475f80bSPeter Rosin 
217b475f80bSPeter Rosin 		return IIO_VAL_INT;
218b475f80bSPeter Rosin 
219b475f80bSPeter Rosin 	case IIO_CHAN_INFO_SCALE:
220b475f80bSPeter Rosin 		return iio_read_channel_scale(env->dac, val, val2);
221b475f80bSPeter Rosin 	}
222b475f80bSPeter Rosin 
223b475f80bSPeter Rosin 	return -EINVAL;
224b475f80bSPeter Rosin 
225b475f80bSPeter Rosin err_unlock:
226b475f80bSPeter Rosin 	mutex_unlock(&env->read_lock);
227b475f80bSPeter Rosin 	return ret;
228b475f80bSPeter Rosin }
229b475f80bSPeter Rosin 
envelope_show_invert(struct iio_dev * indio_dev,uintptr_t private,struct iio_chan_spec const * ch,char * buf)230b475f80bSPeter Rosin static ssize_t envelope_show_invert(struct iio_dev *indio_dev,
231b475f80bSPeter Rosin 				    uintptr_t private,
232b475f80bSPeter Rosin 				    struct iio_chan_spec const *ch, char *buf)
233b475f80bSPeter Rosin {
234b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
235b475f80bSPeter Rosin 
236b475f80bSPeter Rosin 	return sprintf(buf, "%u\n", env->invert);
237b475f80bSPeter Rosin }
238b475f80bSPeter Rosin 
envelope_store_invert(struct iio_dev * indio_dev,uintptr_t private,struct iio_chan_spec const * ch,const char * buf,size_t len)239b475f80bSPeter Rosin static ssize_t envelope_store_invert(struct iio_dev *indio_dev,
240b475f80bSPeter Rosin 				     uintptr_t private,
241b475f80bSPeter Rosin 				     struct iio_chan_spec const *ch,
242b475f80bSPeter Rosin 				     const char *buf, size_t len)
243b475f80bSPeter Rosin {
244b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
245b475f80bSPeter Rosin 	unsigned long invert;
246b475f80bSPeter Rosin 	int ret;
247b475f80bSPeter Rosin 	u32 trigger;
248b475f80bSPeter Rosin 
249b475f80bSPeter Rosin 	ret = kstrtoul(buf, 0, &invert);
250b475f80bSPeter Rosin 	if (ret < 0)
251b475f80bSPeter Rosin 		return ret;
252b475f80bSPeter Rosin 	if (invert > 1)
253b475f80bSPeter Rosin 		return -EINVAL;
254b475f80bSPeter Rosin 
255b475f80bSPeter Rosin 	trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger;
256b475f80bSPeter Rosin 
257b475f80bSPeter Rosin 	mutex_lock(&env->read_lock);
258b475f80bSPeter Rosin 	if (invert != env->invert)
259b475f80bSPeter Rosin 		ret = irq_set_irq_type(env->comp_irq, trigger);
260b475f80bSPeter Rosin 	if (!ret) {
261b475f80bSPeter Rosin 		env->invert = invert;
262b475f80bSPeter Rosin 		ret = len;
263b475f80bSPeter Rosin 	}
264b475f80bSPeter Rosin 	mutex_unlock(&env->read_lock);
265b475f80bSPeter Rosin 
266b475f80bSPeter Rosin 	return ret;
267b475f80bSPeter Rosin }
268b475f80bSPeter Rosin 
envelope_show_comp_interval(struct iio_dev * indio_dev,uintptr_t private,struct iio_chan_spec const * ch,char * buf)269b475f80bSPeter Rosin static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev,
270b475f80bSPeter Rosin 					   uintptr_t private,
271b475f80bSPeter Rosin 					   struct iio_chan_spec const *ch,
272b475f80bSPeter Rosin 					   char *buf)
273b475f80bSPeter Rosin {
274b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
275b475f80bSPeter Rosin 
276b475f80bSPeter Rosin 	return sprintf(buf, "%u\n", env->comp_interval);
277b475f80bSPeter Rosin }
278b475f80bSPeter Rosin 
envelope_store_comp_interval(struct iio_dev * indio_dev,uintptr_t private,struct iio_chan_spec const * ch,const char * buf,size_t len)279b475f80bSPeter Rosin static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev,
280b475f80bSPeter Rosin 					    uintptr_t private,
281b475f80bSPeter Rosin 					    struct iio_chan_spec const *ch,
282b475f80bSPeter Rosin 					    const char *buf, size_t len)
283b475f80bSPeter Rosin {
284b475f80bSPeter Rosin 	struct envelope *env = iio_priv(indio_dev);
285b475f80bSPeter Rosin 	unsigned long interval;
286b475f80bSPeter Rosin 	int ret;
287b475f80bSPeter Rosin 
288b475f80bSPeter Rosin 	ret = kstrtoul(buf, 0, &interval);
289b475f80bSPeter Rosin 	if (ret < 0)
290b475f80bSPeter Rosin 		return ret;
291b475f80bSPeter Rosin 	if (interval > 1000)
292b475f80bSPeter Rosin 		return -EINVAL;
293b475f80bSPeter Rosin 
294b475f80bSPeter Rosin 	mutex_lock(&env->read_lock);
295b475f80bSPeter Rosin 	env->comp_interval = interval;
296b475f80bSPeter Rosin 	mutex_unlock(&env->read_lock);
297b475f80bSPeter Rosin 
298b475f80bSPeter Rosin 	return len;
299b475f80bSPeter Rosin }
300b475f80bSPeter Rosin 
301b475f80bSPeter Rosin static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = {
302b475f80bSPeter Rosin 	{ .name = "invert",
303b475f80bSPeter Rosin 	  .read = envelope_show_invert,
304b475f80bSPeter Rosin 	  .write = envelope_store_invert, },
305b475f80bSPeter Rosin 	{ .name = "compare_interval",
306b475f80bSPeter Rosin 	  .read = envelope_show_comp_interval,
307b475f80bSPeter Rosin 	  .write = envelope_store_comp_interval, },
308b475f80bSPeter Rosin 	{ /* sentinel */ }
309b475f80bSPeter Rosin };
310b475f80bSPeter Rosin 
311b475f80bSPeter Rosin static const struct iio_chan_spec envelope_detector_iio_channel = {
312b475f80bSPeter Rosin 	.type = IIO_ALTVOLTAGE,
313b475f80bSPeter Rosin 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
314b475f80bSPeter Rosin 			    | BIT(IIO_CHAN_INFO_SCALE),
315b475f80bSPeter Rosin 	.ext_info = envelope_detector_ext_info,
316b475f80bSPeter Rosin 	.indexed = 1,
317b475f80bSPeter Rosin };
318b475f80bSPeter Rosin 
319b475f80bSPeter Rosin static const struct iio_info envelope_detector_info = {
320b475f80bSPeter Rosin 	.read_raw = &envelope_detector_read_raw,
321b475f80bSPeter Rosin };
322b475f80bSPeter Rosin 
envelope_detector_probe(struct platform_device * pdev)323b475f80bSPeter Rosin static int envelope_detector_probe(struct platform_device *pdev)
324b475f80bSPeter Rosin {
325b475f80bSPeter Rosin 	struct device *dev = &pdev->dev;
326b475f80bSPeter Rosin 	struct iio_dev *indio_dev;
327b475f80bSPeter Rosin 	struct envelope *env;
328b475f80bSPeter Rosin 	enum iio_chan_type type;
329b475f80bSPeter Rosin 	int ret;
330b475f80bSPeter Rosin 
331b475f80bSPeter Rosin 	indio_dev = devm_iio_device_alloc(dev, sizeof(*env));
332b475f80bSPeter Rosin 	if (!indio_dev)
333b475f80bSPeter Rosin 		return -ENOMEM;
334b475f80bSPeter Rosin 
335b475f80bSPeter Rosin 	platform_set_drvdata(pdev, indio_dev);
336b475f80bSPeter Rosin 	env = iio_priv(indio_dev);
337b475f80bSPeter Rosin 	env->comp_interval = 50; /* some sensible default? */
338b475f80bSPeter Rosin 
339b475f80bSPeter Rosin 	spin_lock_init(&env->comp_lock);
340b475f80bSPeter Rosin 	mutex_init(&env->read_lock);
341b475f80bSPeter Rosin 	init_completion(&env->done);
342b475f80bSPeter Rosin 	INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout);
343b475f80bSPeter Rosin 
344b475f80bSPeter Rosin 	indio_dev->name = dev_name(dev);
345b475f80bSPeter Rosin 	indio_dev->info = &envelope_detector_info;
346b475f80bSPeter Rosin 	indio_dev->channels = &envelope_detector_iio_channel;
347b475f80bSPeter Rosin 	indio_dev->num_channels = 1;
348b475f80bSPeter Rosin 
349b475f80bSPeter Rosin 	env->dac = devm_iio_channel_get(dev, "dac");
35055dc2952SKrzysztof Kozlowski 	if (IS_ERR(env->dac))
35155dc2952SKrzysztof Kozlowski 		return dev_err_probe(dev, PTR_ERR(env->dac),
35255dc2952SKrzysztof Kozlowski 				     "failed to get dac input channel\n");
353b475f80bSPeter Rosin 
354b475f80bSPeter Rosin 	env->comp_irq = platform_get_irq_byname(pdev, "comp");
3557c279229SStephen Boyd 	if (env->comp_irq < 0)
356b475f80bSPeter Rosin 		return env->comp_irq;
357b475f80bSPeter Rosin 
358b475f80bSPeter Rosin 	ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
359b475f80bSPeter Rosin 			       0, "envelope-detector", env);
36055dc2952SKrzysztof Kozlowski 	if (ret)
36155dc2952SKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to request interrupt\n");
36255dc2952SKrzysztof Kozlowski 
363b475f80bSPeter Rosin 	env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq);
364b475f80bSPeter Rosin 	if (env->comp_irq_trigger & IRQF_TRIGGER_RISING)
365b475f80bSPeter Rosin 		env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING;
366b475f80bSPeter Rosin 	if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING)
367b475f80bSPeter Rosin 		env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING;
368b475f80bSPeter Rosin 	if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH)
369b475f80bSPeter Rosin 		env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW;
370b475f80bSPeter Rosin 	if (env->comp_irq_trigger & IRQF_TRIGGER_LOW)
371b475f80bSPeter Rosin 		env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH;
372b475f80bSPeter Rosin 
373b475f80bSPeter Rosin 	ret = iio_get_channel_type(env->dac, &type);
374b475f80bSPeter Rosin 	if (ret < 0)
375b475f80bSPeter Rosin 		return ret;
376b475f80bSPeter Rosin 
377b475f80bSPeter Rosin 	if (type != IIO_VOLTAGE) {
378b475f80bSPeter Rosin 		dev_err(dev, "dac is of the wrong type\n");
379b475f80bSPeter Rosin 		return -EINVAL;
380b475f80bSPeter Rosin 	}
381b475f80bSPeter Rosin 
382b475f80bSPeter Rosin 	ret = iio_read_max_channel_raw(env->dac, &env->dac_max);
383b475f80bSPeter Rosin 	if (ret < 0) {
384b475f80bSPeter Rosin 		dev_err(dev, "dac does not indicate its raw maximum value\n");
385b475f80bSPeter Rosin 		return ret;
386b475f80bSPeter Rosin 	}
387b475f80bSPeter Rosin 
388b475f80bSPeter Rosin 	return devm_iio_device_register(dev, indio_dev);
389b475f80bSPeter Rosin }
390b475f80bSPeter Rosin 
391b475f80bSPeter Rosin static const struct of_device_id envelope_detector_match[] = {
392b475f80bSPeter Rosin 	{ .compatible = "axentia,tse850-envelope-detector", },
393b475f80bSPeter Rosin 	{ /* sentinel */ }
394b475f80bSPeter Rosin };
395b475f80bSPeter Rosin MODULE_DEVICE_TABLE(of, envelope_detector_match);
396b475f80bSPeter Rosin 
397b475f80bSPeter Rosin static struct platform_driver envelope_detector_driver = {
398b475f80bSPeter Rosin 	.probe = envelope_detector_probe,
399b475f80bSPeter Rosin 	.driver = {
400b475f80bSPeter Rosin 		.name = "iio-envelope-detector",
401b475f80bSPeter Rosin 		.of_match_table = envelope_detector_match,
402b475f80bSPeter Rosin 	},
403b475f80bSPeter Rosin };
404b475f80bSPeter Rosin module_platform_driver(envelope_detector_driver);
405b475f80bSPeter Rosin 
406b475f80bSPeter Rosin MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
407b475f80bSPeter Rosin MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
408b475f80bSPeter Rosin MODULE_LICENSE("GPL v2");
409