162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for an envelope detector using a DAC and a comparator 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Axentia Technologies AB 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Peter Rosin <peda@axentia.se> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * The DAC is used to find the peak level of an alternating voltage input 1262306a36Sopenharmony_ci * signal by a binary search using the output of a comparator wired to 1362306a36Sopenharmony_ci * an interrupt pin. Like so: 1462306a36Sopenharmony_ci * _ 1562306a36Sopenharmony_ci * | \ 1662306a36Sopenharmony_ci * input +------>-------|+ \ 1762306a36Sopenharmony_ci * | \ 1862306a36Sopenharmony_ci * .-------. | }---. 1962306a36Sopenharmony_ci * | | | / | 2062306a36Sopenharmony_ci * | dac|-->--|- / | 2162306a36Sopenharmony_ci * | | |_/ | 2262306a36Sopenharmony_ci * | | | 2362306a36Sopenharmony_ci * | | | 2462306a36Sopenharmony_ci * | irq|------<-------' 2562306a36Sopenharmony_ci * | | 2662306a36Sopenharmony_ci * '-------' 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/completion.h> 3062306a36Sopenharmony_ci#include <linux/device.h> 3162306a36Sopenharmony_ci#include <linux/err.h> 3262306a36Sopenharmony_ci#include <linux/kernel.h> 3362306a36Sopenharmony_ci#include <linux/module.h> 3462306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 3562306a36Sopenharmony_ci#include <linux/mutex.h> 3662306a36Sopenharmony_ci#include <linux/iio/consumer.h> 3762306a36Sopenharmony_ci#include <linux/iio/iio.h> 3862306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 3962306a36Sopenharmony_ci#include <linux/interrupt.h> 4062306a36Sopenharmony_ci#include <linux/irq.h> 4162306a36Sopenharmony_ci#include <linux/platform_device.h> 4262306a36Sopenharmony_ci#include <linux/spinlock.h> 4362306a36Sopenharmony_ci#include <linux/workqueue.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct envelope { 4662306a36Sopenharmony_ci spinlock_t comp_lock; /* protects comp */ 4762306a36Sopenharmony_ci int comp; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci struct mutex read_lock; /* protects everything else */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci int comp_irq; 5262306a36Sopenharmony_ci u32 comp_irq_trigger; 5362306a36Sopenharmony_ci u32 comp_irq_trigger_inv; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci struct iio_channel *dac; 5662306a36Sopenharmony_ci struct delayed_work comp_timeout; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci unsigned int comp_interval; 5962306a36Sopenharmony_ci bool invert; 6062306a36Sopenharmony_ci u32 dac_max; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci int high; 6362306a36Sopenharmony_ci int level; 6462306a36Sopenharmony_ci int low; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci struct completion done; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * The envelope_detector_comp_latch function works together with the compare 7162306a36Sopenharmony_ci * interrupt service routine below (envelope_detector_comp_isr) as a latch 7262306a36Sopenharmony_ci * (one-bit memory) for if the interrupt has triggered since last calling 7362306a36Sopenharmony_ci * this function. 7462306a36Sopenharmony_ci * The ..._comp_isr function disables the interrupt so that the cpu does not 7562306a36Sopenharmony_ci * need to service a possible interrupt flood from the comparator when no-one 7662306a36Sopenharmony_ci * cares anyway, and this ..._comp_latch function reenables them again if 7762306a36Sopenharmony_ci * needed. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic int envelope_detector_comp_latch(struct envelope *env) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci int comp; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci spin_lock_irq(&env->comp_lock); 8462306a36Sopenharmony_ci comp = env->comp; 8562306a36Sopenharmony_ci env->comp = 0; 8662306a36Sopenharmony_ci spin_unlock_irq(&env->comp_lock); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (!comp) 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * The irq was disabled, and is reenabled just now. 9362306a36Sopenharmony_ci * But there might have been a pending irq that 9462306a36Sopenharmony_ci * happened while the irq was disabled that fires 9562306a36Sopenharmony_ci * just as the irq is reenabled. That is not what 9662306a36Sopenharmony_ci * is desired. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci enable_irq(env->comp_irq); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* So, synchronize this possibly pending irq... */ 10162306a36Sopenharmony_ci synchronize_irq(env->comp_irq); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* ...and redo the whole dance. */ 10462306a36Sopenharmony_ci spin_lock_irq(&env->comp_lock); 10562306a36Sopenharmony_ci comp = env->comp; 10662306a36Sopenharmony_ci env->comp = 0; 10762306a36Sopenharmony_ci spin_unlock_irq(&env->comp_lock); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (comp) 11062306a36Sopenharmony_ci enable_irq(env->comp_irq); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 1; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic irqreturn_t envelope_detector_comp_isr(int irq, void *ctx) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct envelope *env = ctx; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci spin_lock(&env->comp_lock); 12062306a36Sopenharmony_ci env->comp = 1; 12162306a36Sopenharmony_ci disable_irq_nosync(env->comp_irq); 12262306a36Sopenharmony_ci spin_unlock(&env->comp_lock); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return IRQ_HANDLED; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void envelope_detector_setup_compare(struct envelope *env) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* 13262306a36Sopenharmony_ci * Do a binary search for the peak input level, and stop 13362306a36Sopenharmony_ci * when that level is "trapped" between two adjacent DAC 13462306a36Sopenharmony_ci * values. 13562306a36Sopenharmony_ci * When invert is active, use the midpoint floor so that 13662306a36Sopenharmony_ci * env->level ends up as env->low when the termination 13762306a36Sopenharmony_ci * criteria below is fulfilled, and use the midpoint 13862306a36Sopenharmony_ci * ceiling when invert is not active so that env->level 13962306a36Sopenharmony_ci * ends up as env->high in that case. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci env->level = (env->high + env->low + !env->invert) / 2; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (env->high == env->low + 1) { 14462306a36Sopenharmony_ci complete(&env->done); 14562306a36Sopenharmony_ci return; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Set a "safe" DAC level (if there is such a thing)... */ 14962306a36Sopenharmony_ci ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max); 15062306a36Sopenharmony_ci if (ret < 0) 15162306a36Sopenharmony_ci goto err; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* ...clear the comparison result... */ 15462306a36Sopenharmony_ci envelope_detector_comp_latch(env); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* ...set the real DAC level... */ 15762306a36Sopenharmony_ci ret = iio_write_channel_raw(env->dac, env->level); 15862306a36Sopenharmony_ci if (ret < 0) 15962306a36Sopenharmony_ci goto err; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* ...and wait for a bit to see if the latch catches anything. */ 16262306a36Sopenharmony_ci schedule_delayed_work(&env->comp_timeout, 16362306a36Sopenharmony_ci msecs_to_jiffies(env->comp_interval)); 16462306a36Sopenharmony_ci return; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cierr: 16762306a36Sopenharmony_ci env->level = ret; 16862306a36Sopenharmony_ci complete(&env->done); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void envelope_detector_timeout(struct work_struct *work) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct envelope *env = container_of(work, struct envelope, 17462306a36Sopenharmony_ci comp_timeout.work); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Adjust low/high depending on the latch content... */ 17762306a36Sopenharmony_ci if (!envelope_detector_comp_latch(env) ^ !env->invert) 17862306a36Sopenharmony_ci env->low = env->level; 17962306a36Sopenharmony_ci else 18062306a36Sopenharmony_ci env->high = env->level; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* ...and continue the search. */ 18362306a36Sopenharmony_ci envelope_detector_setup_compare(env); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int envelope_detector_read_raw(struct iio_dev *indio_dev, 18762306a36Sopenharmony_ci struct iio_chan_spec const *chan, 18862306a36Sopenharmony_ci int *val, int *val2, long mask) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct envelope *env = iio_priv(indio_dev); 19162306a36Sopenharmony_ci int ret; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci switch (mask) { 19462306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * When invert is active, start with high=max+1 and low=0 19762306a36Sopenharmony_ci * since we will end up with the low value when the 19862306a36Sopenharmony_ci * termination criteria is fulfilled (rounding down). And 19962306a36Sopenharmony_ci * start with high=max and low=-1 when invert is not active 20062306a36Sopenharmony_ci * since we will end up with the high value in that case. 20162306a36Sopenharmony_ci * This ensures that the returned value in both cases are 20262306a36Sopenharmony_ci * in the same range as the DAC and is a value that has not 20362306a36Sopenharmony_ci * triggered the comparator. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci mutex_lock(&env->read_lock); 20662306a36Sopenharmony_ci env->high = env->dac_max + env->invert; 20762306a36Sopenharmony_ci env->low = -1 + env->invert; 20862306a36Sopenharmony_ci envelope_detector_setup_compare(env); 20962306a36Sopenharmony_ci wait_for_completion(&env->done); 21062306a36Sopenharmony_ci if (env->level < 0) { 21162306a36Sopenharmony_ci ret = env->level; 21262306a36Sopenharmony_ci goto err_unlock; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci *val = env->invert ? env->dac_max - env->level : env->level; 21562306a36Sopenharmony_ci mutex_unlock(&env->read_lock); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return IIO_VAL_INT; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 22062306a36Sopenharmony_ci return iio_read_channel_scale(env->dac, val, val2); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return -EINVAL; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cierr_unlock: 22662306a36Sopenharmony_ci mutex_unlock(&env->read_lock); 22762306a36Sopenharmony_ci return ret; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic ssize_t envelope_show_invert(struct iio_dev *indio_dev, 23162306a36Sopenharmony_ci uintptr_t private, 23262306a36Sopenharmony_ci struct iio_chan_spec const *ch, char *buf) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct envelope *env = iio_priv(indio_dev); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return sprintf(buf, "%u\n", env->invert); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic ssize_t envelope_store_invert(struct iio_dev *indio_dev, 24062306a36Sopenharmony_ci uintptr_t private, 24162306a36Sopenharmony_ci struct iio_chan_spec const *ch, 24262306a36Sopenharmony_ci const char *buf, size_t len) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct envelope *env = iio_priv(indio_dev); 24562306a36Sopenharmony_ci unsigned long invert; 24662306a36Sopenharmony_ci int ret; 24762306a36Sopenharmony_ci u32 trigger; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &invert); 25062306a36Sopenharmony_ci if (ret < 0) 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci if (invert > 1) 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci mutex_lock(&env->read_lock); 25862306a36Sopenharmony_ci if (invert != env->invert) 25962306a36Sopenharmony_ci ret = irq_set_irq_type(env->comp_irq, trigger); 26062306a36Sopenharmony_ci if (!ret) { 26162306a36Sopenharmony_ci env->invert = invert; 26262306a36Sopenharmony_ci ret = len; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci mutex_unlock(&env->read_lock); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return ret; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev, 27062306a36Sopenharmony_ci uintptr_t private, 27162306a36Sopenharmony_ci struct iio_chan_spec const *ch, 27262306a36Sopenharmony_ci char *buf) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct envelope *env = iio_priv(indio_dev); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return sprintf(buf, "%u\n", env->comp_interval); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev, 28062306a36Sopenharmony_ci uintptr_t private, 28162306a36Sopenharmony_ci struct iio_chan_spec const *ch, 28262306a36Sopenharmony_ci const char *buf, size_t len) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct envelope *env = iio_priv(indio_dev); 28562306a36Sopenharmony_ci unsigned long interval; 28662306a36Sopenharmony_ci int ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = kstrtoul(buf, 0, &interval); 28962306a36Sopenharmony_ci if (ret < 0) 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci if (interval > 1000) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci mutex_lock(&env->read_lock); 29562306a36Sopenharmony_ci env->comp_interval = interval; 29662306a36Sopenharmony_ci mutex_unlock(&env->read_lock); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return len; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = { 30262306a36Sopenharmony_ci { .name = "invert", 30362306a36Sopenharmony_ci .read = envelope_show_invert, 30462306a36Sopenharmony_ci .write = envelope_store_invert, }, 30562306a36Sopenharmony_ci { .name = "compare_interval", 30662306a36Sopenharmony_ci .read = envelope_show_comp_interval, 30762306a36Sopenharmony_ci .write = envelope_store_comp_interval, }, 30862306a36Sopenharmony_ci { /* sentinel */ } 30962306a36Sopenharmony_ci}; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic const struct iio_chan_spec envelope_detector_iio_channel = { 31262306a36Sopenharmony_ci .type = IIO_ALTVOLTAGE, 31362306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) 31462306a36Sopenharmony_ci | BIT(IIO_CHAN_INFO_SCALE), 31562306a36Sopenharmony_ci .ext_info = envelope_detector_ext_info, 31662306a36Sopenharmony_ci .indexed = 1, 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic const struct iio_info envelope_detector_info = { 32062306a36Sopenharmony_ci .read_raw = &envelope_detector_read_raw, 32162306a36Sopenharmony_ci}; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int envelope_detector_probe(struct platform_device *pdev) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 32662306a36Sopenharmony_ci struct iio_dev *indio_dev; 32762306a36Sopenharmony_ci struct envelope *env; 32862306a36Sopenharmony_ci enum iio_chan_type type; 32962306a36Sopenharmony_ci int ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*env)); 33262306a36Sopenharmony_ci if (!indio_dev) 33362306a36Sopenharmony_ci return -ENOMEM; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci platform_set_drvdata(pdev, indio_dev); 33662306a36Sopenharmony_ci env = iio_priv(indio_dev); 33762306a36Sopenharmony_ci env->comp_interval = 50; /* some sensible default? */ 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci spin_lock_init(&env->comp_lock); 34062306a36Sopenharmony_ci mutex_init(&env->read_lock); 34162306a36Sopenharmony_ci init_completion(&env->done); 34262306a36Sopenharmony_ci INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci indio_dev->name = dev_name(dev); 34562306a36Sopenharmony_ci indio_dev->info = &envelope_detector_info; 34662306a36Sopenharmony_ci indio_dev->channels = &envelope_detector_iio_channel; 34762306a36Sopenharmony_ci indio_dev->num_channels = 1; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci env->dac = devm_iio_channel_get(dev, "dac"); 35062306a36Sopenharmony_ci if (IS_ERR(env->dac)) 35162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(env->dac), 35262306a36Sopenharmony_ci "failed to get dac input channel\n"); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci env->comp_irq = platform_get_irq_byname(pdev, "comp"); 35562306a36Sopenharmony_ci if (env->comp_irq < 0) 35662306a36Sopenharmony_ci return env->comp_irq; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr, 35962306a36Sopenharmony_ci 0, "envelope-detector", env); 36062306a36Sopenharmony_ci if (ret) 36162306a36Sopenharmony_ci return dev_err_probe(dev, ret, "failed to request interrupt\n"); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq); 36462306a36Sopenharmony_ci if (env->comp_irq_trigger & IRQF_TRIGGER_RISING) 36562306a36Sopenharmony_ci env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING; 36662306a36Sopenharmony_ci if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING) 36762306a36Sopenharmony_ci env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING; 36862306a36Sopenharmony_ci if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH) 36962306a36Sopenharmony_ci env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW; 37062306a36Sopenharmony_ci if (env->comp_irq_trigger & IRQF_TRIGGER_LOW) 37162306a36Sopenharmony_ci env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci ret = iio_get_channel_type(env->dac, &type); 37462306a36Sopenharmony_ci if (ret < 0) 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (type != IIO_VOLTAGE) { 37862306a36Sopenharmony_ci dev_err(dev, "dac is of the wrong type\n"); 37962306a36Sopenharmony_ci return -EINVAL; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ret = iio_read_max_channel_raw(env->dac, &env->dac_max); 38362306a36Sopenharmony_ci if (ret < 0) { 38462306a36Sopenharmony_ci dev_err(dev, "dac does not indicate its raw maximum value\n"); 38562306a36Sopenharmony_ci return ret; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return devm_iio_device_register(dev, indio_dev); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic const struct of_device_id envelope_detector_match[] = { 39262306a36Sopenharmony_ci { .compatible = "axentia,tse850-envelope-detector", }, 39362306a36Sopenharmony_ci { /* sentinel */ } 39462306a36Sopenharmony_ci}; 39562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, envelope_detector_match); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic struct platform_driver envelope_detector_driver = { 39862306a36Sopenharmony_ci .probe = envelope_detector_probe, 39962306a36Sopenharmony_ci .driver = { 40062306a36Sopenharmony_ci .name = "iio-envelope-detector", 40162306a36Sopenharmony_ci .of_match_table = envelope_detector_match, 40262306a36Sopenharmony_ci }, 40362306a36Sopenharmony_ci}; 40462306a36Sopenharmony_cimodule_platform_driver(envelope_detector_driver); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ciMODULE_DESCRIPTION("Envelope detector using a DAC and a comparator"); 40762306a36Sopenharmony_ciMODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 40862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 409