File Index
Files and directories
Sign in
FILE

srf04.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/proximity/srf04.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * SRF04: ultrasonic sensor for distance measuring by using GPIOs
  4 *
  5 * Copyright (c) 2017 Andreas Klinger <ak@it-klinger.de>
  6 *
  7 * For details about the device see:
  8 * https://www.robot-electronics.co.uk/htm/srf04tech.htm
  9 *
 10 * the measurement cycle as timing diagram looks like:
 11 *
 12 *          +---+
 13 * GPIO     |   |
 14 * trig:  --+   +------------------------------------------------------
 15 *          ^   ^
 16 *          |<->|
 17 *         udelay(trigger_pulse_us)
 18 *
 19 * ultra           +-+ +-+ +-+
 20 * sonic           | | | | | |
 21 * burst: ---------+ +-+ +-+ +-----------------------------------------
 22 *                           .
 23 * ultra                     .              +-+ +-+ +-+
 24 * sonic                     .              | | | | | |
 25 * echo:  ----------------------------------+ +-+ +-+ +----------------
 26 *                           .                        .
 27 *                           +------------------------+
 28 * GPIO                      |                        |
 29 * echo:  -------------------+                        +---------------
 30 *                           ^                        ^
 31 *                           interrupt                interrupt
 32 *                           (ts_rising)              (ts_falling)
 33 *                           |<---------------------->|
 34 *                              pulse time measured
 35 *                              --> one round trip of ultra sonic waves
 36 */
 37#include <linux/err.h>
 38#include <linux/gpio/consumer.h>
 39#include <linux/kernel.h>
 40#include <linux/mod_devicetable.h>
 41#include <linux/module.h>
 42#include <linux/platform_device.h>
 43#include <linux/property.h>
 44#include <linux/sched.h>
 45#include <linux/interrupt.h>
 46#include <linux/delay.h>
 47#include <linux/pm_runtime.h>
 48#include <linux/iio/iio.h>
 49#include <linux/iio/sysfs.h>
 50
 51struct srf04_cfg {
 52	unsigned long trigger_pulse_us;
 53};
 54
 55struct srf04_data {
 56	struct device		*dev;
 57	struct gpio_desc	*gpiod_trig;
 58	struct gpio_desc	*gpiod_echo;
 59	struct gpio_desc	*gpiod_power;
 60	struct mutex		lock;
 61	int			irqnr;
 62	ktime_t			ts_rising;
 63	ktime_t			ts_falling;
 64	struct completion	rising;
 65	struct completion	falling;
 66	const struct srf04_cfg	*cfg;
 67	int			startup_time_ms;
 68};
 69
 70static const struct srf04_cfg srf04_cfg = {
 71	.trigger_pulse_us = 10,
 72};
 73
 74static const struct srf04_cfg mb_lv_cfg = {
 75	.trigger_pulse_us = 20,
 76};
 77
 78static irqreturn_t srf04_handle_irq(int irq, void *dev_id)
 79{
 80	struct iio_dev *indio_dev = dev_id;
 81	struct srf04_data *data = iio_priv(indio_dev);
 82	ktime_t now = ktime_get();
 83
 84	if (gpiod_get_value(data->gpiod_echo)) {
 85		data->ts_rising = now;
 86		complete(&data->rising);
 87	} else {
 88		data->ts_falling = now;
 89		complete(&data->falling);
 90	}
 91
 92	return IRQ_HANDLED;
 93}
 94
 95static int srf04_read(struct srf04_data *data)
 96{
 97	int ret;
 98	ktime_t ktime_dt;
 99	u64 dt_ns;
100	u32 time_ns, distance_mm;
101
102	if (data->gpiod_power) {
103		ret = pm_runtime_resume_and_get(data->dev);
104		if (ret < 0)
105			return ret;
106	}
107	/*
108	 * just one read-echo-cycle can take place at a time
109	 * ==> lock against concurrent reading calls
110	 */
111	mutex_lock(&data->lock);
112
113	reinit_completion(&data->rising);
114	reinit_completion(&data->falling);
115
116	gpiod_set_value(data->gpiod_trig, 1);
117	udelay(data->cfg->trigger_pulse_us);
118	gpiod_set_value(data->gpiod_trig, 0);
119
120	if (data->gpiod_power)
121		pm_runtime_put_autosuspend(data->dev);
122
123	/* it should not take more than 20 ms until echo is rising */
124	ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
125	if (ret < 0) {
126		mutex_unlock(&data->lock);
127		return ret;
128	} else if (ret == 0) {
129		mutex_unlock(&data->lock);
130		return -ETIMEDOUT;
131	}
132
133	/* it cannot take more than 50 ms until echo is falling */
134	ret = wait_for_completion_killable_timeout(&data->falling, HZ/20);
135	if (ret < 0) {
136		mutex_unlock(&data->lock);
137		return ret;
138	} else if (ret == 0) {
139		mutex_unlock(&data->lock);
140		return -ETIMEDOUT;
141	}
142
143	ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);
144
145	mutex_unlock(&data->lock);
146
147	dt_ns = ktime_to_ns(ktime_dt);
148	/*
149	 * measuring more than 6,45 meters is beyond the capabilities of
150	 * the supported sensors
151	 * ==> filter out invalid results for not measuring echos of
152	 *     another us sensor
153	 *
154	 * formula:
155	 *         distance     6,45 * 2 m
156	 * time = ---------- = ------------ = 40438871 ns
157	 *          speed         319 m/s
158	 *
159	 * using a minimum speed at -20 °C of 319 m/s
160	 */
161	if (dt_ns > 40438871)
162		return -EIO;
163
164	time_ns = dt_ns;
165
166	/*
167	 * the speed as function of the temperature is approximately:
168	 *
169	 * speed = 331,5 + 0,6 * Temp
170	 *   with Temp in °C
171	 *   and speed in m/s
172	 *
173	 * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the
174	 * temperature
175	 *
176	 * therefore:
177	 *             time     343,5     time * 106
178	 * distance = ------ * ------- = ------------
179	 *             10^6         2         617176
180	 *   with time in ns
181	 *   and distance in mm (one way)
182	 *
183	 * because we limit to 6,45 meters the multiplication with 106 just
184	 * fits into 32 bit
185	 */
186	distance_mm = time_ns * 106 / 617176;
187
188	return distance_mm;
189}
190
191static int srf04_read_raw(struct iio_dev *indio_dev,
192			    struct iio_chan_spec const *channel, int *val,
193			    int *val2, long info)
194{
195	struct srf04_data *data = iio_priv(indio_dev);
196	int ret;
197
198	if (channel->type != IIO_DISTANCE)
199		return -EINVAL;
200
201	switch (info) {
202	case IIO_CHAN_INFO_RAW:
203		ret = srf04_read(data);
204		if (ret < 0)
205			return ret;
206		*val = ret;
207		return IIO_VAL_INT;
208	case IIO_CHAN_INFO_SCALE:
209		/*
210		 * theoretical maximum resolution is 3 mm
211		 * 1 LSB is 1 mm
212		 */
213		*val = 0;
214		*val2 = 1000;
215		return IIO_VAL_INT_PLUS_MICRO;
216	default:
217		return -EINVAL;
218	}
219}
220
221static const struct iio_info srf04_iio_info = {
222	.read_raw		= srf04_read_raw,
223};
224
225static const struct iio_chan_spec srf04_chan_spec[] = {
226	{
227		.type = IIO_DISTANCE,
228		.info_mask_separate =
229				BIT(IIO_CHAN_INFO_RAW) |
230				BIT(IIO_CHAN_INFO_SCALE),
231	},
232};
233
234static const struct of_device_id of_srf04_match[] = {
235	{ .compatible = "devantech,srf04", .data = &srf04_cfg },
236	{ .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg },
237	{ .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg },
238	{ .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg },
239	{ .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg },
240	{ .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg },
241	{ }
242};
243
244MODULE_DEVICE_TABLE(of, of_srf04_match);
245
246static int srf04_probe(struct platform_device *pdev)
247{
248	struct device *dev = &pdev->dev;
249	struct srf04_data *data;
250	struct iio_dev *indio_dev;
251	int ret;
252
253	indio_dev = devm_iio_device_alloc(dev, sizeof(struct srf04_data));
254	if (!indio_dev)
255		return -ENOMEM;
256
257	data = iio_priv(indio_dev);
258	data->dev = dev;
259	data->cfg = device_get_match_data(dev);
260
261	mutex_init(&data->lock);
262	init_completion(&data->rising);
263	init_completion(&data->falling);
264
265	data->gpiod_trig = devm_gpiod_get(dev, "trig", GPIOD_OUT_LOW);
266	if (IS_ERR(data->gpiod_trig)) {
267		dev_err(dev, "failed to get trig-gpios: err=%ld\n",
268					PTR_ERR(data->gpiod_trig));
269		return PTR_ERR(data->gpiod_trig);
270	}
271
272	data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN);
273	if (IS_ERR(data->gpiod_echo)) {
274		dev_err(dev, "failed to get echo-gpios: err=%ld\n",
275					PTR_ERR(data->gpiod_echo));
276		return PTR_ERR(data->gpiod_echo);
277	}
278
279	data->gpiod_power = devm_gpiod_get_optional(dev, "power",
280								GPIOD_OUT_LOW);
281	if (IS_ERR(data->gpiod_power)) {
282		dev_err(dev, "failed to get power-gpios: err=%ld\n",
283						PTR_ERR(data->gpiod_power));
284		return PTR_ERR(data->gpiod_power);
285	}
286	if (data->gpiod_power) {
287		data->startup_time_ms = 100;
288		device_property_read_u32(dev, "startup-time-ms", &data->startup_time_ms);
289		dev_dbg(dev, "using power gpio: startup-time-ms=%d\n",
290							data->startup_time_ms);
291	}
292
293	if (gpiod_cansleep(data->gpiod_echo)) {
294		dev_err(data->dev, "cansleep-GPIOs not supported\n");
295		return -ENODEV;
296	}
297
298	data->irqnr = gpiod_to_irq(data->gpiod_echo);
299	if (data->irqnr < 0) {
300		dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr);
301		return data->irqnr;
302	}
303
304	ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq,
305			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
306			pdev->name, indio_dev);
307	if (ret < 0) {
308		dev_err(data->dev, "request_irq: %d\n", ret);
309		return ret;
310	}
311
312	platform_set_drvdata(pdev, indio_dev);
313
314	indio_dev->name = "srf04";
315	indio_dev->info = &srf04_iio_info;
316	indio_dev->modes = INDIO_DIRECT_MODE;
317	indio_dev->channels = srf04_chan_spec;
318	indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec);
319
320	ret = iio_device_register(indio_dev);
321	if (ret < 0) {
322		dev_err(data->dev, "iio_device_register: %d\n", ret);
323		return ret;
324	}
325
326	if (data->gpiod_power) {
327		pm_runtime_set_autosuspend_delay(data->dev, 1000);
328		pm_runtime_use_autosuspend(data->dev);
329
330		ret = pm_runtime_set_active(data->dev);
331		if (ret) {
332			dev_err(data->dev, "pm_runtime_set_active: %d\n", ret);
333			iio_device_unregister(indio_dev);
334		}
335
336		pm_runtime_enable(data->dev);
337		pm_runtime_idle(data->dev);
338	}
339
340	return ret;
341}
342
343static void srf04_remove(struct platform_device *pdev)
344{
345	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
346	struct srf04_data *data = iio_priv(indio_dev);
347
348	iio_device_unregister(indio_dev);
349
350	if (data->gpiod_power) {
351		pm_runtime_disable(data->dev);
352		pm_runtime_set_suspended(data->dev);
353	}
354}
355
356static int  srf04_pm_runtime_suspend(struct device *dev)
357{
358	struct platform_device *pdev = container_of(dev,
359						struct platform_device, dev);
360	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
361	struct srf04_data *data = iio_priv(indio_dev);
362
363	gpiod_set_value(data->gpiod_power, 0);
364
365	return 0;
366}
367
368static int srf04_pm_runtime_resume(struct device *dev)
369{
370	struct platform_device *pdev = container_of(dev,
371						struct platform_device, dev);
372	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
373	struct srf04_data *data = iio_priv(indio_dev);
374
375	gpiod_set_value(data->gpiod_power, 1);
376	msleep(data->startup_time_ms);
377
378	return 0;
379}
380
381static const struct dev_pm_ops srf04_pm_ops = {
382	RUNTIME_PM_OPS(srf04_pm_runtime_suspend,
383		       srf04_pm_runtime_resume, NULL)
384};
385
386static struct platform_driver srf04_driver = {
387	.probe		= srf04_probe,
388	.remove		= srf04_remove,
389	.driver		= {
390		.name		= "srf04-gpio",
391		.of_match_table	= of_srf04_match,
392		.pm		= pm_ptr(&srf04_pm_ops),
393	},
394};
395
396module_platform_driver(srf04_driver);
397
398MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
399MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs");
400MODULE_LICENSE("GPL");
401MODULE_ALIAS("platform:srf04");
~/ Read-only file preview.