18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SRF04: ultrasonic sensor for distance measuring by using GPIOs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2017 Andreas Klinger <ak@it-klinger.de> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * For details about the device see: 88c2ecf20Sopenharmony_ci * https://www.robot-electronics.co.uk/htm/srf04tech.htm 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * the measurement cycle as timing diagram looks like: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * +---+ 138c2ecf20Sopenharmony_ci * GPIO | | 148c2ecf20Sopenharmony_ci * trig: --+ +------------------------------------------------------ 158c2ecf20Sopenharmony_ci * ^ ^ 168c2ecf20Sopenharmony_ci * |<->| 178c2ecf20Sopenharmony_ci * udelay(trigger_pulse_us) 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * ultra +-+ +-+ +-+ 208c2ecf20Sopenharmony_ci * sonic | | | | | | 218c2ecf20Sopenharmony_ci * burst: ---------+ +-+ +-+ +----------------------------------------- 228c2ecf20Sopenharmony_ci * . 238c2ecf20Sopenharmony_ci * ultra . +-+ +-+ +-+ 248c2ecf20Sopenharmony_ci * sonic . | | | | | | 258c2ecf20Sopenharmony_ci * echo: ----------------------------------+ +-+ +-+ +---------------- 268c2ecf20Sopenharmony_ci * . . 278c2ecf20Sopenharmony_ci * +------------------------+ 288c2ecf20Sopenharmony_ci * GPIO | | 298c2ecf20Sopenharmony_ci * echo: -------------------+ +--------------- 308c2ecf20Sopenharmony_ci * ^ ^ 318c2ecf20Sopenharmony_ci * interrupt interrupt 328c2ecf20Sopenharmony_ci * (ts_rising) (ts_falling) 338c2ecf20Sopenharmony_ci * |<---------------------->| 348c2ecf20Sopenharmony_ci * pulse time measured 358c2ecf20Sopenharmony_ci * --> one round trip of ultra sonic waves 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci#include <linux/err.h> 388c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 398c2ecf20Sopenharmony_ci#include <linux/kernel.h> 408c2ecf20Sopenharmony_ci#include <linux/module.h> 418c2ecf20Sopenharmony_ci#include <linux/of.h> 428c2ecf20Sopenharmony_ci#include <linux/of_device.h> 438c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 448c2ecf20Sopenharmony_ci#include <linux/property.h> 458c2ecf20Sopenharmony_ci#include <linux/sched.h> 468c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 478c2ecf20Sopenharmony_ci#include <linux/delay.h> 488c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 498c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 508c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct srf04_cfg { 538c2ecf20Sopenharmony_ci unsigned long trigger_pulse_us; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct srf04_data { 578c2ecf20Sopenharmony_ci struct device *dev; 588c2ecf20Sopenharmony_ci struct gpio_desc *gpiod_trig; 598c2ecf20Sopenharmony_ci struct gpio_desc *gpiod_echo; 608c2ecf20Sopenharmony_ci struct gpio_desc *gpiod_power; 618c2ecf20Sopenharmony_ci struct mutex lock; 628c2ecf20Sopenharmony_ci int irqnr; 638c2ecf20Sopenharmony_ci ktime_t ts_rising; 648c2ecf20Sopenharmony_ci ktime_t ts_falling; 658c2ecf20Sopenharmony_ci struct completion rising; 668c2ecf20Sopenharmony_ci struct completion falling; 678c2ecf20Sopenharmony_ci const struct srf04_cfg *cfg; 688c2ecf20Sopenharmony_ci int startup_time_ms; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic const struct srf04_cfg srf04_cfg = { 728c2ecf20Sopenharmony_ci .trigger_pulse_us = 10, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const struct srf04_cfg mb_lv_cfg = { 768c2ecf20Sopenharmony_ci .trigger_pulse_us = 20, 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic irqreturn_t srf04_handle_irq(int irq, void *dev_id) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_id; 828c2ecf20Sopenharmony_ci struct srf04_data *data = iio_priv(indio_dev); 838c2ecf20Sopenharmony_ci ktime_t now = ktime_get(); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (gpiod_get_value(data->gpiod_echo)) { 868c2ecf20Sopenharmony_ci data->ts_rising = now; 878c2ecf20Sopenharmony_ci complete(&data->rising); 888c2ecf20Sopenharmony_ci } else { 898c2ecf20Sopenharmony_ci data->ts_falling = now; 908c2ecf20Sopenharmony_ci complete(&data->falling); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return IRQ_HANDLED; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int srf04_read(struct srf04_data *data) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int ret; 998c2ecf20Sopenharmony_ci ktime_t ktime_dt; 1008c2ecf20Sopenharmony_ci u64 dt_ns; 1018c2ecf20Sopenharmony_ci u32 time_ns, distance_mm; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (data->gpiod_power) 1048c2ecf20Sopenharmony_ci pm_runtime_get_sync(data->dev); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* 1078c2ecf20Sopenharmony_ci * just one read-echo-cycle can take place at a time 1088c2ecf20Sopenharmony_ci * ==> lock against concurrent reading calls 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci reinit_completion(&data->rising); 1138c2ecf20Sopenharmony_ci reinit_completion(&data->falling); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci gpiod_set_value(data->gpiod_trig, 1); 1168c2ecf20Sopenharmony_ci udelay(data->cfg->trigger_pulse_us); 1178c2ecf20Sopenharmony_ci gpiod_set_value(data->gpiod_trig, 0); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (data->gpiod_power) { 1208c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(data->dev); 1218c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(data->dev); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* it should not take more than 20 ms until echo is rising */ 1258c2ecf20Sopenharmony_ci ret = wait_for_completion_killable_timeout(&data->rising, HZ/50); 1268c2ecf20Sopenharmony_ci if (ret < 0) { 1278c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci } else if (ret == 0) { 1308c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1318c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* it cannot take more than 50 ms until echo is falling */ 1358c2ecf20Sopenharmony_ci ret = wait_for_completion_killable_timeout(&data->falling, HZ/20); 1368c2ecf20Sopenharmony_ci if (ret < 0) { 1378c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci } else if (ret == 0) { 1408c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1418c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ktime_dt = ktime_sub(data->ts_falling, data->ts_rising); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dt_ns = ktime_to_ns(ktime_dt); 1498c2ecf20Sopenharmony_ci /* 1508c2ecf20Sopenharmony_ci * measuring more than 6,45 meters is beyond the capabilities of 1518c2ecf20Sopenharmony_ci * the supported sensors 1528c2ecf20Sopenharmony_ci * ==> filter out invalid results for not measuring echos of 1538c2ecf20Sopenharmony_ci * another us sensor 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * formula: 1568c2ecf20Sopenharmony_ci * distance 6,45 * 2 m 1578c2ecf20Sopenharmony_ci * time = ---------- = ------------ = 40438871 ns 1588c2ecf20Sopenharmony_ci * speed 319 m/s 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * using a minimum speed at -20 °C of 319 m/s 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci if (dt_ns > 40438871) 1638c2ecf20Sopenharmony_ci return -EIO; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci time_ns = dt_ns; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * the speed as function of the temperature is approximately: 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * speed = 331,5 + 0,6 * Temp 1718c2ecf20Sopenharmony_ci * with Temp in °C 1728c2ecf20Sopenharmony_ci * and speed in m/s 1738c2ecf20Sopenharmony_ci * 1748c2ecf20Sopenharmony_ci * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the 1758c2ecf20Sopenharmony_ci * temperature 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * therefore: 1788c2ecf20Sopenharmony_ci * time 343,5 time * 106 1798c2ecf20Sopenharmony_ci * distance = ------ * ------- = ------------ 1808c2ecf20Sopenharmony_ci * 10^6 2 617176 1818c2ecf20Sopenharmony_ci * with time in ns 1828c2ecf20Sopenharmony_ci * and distance in mm (one way) 1838c2ecf20Sopenharmony_ci * 1848c2ecf20Sopenharmony_ci * because we limit to 6,45 meters the multiplication with 106 just 1858c2ecf20Sopenharmony_ci * fits into 32 bit 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci distance_mm = time_ns * 106 / 617176; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return distance_mm; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int srf04_read_raw(struct iio_dev *indio_dev, 1938c2ecf20Sopenharmony_ci struct iio_chan_spec const *channel, int *val, 1948c2ecf20Sopenharmony_ci int *val2, long info) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct srf04_data *data = iio_priv(indio_dev); 1978c2ecf20Sopenharmony_ci int ret; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (channel->type != IIO_DISTANCE) 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci switch (info) { 2038c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 2048c2ecf20Sopenharmony_ci ret = srf04_read(data); 2058c2ecf20Sopenharmony_ci if (ret < 0) 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci *val = ret; 2088c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2098c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * theoretical maximum resolution is 3 mm 2128c2ecf20Sopenharmony_ci * 1 LSB is 1 mm 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci *val = 0; 2158c2ecf20Sopenharmony_ci *val2 = 1000; 2168c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2178c2ecf20Sopenharmony_ci default: 2188c2ecf20Sopenharmony_ci return -EINVAL; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const struct iio_info srf04_iio_info = { 2238c2ecf20Sopenharmony_ci .read_raw = srf04_read_raw, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const struct iio_chan_spec srf04_chan_spec[] = { 2278c2ecf20Sopenharmony_ci { 2288c2ecf20Sopenharmony_ci .type = IIO_DISTANCE, 2298c2ecf20Sopenharmony_ci .info_mask_separate = 2308c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW) | 2318c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 2328c2ecf20Sopenharmony_ci }, 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct of_device_id of_srf04_match[] = { 2368c2ecf20Sopenharmony_ci { .compatible = "devantech,srf04", .data = &srf04_cfg}, 2378c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg}, 2388c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg}, 2398c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg}, 2408c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg}, 2418c2ecf20Sopenharmony_ci { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg}, 2428c2ecf20Sopenharmony_ci {}, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_srf04_match); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int srf04_probe(struct platform_device *pdev) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2508c2ecf20Sopenharmony_ci struct srf04_data *data; 2518c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 2528c2ecf20Sopenharmony_ci int ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(struct srf04_data)); 2558c2ecf20Sopenharmony_ci if (!indio_dev) { 2568c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate IIO device\n"); 2578c2ecf20Sopenharmony_ci return -ENOMEM; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 2618c2ecf20Sopenharmony_ci data->dev = dev; 2628c2ecf20Sopenharmony_ci data->cfg = of_match_device(of_srf04_match, dev)->data; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mutex_init(&data->lock); 2658c2ecf20Sopenharmony_ci init_completion(&data->rising); 2668c2ecf20Sopenharmony_ci init_completion(&data->falling); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci data->gpiod_trig = devm_gpiod_get(dev, "trig", GPIOD_OUT_LOW); 2698c2ecf20Sopenharmony_ci if (IS_ERR(data->gpiod_trig)) { 2708c2ecf20Sopenharmony_ci dev_err(dev, "failed to get trig-gpios: err=%ld\n", 2718c2ecf20Sopenharmony_ci PTR_ERR(data->gpiod_trig)); 2728c2ecf20Sopenharmony_ci return PTR_ERR(data->gpiod_trig); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN); 2768c2ecf20Sopenharmony_ci if (IS_ERR(data->gpiod_echo)) { 2778c2ecf20Sopenharmony_ci dev_err(dev, "failed to get echo-gpios: err=%ld\n", 2788c2ecf20Sopenharmony_ci PTR_ERR(data->gpiod_echo)); 2798c2ecf20Sopenharmony_ci return PTR_ERR(data->gpiod_echo); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci data->gpiod_power = devm_gpiod_get_optional(dev, "power", 2838c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 2848c2ecf20Sopenharmony_ci if (IS_ERR(data->gpiod_power)) { 2858c2ecf20Sopenharmony_ci dev_err(dev, "failed to get power-gpios: err=%ld\n", 2868c2ecf20Sopenharmony_ci PTR_ERR(data->gpiod_power)); 2878c2ecf20Sopenharmony_ci return PTR_ERR(data->gpiod_power); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci if (data->gpiod_power) { 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "startup-time-ms", 2928c2ecf20Sopenharmony_ci &data->startup_time_ms)) 2938c2ecf20Sopenharmony_ci data->startup_time_ms = 100; 2948c2ecf20Sopenharmony_ci dev_dbg(dev, "using power gpio: startup-time-ms=%d\n", 2958c2ecf20Sopenharmony_ci data->startup_time_ms); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (gpiod_cansleep(data->gpiod_echo)) { 2998c2ecf20Sopenharmony_ci dev_err(data->dev, "cansleep-GPIOs not supported\n"); 3008c2ecf20Sopenharmony_ci return -ENODEV; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci data->irqnr = gpiod_to_irq(data->gpiod_echo); 3048c2ecf20Sopenharmony_ci if (data->irqnr < 0) { 3058c2ecf20Sopenharmony_ci dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr); 3068c2ecf20Sopenharmony_ci return data->irqnr; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq, 3108c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 3118c2ecf20Sopenharmony_ci pdev->name, indio_dev); 3128c2ecf20Sopenharmony_ci if (ret < 0) { 3138c2ecf20Sopenharmony_ci dev_err(data->dev, "request_irq: %d\n", ret); 3148c2ecf20Sopenharmony_ci return ret; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, indio_dev); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci indio_dev->name = "srf04"; 3208c2ecf20Sopenharmony_ci indio_dev->info = &srf04_iio_info; 3218c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 3228c2ecf20Sopenharmony_ci indio_dev->channels = srf04_chan_spec; 3238c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 3268c2ecf20Sopenharmony_ci if (ret < 0) { 3278c2ecf20Sopenharmony_ci dev_err(data->dev, "iio_device_register: %d\n", ret); 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (data->gpiod_power) { 3328c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(data->dev, 1000); 3338c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(data->dev); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ret = pm_runtime_set_active(data->dev); 3368c2ecf20Sopenharmony_ci if (ret) { 3378c2ecf20Sopenharmony_ci dev_err(data->dev, "pm_runtime_set_active: %d\n", ret); 3388c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci pm_runtime_enable(data->dev); 3428c2ecf20Sopenharmony_ci pm_runtime_idle(data->dev); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int srf04_remove(struct platform_device *pdev) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = platform_get_drvdata(pdev); 3518c2ecf20Sopenharmony_ci struct srf04_data *data = iio_priv(indio_dev); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (data->gpiod_power) { 3568c2ecf20Sopenharmony_ci pm_runtime_disable(data->dev); 3578c2ecf20Sopenharmony_ci pm_runtime_set_suspended(data->dev); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int __maybe_unused srf04_pm_runtime_suspend(struct device *dev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct platform_device *pdev = container_of(dev, 3668c2ecf20Sopenharmony_ci struct platform_device, dev); 3678c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = platform_get_drvdata(pdev); 3688c2ecf20Sopenharmony_ci struct srf04_data *data = iio_priv(indio_dev); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci gpiod_set_value(data->gpiod_power, 0); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int __maybe_unused srf04_pm_runtime_resume(struct device *dev) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct platform_device *pdev = container_of(dev, 3788c2ecf20Sopenharmony_ci struct platform_device, dev); 3798c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = platform_get_drvdata(pdev); 3808c2ecf20Sopenharmony_ci struct srf04_data *data = iio_priv(indio_dev); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci gpiod_set_value(data->gpiod_power, 1); 3838c2ecf20Sopenharmony_ci msleep(data->startup_time_ms); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic const struct dev_pm_ops srf04_pm_ops = { 3898c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend, 3908c2ecf20Sopenharmony_ci srf04_pm_runtime_resume, NULL) 3918c2ecf20Sopenharmony_ci}; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic struct platform_driver srf04_driver = { 3948c2ecf20Sopenharmony_ci .probe = srf04_probe, 3958c2ecf20Sopenharmony_ci .remove = srf04_remove, 3968c2ecf20Sopenharmony_ci .driver = { 3978c2ecf20Sopenharmony_ci .name = "srf04-gpio", 3988c2ecf20Sopenharmony_ci .of_match_table = of_srf04_match, 3998c2ecf20Sopenharmony_ci .pm = &srf04_pm_ops, 4008c2ecf20Sopenharmony_ci }, 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cimodule_platform_driver(srf04_driver); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>"); 4068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs"); 4078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4088c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:srf04"); 409