.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * SRF04: ultrasonic sensor for distance measuring by using GPIOs |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2017 Andreas Klinger <ak@it-klinger.de> |
---|
5 | 6 | * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | | - * |
---|
16 | 7 | * For details about the device see: |
---|
17 | | - * http://www.robot-electronics.co.uk/htm/srf04tech.htm |
---|
| 8 | + * https://www.robot-electronics.co.uk/htm/srf04tech.htm |
---|
18 | 9 | * |
---|
19 | 10 | * the measurement cycle as timing diagram looks like: |
---|
20 | 11 | * |
---|
.. | .. |
---|
23 | 14 | * trig: --+ +------------------------------------------------------ |
---|
24 | 15 | * ^ ^ |
---|
25 | 16 | * |<->| |
---|
26 | | - * udelay(10) |
---|
| 17 | + * udelay(trigger_pulse_us) |
---|
27 | 18 | * |
---|
28 | 19 | * ultra +-+ +-+ +-+ |
---|
29 | 20 | * sonic | | | | | | |
---|
.. | .. |
---|
48 | 39 | #include <linux/kernel.h> |
---|
49 | 40 | #include <linux/module.h> |
---|
50 | 41 | #include <linux/of.h> |
---|
| 42 | +#include <linux/of_device.h> |
---|
51 | 43 | #include <linux/platform_device.h> |
---|
52 | 44 | #include <linux/property.h> |
---|
53 | 45 | #include <linux/sched.h> |
---|
54 | 46 | #include <linux/interrupt.h> |
---|
55 | 47 | #include <linux/delay.h> |
---|
| 48 | +#include <linux/pm_runtime.h> |
---|
56 | 49 | #include <linux/iio/iio.h> |
---|
57 | 50 | #include <linux/iio/sysfs.h> |
---|
| 51 | + |
---|
| 52 | +struct srf04_cfg { |
---|
| 53 | + unsigned long trigger_pulse_us; |
---|
| 54 | +}; |
---|
58 | 55 | |
---|
59 | 56 | struct srf04_data { |
---|
60 | 57 | struct device *dev; |
---|
61 | 58 | struct gpio_desc *gpiod_trig; |
---|
62 | 59 | struct gpio_desc *gpiod_echo; |
---|
| 60 | + struct gpio_desc *gpiod_power; |
---|
63 | 61 | struct mutex lock; |
---|
64 | 62 | int irqnr; |
---|
65 | 63 | ktime_t ts_rising; |
---|
66 | 64 | ktime_t ts_falling; |
---|
67 | 65 | struct completion rising; |
---|
68 | 66 | struct completion falling; |
---|
| 67 | + const struct srf04_cfg *cfg; |
---|
| 68 | + int startup_time_ms; |
---|
| 69 | +}; |
---|
| 70 | + |
---|
| 71 | +static const struct srf04_cfg srf04_cfg = { |
---|
| 72 | + .trigger_pulse_us = 10, |
---|
| 73 | +}; |
---|
| 74 | + |
---|
| 75 | +static const struct srf04_cfg mb_lv_cfg = { |
---|
| 76 | + .trigger_pulse_us = 20, |
---|
69 | 77 | }; |
---|
70 | 78 | |
---|
71 | 79 | static irqreturn_t srf04_handle_irq(int irq, void *dev_id) |
---|
.. | .. |
---|
92 | 100 | u64 dt_ns; |
---|
93 | 101 | u32 time_ns, distance_mm; |
---|
94 | 102 | |
---|
| 103 | + if (data->gpiod_power) |
---|
| 104 | + pm_runtime_get_sync(data->dev); |
---|
| 105 | + |
---|
95 | 106 | /* |
---|
96 | 107 | * just one read-echo-cycle can take place at a time |
---|
97 | 108 | * ==> lock against concurrent reading calls |
---|
.. | .. |
---|
102 | 113 | reinit_completion(&data->falling); |
---|
103 | 114 | |
---|
104 | 115 | gpiod_set_value(data->gpiod_trig, 1); |
---|
105 | | - udelay(10); |
---|
| 116 | + udelay(data->cfg->trigger_pulse_us); |
---|
106 | 117 | gpiod_set_value(data->gpiod_trig, 0); |
---|
| 118 | + |
---|
| 119 | + if (data->gpiod_power) { |
---|
| 120 | + pm_runtime_mark_last_busy(data->dev); |
---|
| 121 | + pm_runtime_put_autosuspend(data->dev); |
---|
| 122 | + } |
---|
107 | 123 | |
---|
108 | 124 | /* it should not take more than 20 ms until echo is rising */ |
---|
109 | 125 | ret = wait_for_completion_killable_timeout(&data->rising, HZ/50); |
---|
.. | .. |
---|
216 | 232 | }, |
---|
217 | 233 | }; |
---|
218 | 234 | |
---|
| 235 | +static const struct of_device_id of_srf04_match[] = { |
---|
| 236 | + { .compatible = "devantech,srf04", .data = &srf04_cfg}, |
---|
| 237 | + { .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg}, |
---|
| 238 | + { .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg}, |
---|
| 239 | + { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg}, |
---|
| 240 | + { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg}, |
---|
| 241 | + { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg}, |
---|
| 242 | + {}, |
---|
| 243 | +}; |
---|
| 244 | + |
---|
| 245 | +MODULE_DEVICE_TABLE(of, of_srf04_match); |
---|
| 246 | + |
---|
219 | 247 | static int srf04_probe(struct platform_device *pdev) |
---|
220 | 248 | { |
---|
221 | 249 | struct device *dev = &pdev->dev; |
---|
.. | .. |
---|
231 | 259 | |
---|
232 | 260 | data = iio_priv(indio_dev); |
---|
233 | 261 | data->dev = dev; |
---|
| 262 | + data->cfg = of_match_device(of_srf04_match, dev)->data; |
---|
234 | 263 | |
---|
235 | 264 | mutex_init(&data->lock); |
---|
236 | 265 | init_completion(&data->rising); |
---|
.. | .. |
---|
248 | 277 | dev_err(dev, "failed to get echo-gpios: err=%ld\n", |
---|
249 | 278 | PTR_ERR(data->gpiod_echo)); |
---|
250 | 279 | return PTR_ERR(data->gpiod_echo); |
---|
| 280 | + } |
---|
| 281 | + |
---|
| 282 | + data->gpiod_power = devm_gpiod_get_optional(dev, "power", |
---|
| 283 | + GPIOD_OUT_LOW); |
---|
| 284 | + if (IS_ERR(data->gpiod_power)) { |
---|
| 285 | + dev_err(dev, "failed to get power-gpios: err=%ld\n", |
---|
| 286 | + PTR_ERR(data->gpiod_power)); |
---|
| 287 | + return PTR_ERR(data->gpiod_power); |
---|
| 288 | + } |
---|
| 289 | + if (data->gpiod_power) { |
---|
| 290 | + |
---|
| 291 | + if (of_property_read_u32(dev->of_node, "startup-time-ms", |
---|
| 292 | + &data->startup_time_ms)) |
---|
| 293 | + data->startup_time_ms = 100; |
---|
| 294 | + dev_dbg(dev, "using power gpio: startup-time-ms=%d\n", |
---|
| 295 | + data->startup_time_ms); |
---|
251 | 296 | } |
---|
252 | 297 | |
---|
253 | 298 | if (gpiod_cansleep(data->gpiod_echo)) { |
---|
.. | .. |
---|
272 | 317 | platform_set_drvdata(pdev, indio_dev); |
---|
273 | 318 | |
---|
274 | 319 | indio_dev->name = "srf04"; |
---|
275 | | - indio_dev->dev.parent = &pdev->dev; |
---|
276 | 320 | indio_dev->info = &srf04_iio_info; |
---|
277 | 321 | indio_dev->modes = INDIO_DIRECT_MODE; |
---|
278 | 322 | indio_dev->channels = srf04_chan_spec; |
---|
279 | 323 | indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec); |
---|
280 | 324 | |
---|
281 | | - return devm_iio_device_register(dev, indio_dev); |
---|
| 325 | + ret = iio_device_register(indio_dev); |
---|
| 326 | + if (ret < 0) { |
---|
| 327 | + dev_err(data->dev, "iio_device_register: %d\n", ret); |
---|
| 328 | + return ret; |
---|
| 329 | + } |
---|
| 330 | + |
---|
| 331 | + if (data->gpiod_power) { |
---|
| 332 | + pm_runtime_set_autosuspend_delay(data->dev, 1000); |
---|
| 333 | + pm_runtime_use_autosuspend(data->dev); |
---|
| 334 | + |
---|
| 335 | + ret = pm_runtime_set_active(data->dev); |
---|
| 336 | + if (ret) { |
---|
| 337 | + dev_err(data->dev, "pm_runtime_set_active: %d\n", ret); |
---|
| 338 | + iio_device_unregister(indio_dev); |
---|
| 339 | + } |
---|
| 340 | + |
---|
| 341 | + pm_runtime_enable(data->dev); |
---|
| 342 | + pm_runtime_idle(data->dev); |
---|
| 343 | + } |
---|
| 344 | + |
---|
| 345 | + return ret; |
---|
282 | 346 | } |
---|
283 | 347 | |
---|
284 | | -static const struct of_device_id of_srf04_match[] = { |
---|
285 | | - { .compatible = "devantech,srf04", }, |
---|
286 | | - {}, |
---|
287 | | -}; |
---|
| 348 | +static int srf04_remove(struct platform_device *pdev) |
---|
| 349 | +{ |
---|
| 350 | + struct iio_dev *indio_dev = platform_get_drvdata(pdev); |
---|
| 351 | + struct srf04_data *data = iio_priv(indio_dev); |
---|
288 | 352 | |
---|
289 | | -MODULE_DEVICE_TABLE(of, of_srf04_match); |
---|
| 353 | + iio_device_unregister(indio_dev); |
---|
| 354 | + |
---|
| 355 | + if (data->gpiod_power) { |
---|
| 356 | + pm_runtime_disable(data->dev); |
---|
| 357 | + pm_runtime_set_suspended(data->dev); |
---|
| 358 | + } |
---|
| 359 | + |
---|
| 360 | + return 0; |
---|
| 361 | +} |
---|
| 362 | + |
---|
| 363 | +static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev) |
---|
| 364 | +{ |
---|
| 365 | + struct platform_device *pdev = container_of(dev, |
---|
| 366 | + struct platform_device, dev); |
---|
| 367 | + struct iio_dev *indio_dev = platform_get_drvdata(pdev); |
---|
| 368 | + struct srf04_data *data = iio_priv(indio_dev); |
---|
| 369 | + |
---|
| 370 | + gpiod_set_value(data->gpiod_power, 0); |
---|
| 371 | + |
---|
| 372 | + return 0; |
---|
| 373 | +} |
---|
| 374 | + |
---|
| 375 | +static int __maybe_unused srf04_pm_runtime_resume(struct device *dev) |
---|
| 376 | +{ |
---|
| 377 | + struct platform_device *pdev = container_of(dev, |
---|
| 378 | + struct platform_device, dev); |
---|
| 379 | + struct iio_dev *indio_dev = platform_get_drvdata(pdev); |
---|
| 380 | + struct srf04_data *data = iio_priv(indio_dev); |
---|
| 381 | + |
---|
| 382 | + gpiod_set_value(data->gpiod_power, 1); |
---|
| 383 | + msleep(data->startup_time_ms); |
---|
| 384 | + |
---|
| 385 | + return 0; |
---|
| 386 | +} |
---|
| 387 | + |
---|
| 388 | +static const struct dev_pm_ops srf04_pm_ops = { |
---|
| 389 | + SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend, |
---|
| 390 | + srf04_pm_runtime_resume, NULL) |
---|
| 391 | +}; |
---|
290 | 392 | |
---|
291 | 393 | static struct platform_driver srf04_driver = { |
---|
292 | 394 | .probe = srf04_probe, |
---|
| 395 | + .remove = srf04_remove, |
---|
293 | 396 | .driver = { |
---|
294 | 397 | .name = "srf04-gpio", |
---|
295 | 398 | .of_match_table = of_srf04_match, |
---|
| 399 | + .pm = &srf04_pm_ops, |
---|
296 | 400 | }, |
---|
297 | 401 | }; |
---|
298 | 402 | |
---|