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