Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * PING: ultrasonic sensor for distance measuring by using only one GPIOs
0004  *
0005  * Copyright (c) 2019 Andreas Klinger <ak@it-klinger.de>
0006  *
0007  * For details about the devices see:
0008  * http://parallax.com/sites/default/files/downloads/28041-LaserPING-2m-Rangefinder-Guide.pdf
0009  * http://parallax.com/sites/default/files/downloads/28015-PING-Documentation-v1.6.pdf
0010  *
0011  * the measurement cycle as timing diagram looks like:
0012  *
0013  * GPIO      ___              ________________________
0014  * ping:  __/   \____________/                        \________________
0015  *          ^   ^            ^                        ^
0016  *          |<->|            interrupt                interrupt
0017  *         udelay(5)         (ts_rising)              (ts_falling)
0018  *                           |<---------------------->|
0019  *                           .  pulse time measured   .
0020  *                           .  --> one round trip of ultra sonic waves
0021  * ultra                     .                        .
0022  * sonic            _   _   _.                        .
0023  * burst: _________/ \_/ \_/ \_________________________________________
0024  *                                                    .
0025  * ultra                                              .
0026  * sonic                                     _   _   _.
0027  * echo:  __________________________________/ \_/ \_/ \________________
0028  */
0029 #include <linux/err.h>
0030 #include <linux/gpio/consumer.h>
0031 #include <linux/kernel.h>
0032 #include <linux/mod_devicetable.h>
0033 #include <linux/module.h>
0034 #include <linux/platform_device.h>
0035 #include <linux/property.h>
0036 #include <linux/sched.h>
0037 #include <linux/interrupt.h>
0038 #include <linux/delay.h>
0039 #include <linux/iio/iio.h>
0040 #include <linux/iio/sysfs.h>
0041 
0042 struct ping_cfg {
0043     unsigned long   trigger_pulse_us;   /* length of trigger pulse */
0044     int     laserping_error;    /* support error code in */
0045                         /*   pulse width of laser */
0046                         /*   ping sensors */
0047     s64     timeout_ns;     /* timeout in ns */
0048 };
0049 
0050 struct ping_data {
0051     struct device       *dev;
0052     struct gpio_desc    *gpiod_ping;
0053     struct mutex        lock;
0054     int         irqnr;
0055     ktime_t         ts_rising;
0056     ktime_t         ts_falling;
0057     struct completion   rising;
0058     struct completion   falling;
0059     const struct ping_cfg   *cfg;
0060 };
0061 
0062 static const struct ping_cfg pa_ping_cfg = {
0063     .trigger_pulse_us   = 5,
0064     .laserping_error    = 0,
0065     .timeout_ns     = 18500000, /* 3 meters */
0066 };
0067 
0068 static const struct ping_cfg pa_laser_ping_cfg = {
0069     .trigger_pulse_us   = 5,
0070     .laserping_error    = 1,
0071     .timeout_ns     = 15500000, /* 2 meters plus error codes */
0072 };
0073 
0074 static irqreturn_t ping_handle_irq(int irq, void *dev_id)
0075 {
0076     struct iio_dev *indio_dev = dev_id;
0077     struct ping_data *data = iio_priv(indio_dev);
0078     ktime_t now = ktime_get();
0079 
0080     if (gpiod_get_value(data->gpiod_ping)) {
0081         data->ts_rising = now;
0082         complete(&data->rising);
0083     } else {
0084         data->ts_falling = now;
0085         complete(&data->falling);
0086     }
0087 
0088     return IRQ_HANDLED;
0089 }
0090 
0091 static int ping_read(struct iio_dev *indio_dev)
0092 {
0093     struct ping_data *data = iio_priv(indio_dev);
0094     int ret;
0095     ktime_t ktime_dt;
0096     s64 dt_ns;
0097     u32 time_ns, distance_mm;
0098     struct platform_device *pdev = to_platform_device(data->dev);
0099 
0100     /*
0101      * just one read-echo-cycle can take place at a time
0102      * ==> lock against concurrent reading calls
0103      */
0104     mutex_lock(&data->lock);
0105 
0106     reinit_completion(&data->rising);
0107     reinit_completion(&data->falling);
0108 
0109     gpiod_set_value(data->gpiod_ping, 1);
0110     udelay(data->cfg->trigger_pulse_us);
0111     gpiod_set_value(data->gpiod_ping, 0);
0112 
0113     ret = gpiod_direction_input(data->gpiod_ping);
0114     if (ret < 0) {
0115         mutex_unlock(&data->lock);
0116         return ret;
0117     }
0118 
0119     data->irqnr = gpiod_to_irq(data->gpiod_ping);
0120     if (data->irqnr < 0) {
0121         dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr);
0122         mutex_unlock(&data->lock);
0123         return data->irqnr;
0124     }
0125 
0126     ret = request_irq(data->irqnr, ping_handle_irq,
0127                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0128                             pdev->name, indio_dev);
0129     if (ret < 0) {
0130         dev_err(data->dev, "request_irq: %d\n", ret);
0131         mutex_unlock(&data->lock);
0132         return ret;
0133     }
0134 
0135     /* it should not take more than 20 ms until echo is rising */
0136     ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
0137     if (ret < 0)
0138         goto err_reset_direction;
0139     else if (ret == 0) {
0140         ret = -ETIMEDOUT;
0141         goto err_reset_direction;
0142     }
0143 
0144     /* it cannot take more than 50 ms until echo is falling */
0145     ret = wait_for_completion_killable_timeout(&data->falling, HZ/20);
0146     if (ret < 0)
0147         goto err_reset_direction;
0148     else if (ret == 0) {
0149         ret = -ETIMEDOUT;
0150         goto err_reset_direction;
0151     }
0152 
0153     ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);
0154 
0155     free_irq(data->irqnr, indio_dev);
0156 
0157     ret = gpiod_direction_output(data->gpiod_ping, GPIOD_OUT_LOW);
0158     if (ret < 0) {
0159         mutex_unlock(&data->lock);
0160         return ret;
0161     }
0162 
0163     mutex_unlock(&data->lock);
0164 
0165     dt_ns = ktime_to_ns(ktime_dt);
0166     if (dt_ns > data->cfg->timeout_ns) {
0167         dev_dbg(data->dev, "distance out of range: dt=%lldns\n",
0168                                 dt_ns);
0169         return -EIO;
0170     }
0171 
0172     time_ns = dt_ns;
0173 
0174     /*
0175      * read error code of laser ping sensor and give users chance to
0176      * figure out error by using dynamic debugging
0177      */
0178     if (data->cfg->laserping_error) {
0179         if ((time_ns > 12500000) && (time_ns <= 13500000)) {
0180             dev_dbg(data->dev, "target too close or to far\n");
0181             return -EIO;
0182         }
0183         if ((time_ns > 13500000) && (time_ns <= 14500000)) {
0184             dev_dbg(data->dev, "internal sensor error\n");
0185             return -EIO;
0186         }
0187         if ((time_ns > 14500000) && (time_ns <= 15500000)) {
0188             dev_dbg(data->dev, "internal sensor timeout\n");
0189             return -EIO;
0190         }
0191     }
0192 
0193     /*
0194      * the speed as function of the temperature is approximately:
0195      *
0196      * speed = 331,5 + 0,6 * Temp
0197      *   with Temp in °C
0198      *   and speed in m/s
0199      *
0200      * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the
0201      * temperature
0202      *
0203      * therefore:
0204      *             time     343,5     time * 232
0205      * distance = ------ * ------- = ------------
0206      *             10^6         2        1350800
0207      *   with time in ns
0208      *   and distance in mm (one way)
0209      *
0210      * because we limit to 3 meters the multiplication with 232 just
0211      * fits into 32 bit
0212      */
0213     distance_mm = time_ns * 232 / 1350800;
0214 
0215     return distance_mm;
0216 
0217 err_reset_direction:
0218     free_irq(data->irqnr, indio_dev);
0219     mutex_unlock(&data->lock);
0220 
0221     if (gpiod_direction_output(data->gpiod_ping, GPIOD_OUT_LOW))
0222         dev_dbg(data->dev, "error in gpiod_direction_output\n");
0223     return ret;
0224 }
0225 
0226 static int ping_read_raw(struct iio_dev *indio_dev,
0227                 struct iio_chan_spec const *channel, int *val,
0228                 int *val2, long info)
0229 {
0230     int ret;
0231 
0232     if (channel->type != IIO_DISTANCE)
0233         return -EINVAL;
0234 
0235     switch (info) {
0236     case IIO_CHAN_INFO_RAW:
0237         ret = ping_read(indio_dev);
0238         if (ret < 0)
0239             return ret;
0240         *val = ret;
0241         return IIO_VAL_INT;
0242     case IIO_CHAN_INFO_SCALE:
0243         /*
0244          * maximum resolution in datasheet is 1 mm
0245          * 1 LSB is 1 mm
0246          */
0247         *val = 0;
0248         *val2 = 1000;
0249         return IIO_VAL_INT_PLUS_MICRO;
0250     default:
0251         return -EINVAL;
0252     }
0253 }
0254 
0255 static const struct iio_info ping_iio_info = {
0256     .read_raw       = ping_read_raw,
0257 };
0258 
0259 static const struct iio_chan_spec ping_chan_spec[] = {
0260     {
0261         .type = IIO_DISTANCE,
0262         .info_mask_separate =
0263                 BIT(IIO_CHAN_INFO_RAW) |
0264                 BIT(IIO_CHAN_INFO_SCALE),
0265     },
0266 };
0267 
0268 static const struct of_device_id of_ping_match[] = {
0269     { .compatible = "parallax,ping", .data = &pa_ping_cfg },
0270     { .compatible = "parallax,laserping", .data = &pa_laser_ping_cfg },
0271     {},
0272 };
0273 
0274 MODULE_DEVICE_TABLE(of, of_ping_match);
0275 
0276 static int ping_probe(struct platform_device *pdev)
0277 {
0278     struct device *dev = &pdev->dev;
0279     struct ping_data *data;
0280     struct iio_dev *indio_dev;
0281 
0282     indio_dev = devm_iio_device_alloc(dev, sizeof(struct ping_data));
0283     if (!indio_dev) {
0284         dev_err(dev, "failed to allocate IIO device\n");
0285         return -ENOMEM;
0286     }
0287 
0288     data = iio_priv(indio_dev);
0289     data->dev = dev;
0290     data->cfg = device_get_match_data(dev);
0291 
0292     mutex_init(&data->lock);
0293     init_completion(&data->rising);
0294     init_completion(&data->falling);
0295 
0296     data->gpiod_ping = devm_gpiod_get(dev, "ping", GPIOD_OUT_LOW);
0297     if (IS_ERR(data->gpiod_ping)) {
0298         dev_err(dev, "failed to get ping-gpios: err=%ld\n",
0299                         PTR_ERR(data->gpiod_ping));
0300         return PTR_ERR(data->gpiod_ping);
0301     }
0302 
0303     if (gpiod_cansleep(data->gpiod_ping)) {
0304         dev_err(data->dev, "cansleep-GPIOs not supported\n");
0305         return -ENODEV;
0306     }
0307 
0308     platform_set_drvdata(pdev, indio_dev);
0309 
0310     indio_dev->name = "ping";
0311     indio_dev->info = &ping_iio_info;
0312     indio_dev->modes = INDIO_DIRECT_MODE;
0313     indio_dev->channels = ping_chan_spec;
0314     indio_dev->num_channels = ARRAY_SIZE(ping_chan_spec);
0315 
0316     return devm_iio_device_register(dev, indio_dev);
0317 }
0318 
0319 static struct platform_driver ping_driver = {
0320     .probe      = ping_probe,
0321     .driver     = {
0322         .name       = "ping-gpio",
0323         .of_match_table = of_ping_match,
0324     },
0325 };
0326 
0327 module_platform_driver(ping_driver);
0328 
0329 MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
0330 MODULE_DESCRIPTION("PING sensors for distance measuring using one GPIOs");
0331 MODULE_LICENSE("GPL");
0332 MODULE_ALIAS("platform:ping");