envelope-detector.c 11 KB

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