brintos

brintos / linux-shallow public Read only

0
0
Text · 10.3 KiB · 86c5767 Raw
406 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * SRF04: ultrasonic sensor for distance measuring by using GPIOs4 *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.htm9 *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                interrupt32 *                           (ts_rising)              (ts_falling)33 *                           |<---------------------->|34 *                              pulse time measured35 *                              --> one round trip of ultra sonic waves36 */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 time109	 * ==> lock against concurrent reading calls110	 */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_mark_last_busy(data->dev);122		pm_runtime_put_autosuspend(data->dev);123	}124 125	/* it should not take more than 20 ms until echo is rising */126	ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);127	if (ret < 0) {128		mutex_unlock(&data->lock);129		return ret;130	} else if (ret == 0) {131		mutex_unlock(&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(&data->falling, HZ/20);137	if (ret < 0) {138		mutex_unlock(&data->lock);139		return ret;140	} else if (ret == 0) {141		mutex_unlock(&data->lock);142		return -ETIMEDOUT;143	}144 145	ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);146 147	mutex_unlock(&data->lock);148 149	dt_ns = ktime_to_ns(ktime_dt);150	/*151	 * measuring more than 6,45 meters is beyond the capabilities of152	 * the supported sensors153	 * ==> filter out invalid results for not measuring echos of154	 *     another us sensor155	 *156	 * formula:157	 *         distance     6,45 * 2 m158	 * time = ---------- = ------------ = 40438871 ns159	 *          speed         319 m/s160	 *161	 * using a minimum speed at -20 °C of 319 m/s162	 */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 * Temp172	 *   with Temp in °C173	 *   and speed in m/s174	 *175	 * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the176	 * temperature177	 *178	 * therefore:179	 *             time     343,5     time * 106180	 * distance = ------ * ------- = ------------181	 *             10^6         2         617176182	 *   with time in ns183	 *   and distance in mm (one way)184	 *185	 * because we limit to 6,45 meters the multiplication with 106 just186	 * fits into 32 bit187	 */188	distance_mm = time_ns * 106 / 617176;189 190	return distance_mm;191}192 193static 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 mm213		 * 1 LSB is 1 mm214		 */215		*val = 0;216		*val2 = 1000;217		return IIO_VAL_INT_PLUS_MICRO;218	default:219		return -EINVAL;220	}221}222 223static const struct iio_info srf04_iio_info = {224	.read_raw		= srf04_read_raw,225};226 227static 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 236static 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 246MODULE_DEVICE_TABLE(of, of_srf04_match);247 248static 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(dev, 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(&data->rising);267	init_completion(&data->falling);268 269	data->gpiod_trig = devm_gpiod_get(dev, "trig", GPIOD_OUT_LOW);270	if (IS_ERR(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(data->gpiod_trig);274	}275 276	data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN);277	if (IS_ERR(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(data->gpiod_echo);281	}282 283	data->gpiod_power = devm_gpiod_get_optional(dev, "power",284								GPIOD_OUT_LOW);285	if (IS_ERR(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(data->gpiod_power);289	}290	if (data->gpiod_power) {291		data->startup_time_ms = 100;292		device_property_read_u32(dev, "startup-time-ms", &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(data->gpiod_echo)) {298		dev_err(data->dev, "cansleep-GPIOs not supported\n");299		return -ENODEV;300	}301 302	data->irqnr = gpiod_to_irq(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, data->irqnr, srf04_handle_irq,309			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,310			pdev->name, 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, 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(data->dev, 1000);332		pm_runtime_use_autosuspend(data->dev);333 334		ret = pm_runtime_set_active(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(data->dev);341		pm_runtime_idle(data->dev);342	}343 344	return ret;345}346 347static 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(data->dev);356		pm_runtime_set_suspended(data->dev);357	}358}359 360static 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(data->gpiod_power, 0);368 369	return 0;370}371 372static 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(data->gpiod_power, 1);380	msleep(data->startup_time_ms);381 382	return 0;383}384 385static const struct dev_pm_ops srf04_pm_ops = {386	RUNTIME_PM_OPS(srf04_pm_runtime_suspend,387		       srf04_pm_runtime_resume, NULL)388};389 390static 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 400module_platform_driver(srf04_driver);401 402MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");403MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs");404MODULE_LICENSE("GPL");405MODULE_ALIAS("platform:srf04");406