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 | |
51 | struct srf04_cfg { |
52 | unsigned long trigger_pulse_us; |
53 | }; |
54 | |
55 | struct 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 | |
70 | static const struct srf04_cfg srf04_cfg = { |
71 | .trigger_pulse_us = 10, |
72 | }; |
73 | |
74 | static const struct srf04_cfg mb_lv_cfg = { |
75 | .trigger_pulse_us = 20, |
76 | }; |
77 | |
78 | static 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(desc: 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 | |
95 | static 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(dev: 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(x: &data->rising); |
114 | reinit_completion(x: &data->falling); |
115 | |
116 | gpiod_set_value(desc: data->gpiod_trig, value: 1); |
117 | udelay(data->cfg->trigger_pulse_us); |
118 | gpiod_set_value(desc: data->gpiod_trig, value: 0); |
119 | |
120 | if (data->gpiod_power) { |
121 | pm_runtime_mark_last_busy(dev: data->dev); |
122 | pm_runtime_put_autosuspend(dev: data->dev); |
123 | } |
124 | |
125 | /* it should not take more than 20 ms until echo is rising */ |
126 | ret = wait_for_completion_killable_timeout(x: &data->rising, HZ/50); |
127 | if (ret < 0) { |
128 | mutex_unlock(lock: &data->lock); |
129 | return ret; |
130 | } else if (ret == 0) { |
131 | mutex_unlock(lock: &data->lock); |
132 | return -ETIMEDOUT; |
133 | } |
134 | |
135 | /* it cannot take more than 50 ms until echo is falling */ |
136 | ret = wait_for_completion_killable_timeout(x: &data->falling, HZ/20); |
137 | if (ret < 0) { |
138 | mutex_unlock(lock: &data->lock); |
139 | return ret; |
140 | } else if (ret == 0) { |
141 | mutex_unlock(lock: &data->lock); |
142 | return -ETIMEDOUT; |
143 | } |
144 | |
145 | ktime_dt = ktime_sub(data->ts_falling, data->ts_rising); |
146 | |
147 | mutex_unlock(lock: &data->lock); |
148 | |
149 | dt_ns = ktime_to_ns(kt: ktime_dt); |
150 | /* |
151 | * measuring more than 6,45 meters is beyond the capabilities of |
152 | * the supported sensors |
153 | * ==> filter out invalid results for not measuring echos of |
154 | * another us sensor |
155 | * |
156 | * formula: |
157 | * distance 6,45 * 2 m |
158 | * time = ---------- = ------------ = 40438871 ns |
159 | * speed 319 m/s |
160 | * |
161 | * using a minimum speed at -20 °C of 319 m/s |
162 | */ |
163 | if (dt_ns > 40438871) |
164 | return -EIO; |
165 | |
166 | time_ns = dt_ns; |
167 | |
168 | /* |
169 | * the speed as function of the temperature is approximately: |
170 | * |
171 | * speed = 331,5 + 0,6 * Temp |
172 | * with Temp in °C |
173 | * and speed in m/s |
174 | * |
175 | * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the |
176 | * temperature |
177 | * |
178 | * therefore: |
179 | * time 343,5 time * 106 |
180 | * distance = ------ * ------- = ------------ |
181 | * 10^6 2 617176 |
182 | * with time in ns |
183 | * and distance in mm (one way) |
184 | * |
185 | * because we limit to 6,45 meters the multiplication with 106 just |
186 | * fits into 32 bit |
187 | */ |
188 | distance_mm = time_ns * 106 / 617176; |
189 | |
190 | return distance_mm; |
191 | } |
192 | |
193 | static int srf04_read_raw(struct iio_dev *indio_dev, |
194 | struct iio_chan_spec const *channel, int *val, |
195 | int *val2, long info) |
196 | { |
197 | struct srf04_data *data = iio_priv(indio_dev); |
198 | int ret; |
199 | |
200 | if (channel->type != IIO_DISTANCE) |
201 | return -EINVAL; |
202 | |
203 | switch (info) { |
204 | case IIO_CHAN_INFO_RAW: |
205 | ret = srf04_read(data); |
206 | if (ret < 0) |
207 | return ret; |
208 | *val = ret; |
209 | return IIO_VAL_INT; |
210 | case IIO_CHAN_INFO_SCALE: |
211 | /* |
212 | * theoretical maximum resolution is 3 mm |
213 | * 1 LSB is 1 mm |
214 | */ |
215 | *val = 0; |
216 | *val2 = 1000; |
217 | return IIO_VAL_INT_PLUS_MICRO; |
218 | default: |
219 | return -EINVAL; |
220 | } |
221 | } |
222 | |
223 | static const struct iio_info srf04_iio_info = { |
224 | .read_raw = srf04_read_raw, |
225 | }; |
226 | |
227 | static const struct iio_chan_spec srf04_chan_spec[] = { |
228 | { |
229 | .type = IIO_DISTANCE, |
230 | .info_mask_separate = |
231 | BIT(IIO_CHAN_INFO_RAW) | |
232 | BIT(IIO_CHAN_INFO_SCALE), |
233 | }, |
234 | }; |
235 | |
236 | static const struct of_device_id of_srf04_match[] = { |
237 | { .compatible = "devantech,srf04" , .data = &srf04_cfg }, |
238 | { .compatible = "maxbotix,mb1000" , .data = &mb_lv_cfg }, |
239 | { .compatible = "maxbotix,mb1010" , .data = &mb_lv_cfg }, |
240 | { .compatible = "maxbotix,mb1020" , .data = &mb_lv_cfg }, |
241 | { .compatible = "maxbotix,mb1030" , .data = &mb_lv_cfg }, |
242 | { .compatible = "maxbotix,mb1040" , .data = &mb_lv_cfg }, |
243 | {}, |
244 | }; |
245 | |
246 | MODULE_DEVICE_TABLE(of, of_srf04_match); |
247 | |
248 | static int srf04_probe(struct platform_device *pdev) |
249 | { |
250 | struct device *dev = &pdev->dev; |
251 | struct srf04_data *data; |
252 | struct iio_dev *indio_dev; |
253 | int ret; |
254 | |
255 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(struct srf04_data)); |
256 | if (!indio_dev) { |
257 | dev_err(dev, "failed to allocate IIO device\n" ); |
258 | return -ENOMEM; |
259 | } |
260 | |
261 | data = iio_priv(indio_dev); |
262 | data->dev = dev; |
263 | data->cfg = device_get_match_data(dev); |
264 | |
265 | mutex_init(&data->lock); |
266 | init_completion(x: &data->rising); |
267 | init_completion(x: &data->falling); |
268 | |
269 | data->gpiod_trig = devm_gpiod_get(dev, con_id: "trig" , flags: GPIOD_OUT_LOW); |
270 | if (IS_ERR(ptr: data->gpiod_trig)) { |
271 | dev_err(dev, "failed to get trig-gpios: err=%ld\n" , |
272 | PTR_ERR(data->gpiod_trig)); |
273 | return PTR_ERR(ptr: data->gpiod_trig); |
274 | } |
275 | |
276 | data->gpiod_echo = devm_gpiod_get(dev, con_id: "echo" , flags: GPIOD_IN); |
277 | if (IS_ERR(ptr: data->gpiod_echo)) { |
278 | dev_err(dev, "failed to get echo-gpios: err=%ld\n" , |
279 | PTR_ERR(data->gpiod_echo)); |
280 | return PTR_ERR(ptr: data->gpiod_echo); |
281 | } |
282 | |
283 | data->gpiod_power = devm_gpiod_get_optional(dev, con_id: "power" , |
284 | flags: GPIOD_OUT_LOW); |
285 | if (IS_ERR(ptr: data->gpiod_power)) { |
286 | dev_err(dev, "failed to get power-gpios: err=%ld\n" , |
287 | PTR_ERR(data->gpiod_power)); |
288 | return PTR_ERR(ptr: data->gpiod_power); |
289 | } |
290 | if (data->gpiod_power) { |
291 | data->startup_time_ms = 100; |
292 | device_property_read_u32(dev, propname: "startup-time-ms" , val: &data->startup_time_ms); |
293 | dev_dbg(dev, "using power gpio: startup-time-ms=%d\n" , |
294 | data->startup_time_ms); |
295 | } |
296 | |
297 | if (gpiod_cansleep(desc: data->gpiod_echo)) { |
298 | dev_err(data->dev, "cansleep-GPIOs not supported\n" ); |
299 | return -ENODEV; |
300 | } |
301 | |
302 | data->irqnr = gpiod_to_irq(desc: data->gpiod_echo); |
303 | if (data->irqnr < 0) { |
304 | dev_err(data->dev, "gpiod_to_irq: %d\n" , data->irqnr); |
305 | return data->irqnr; |
306 | } |
307 | |
308 | ret = devm_request_irq(dev, irq: data->irqnr, handler: srf04_handle_irq, |
309 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, |
310 | devname: pdev->name, dev_id: indio_dev); |
311 | if (ret < 0) { |
312 | dev_err(data->dev, "request_irq: %d\n" , ret); |
313 | return ret; |
314 | } |
315 | |
316 | platform_set_drvdata(pdev, data: indio_dev); |
317 | |
318 | indio_dev->name = "srf04" ; |
319 | indio_dev->info = &srf04_iio_info; |
320 | indio_dev->modes = INDIO_DIRECT_MODE; |
321 | indio_dev->channels = srf04_chan_spec; |
322 | indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec); |
323 | |
324 | ret = iio_device_register(indio_dev); |
325 | if (ret < 0) { |
326 | dev_err(data->dev, "iio_device_register: %d\n" , ret); |
327 | return ret; |
328 | } |
329 | |
330 | if (data->gpiod_power) { |
331 | pm_runtime_set_autosuspend_delay(dev: data->dev, delay: 1000); |
332 | pm_runtime_use_autosuspend(dev: data->dev); |
333 | |
334 | ret = pm_runtime_set_active(dev: data->dev); |
335 | if (ret) { |
336 | dev_err(data->dev, "pm_runtime_set_active: %d\n" , ret); |
337 | iio_device_unregister(indio_dev); |
338 | } |
339 | |
340 | pm_runtime_enable(dev: data->dev); |
341 | pm_runtime_idle(dev: data->dev); |
342 | } |
343 | |
344 | return ret; |
345 | } |
346 | |
347 | static void srf04_remove(struct platform_device *pdev) |
348 | { |
349 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); |
350 | struct srf04_data *data = iio_priv(indio_dev); |
351 | |
352 | iio_device_unregister(indio_dev); |
353 | |
354 | if (data->gpiod_power) { |
355 | pm_runtime_disable(dev: data->dev); |
356 | pm_runtime_set_suspended(dev: data->dev); |
357 | } |
358 | } |
359 | |
360 | static int srf04_pm_runtime_suspend(struct device *dev) |
361 | { |
362 | struct platform_device *pdev = container_of(dev, |
363 | struct platform_device, dev); |
364 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); |
365 | struct srf04_data *data = iio_priv(indio_dev); |
366 | |
367 | gpiod_set_value(desc: data->gpiod_power, value: 0); |
368 | |
369 | return 0; |
370 | } |
371 | |
372 | static int srf04_pm_runtime_resume(struct device *dev) |
373 | { |
374 | struct platform_device *pdev = container_of(dev, |
375 | struct platform_device, dev); |
376 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); |
377 | struct srf04_data *data = iio_priv(indio_dev); |
378 | |
379 | gpiod_set_value(desc: data->gpiod_power, value: 1); |
380 | msleep(msecs: data->startup_time_ms); |
381 | |
382 | return 0; |
383 | } |
384 | |
385 | static const struct dev_pm_ops srf04_pm_ops = { |
386 | RUNTIME_PM_OPS(srf04_pm_runtime_suspend, |
387 | srf04_pm_runtime_resume, NULL) |
388 | }; |
389 | |
390 | static struct platform_driver srf04_driver = { |
391 | .probe = srf04_probe, |
392 | .remove_new = srf04_remove, |
393 | .driver = { |
394 | .name = "srf04-gpio" , |
395 | .of_match_table = of_srf04_match, |
396 | .pm = pm_ptr(&srf04_pm_ops), |
397 | }, |
398 | }; |
399 | |
400 | module_platform_driver(srf04_driver); |
401 | |
402 | MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>" ); |
403 | MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs" ); |
404 | MODULE_LICENSE("GPL" ); |
405 | MODULE_ALIAS("platform:srf04" ); |
406 | |