treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 157
[linux-2.6-block.git] / drivers / iio / proximity / srf04.c
CommitLineData
c942fddf 1// SPDX-License-Identifier: GPL-2.0-or-later
feda2840
AK
2/*
3 * SRF04: ultrasonic sensor for distance measuring by using GPIOs
4 *
5 * Copyright (c) 2017 Andreas Klinger <ak@it-klinger.de>
6 *
feda2840
AK
7 * For details about the device see:
8 * http://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 * |<->|
bb208037 17 * udelay(trigger_pulse_us)
feda2840
AK
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/module.h>
41#include <linux/of.h>
bb208037 42#include <linux/of_device.h>
feda2840
AK
43#include <linux/platform_device.h>
44#include <linux/property.h>
45#include <linux/sched.h>
46#include <linux/interrupt.h>
47#include <linux/delay.h>
48#include <linux/iio/iio.h>
49#include <linux/iio/sysfs.h>
50
bb208037
AK
51struct srf04_cfg {
52 unsigned long trigger_pulse_us;
53};
54
feda2840
AK
55struct srf04_data {
56 struct device *dev;
57 struct gpio_desc *gpiod_trig;
58 struct gpio_desc *gpiod_echo;
59 struct mutex lock;
60 int irqnr;
61 ktime_t ts_rising;
62 ktime_t ts_falling;
63 struct completion rising;
64 struct completion falling;
bb208037
AK
65 const struct srf04_cfg *cfg;
66};
67
68static const struct srf04_cfg srf04_cfg = {
69 .trigger_pulse_us = 10,
70};
71
72static const struct srf04_cfg mb_lv_cfg = {
73 .trigger_pulse_us = 20,
feda2840
AK
74};
75
76static irqreturn_t srf04_handle_irq(int irq, void *dev_id)
77{
78 struct iio_dev *indio_dev = dev_id;
79 struct srf04_data *data = iio_priv(indio_dev);
80 ktime_t now = ktime_get();
81
82 if (gpiod_get_value(data->gpiod_echo)) {
83 data->ts_rising = now;
84 complete(&data->rising);
85 } else {
86 data->ts_falling = now;
87 complete(&data->falling);
88 }
89
90 return IRQ_HANDLED;
91}
92
93static int srf04_read(struct srf04_data *data)
94{
95 int ret;
96 ktime_t ktime_dt;
97 u64 dt_ns;
98 u32 time_ns, distance_mm;
99
100 /*
101 * just one read-echo-cycle can take place at a time
102 * ==> lock against concurrent reading calls
103 */
104 mutex_lock(&data->lock);
105
106 reinit_completion(&data->rising);
107 reinit_completion(&data->falling);
108
109 gpiod_set_value(data->gpiod_trig, 1);
bb208037 110 udelay(data->cfg->trigger_pulse_us);
feda2840
AK
111 gpiod_set_value(data->gpiod_trig, 0);
112
113 /* it cannot take more than 20 ms */
114 ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
115 if (ret < 0) {
116 mutex_unlock(&data->lock);
117 return ret;
118 } else if (ret == 0) {
119 mutex_unlock(&data->lock);
120 return -ETIMEDOUT;
121 }
122
123 ret = wait_for_completion_killable_timeout(&data->falling, HZ/50);
124 if (ret < 0) {
125 mutex_unlock(&data->lock);
126 return ret;
127 } else if (ret == 0) {
128 mutex_unlock(&data->lock);
129 return -ETIMEDOUT;
130 }
131
132 ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);
133
134 mutex_unlock(&data->lock);
135
136 dt_ns = ktime_to_ns(ktime_dt);
137 /*
138 * measuring more than 3 meters is beyond the capabilities of
139 * the sensor
140 * ==> filter out invalid results for not measuring echos of
141 * another us sensor
142 *
143 * formula:
144 * distance 3 m
145 * time = ---------- = --------- = 9404389 ns
146 * speed 319 m/s
147 *
148 * using a minimum speed at -20 °C of 319 m/s
149 */
150 if (dt_ns > 9404389)
151 return -EIO;
152
153 time_ns = dt_ns;
154
155 /*
156 * the speed as function of the temperature is approximately:
157 *
158 * speed = 331,5 + 0,6 * Temp
159 * with Temp in °C
160 * and speed in m/s
161 *
162 * use 343 m/s as ultrasonic speed at 20 °C here in absence of the
163 * temperature
164 *
165 * therefore:
166 * time 343
167 * distance = ------ * -----
168 * 10^6 2
169 * with time in ns
170 * and distance in mm (one way)
171 *
172 * because we limit to 3 meters the multiplication with 343 just
173 * fits into 32 bit
174 */
175 distance_mm = time_ns * 343 / 2000000;
176
177 return distance_mm;
178}
179
180static int srf04_read_raw(struct iio_dev *indio_dev,
181 struct iio_chan_spec const *channel, int *val,
182 int *val2, long info)
183{
184 struct srf04_data *data = iio_priv(indio_dev);
185 int ret;
186
187 if (channel->type != IIO_DISTANCE)
188 return -EINVAL;
189
190 switch (info) {
191 case IIO_CHAN_INFO_RAW:
192 ret = srf04_read(data);
193 if (ret < 0)
194 return ret;
195 *val = ret;
196 return IIO_VAL_INT;
197 case IIO_CHAN_INFO_SCALE:
198 /*
199 * theoretical maximum resolution is 3 mm
200 * 1 LSB is 1 mm
201 */
202 *val = 0;
203 *val2 = 1000;
204 return IIO_VAL_INT_PLUS_MICRO;
205 default:
206 return -EINVAL;
207 }
208}
209
210static const struct iio_info srf04_iio_info = {
feda2840
AK
211 .read_raw = srf04_read_raw,
212};
213
214static const struct iio_chan_spec srf04_chan_spec[] = {
215 {
216 .type = IIO_DISTANCE,
217 .info_mask_separate =
218 BIT(IIO_CHAN_INFO_RAW) |
219 BIT(IIO_CHAN_INFO_SCALE),
220 },
221};
222
bb208037
AK
223static const struct of_device_id of_srf04_match[] = {
224 { .compatible = "devantech,srf04", .data = &srf04_cfg},
225 { .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg},
226 { .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg},
227 { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg},
228 { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg},
229 { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg},
230 {},
231};
232
233MODULE_DEVICE_TABLE(of, of_srf04_match);
234
feda2840
AK
235static int srf04_probe(struct platform_device *pdev)
236{
237 struct device *dev = &pdev->dev;
238 struct srf04_data *data;
239 struct iio_dev *indio_dev;
240 int ret;
241
242 indio_dev = devm_iio_device_alloc(dev, sizeof(struct srf04_data));
243 if (!indio_dev) {
244 dev_err(dev, "failed to allocate IIO device\n");
245 return -ENOMEM;
246 }
247
248 data = iio_priv(indio_dev);
249 data->dev = dev;
bb208037 250 data->cfg = of_match_device(of_srf04_match, dev)->data;
feda2840
AK
251
252 mutex_init(&data->lock);
253 init_completion(&data->rising);
254 init_completion(&data->falling);
255
256 data->gpiod_trig = devm_gpiod_get(dev, "trig", GPIOD_OUT_LOW);
257 if (IS_ERR(data->gpiod_trig)) {
258 dev_err(dev, "failed to get trig-gpios: err=%ld\n",
259 PTR_ERR(data->gpiod_trig));
260 return PTR_ERR(data->gpiod_trig);
261 }
262
263 data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN);
264 if (IS_ERR(data->gpiod_echo)) {
265 dev_err(dev, "failed to get echo-gpios: err=%ld\n",
266 PTR_ERR(data->gpiod_echo));
267 return PTR_ERR(data->gpiod_echo);
268 }
269
270 if (gpiod_cansleep(data->gpiod_echo)) {
271 dev_err(data->dev, "cansleep-GPIOs not supported\n");
272 return -ENODEV;
273 }
274
275 data->irqnr = gpiod_to_irq(data->gpiod_echo);
276 if (data->irqnr < 0) {
277 dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr);
278 return data->irqnr;
279 }
280
281 ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq,
282 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
283 pdev->name, indio_dev);
284 if (ret < 0) {
285 dev_err(data->dev, "request_irq: %d\n", ret);
286 return ret;
287 }
288
289 platform_set_drvdata(pdev, indio_dev);
290
291 indio_dev->name = "srf04";
292 indio_dev->dev.parent = &pdev->dev;
293 indio_dev->info = &srf04_iio_info;
294 indio_dev->modes = INDIO_DIRECT_MODE;
295 indio_dev->channels = srf04_chan_spec;
296 indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec);
297
298 return devm_iio_device_register(dev, indio_dev);
299}
300
feda2840
AK
301static struct platform_driver srf04_driver = {
302 .probe = srf04_probe,
303 .driver = {
304 .name = "srf04-gpio",
305 .of_match_table = of_srf04_match,
306 },
307};
308
309module_platform_driver(srf04_driver);
310
311MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
312MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs");
313MODULE_LICENSE("GPL");
314MODULE_ALIAS("platform:srf04");