cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ping.c (8766B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * PING: ultrasonic sensor for distance measuring by using only one GPIOs
      4 *
      5 * Copyright (c) 2019 Andreas Klinger <ak@it-klinger.de>
      6 *
      7 * For details about the devices see:
      8 * http://parallax.com/sites/default/files/downloads/28041-LaserPING-2m-Rangefinder-Guide.pdf
      9 * http://parallax.com/sites/default/files/downloads/28015-PING-Documentation-v1.6.pdf
     10 *
     11 * the measurement cycle as timing diagram looks like:
     12 *
     13 * GPIO      ___              ________________________
     14 * ping:  __/   \____________/                        \________________
     15 *          ^   ^            ^                        ^
     16 *          |<->|            interrupt                interrupt
     17 *         udelay(5)         (ts_rising)              (ts_falling)
     18 *                           |<---------------------->|
     19 *                           .  pulse time measured   .
     20 *                           .  --> one round trip of ultra sonic waves
     21 * ultra                     .                        .
     22 * sonic            _   _   _.                        .
     23 * burst: _________/ \_/ \_/ \_________________________________________
     24 *                                                    .
     25 * ultra                                              .
     26 * sonic                                     _   _   _.
     27 * echo:  __________________________________/ \_/ \_/ \________________
     28 */
     29#include <linux/err.h>
     30#include <linux/gpio/consumer.h>
     31#include <linux/kernel.h>
     32#include <linux/mod_devicetable.h>
     33#include <linux/module.h>
     34#include <linux/platform_device.h>
     35#include <linux/property.h>
     36#include <linux/sched.h>
     37#include <linux/interrupt.h>
     38#include <linux/delay.h>
     39#include <linux/iio/iio.h>
     40#include <linux/iio/sysfs.h>
     41
     42struct ping_cfg {
     43	unsigned long	trigger_pulse_us;	/* length of trigger pulse */
     44	int		laserping_error;	/* support error code in */
     45						/*   pulse width of laser */
     46						/*   ping sensors */
     47	s64		timeout_ns;		/* timeout in ns */
     48};
     49
     50struct ping_data {
     51	struct device		*dev;
     52	struct gpio_desc	*gpiod_ping;
     53	struct mutex		lock;
     54	int			irqnr;
     55	ktime_t			ts_rising;
     56	ktime_t			ts_falling;
     57	struct completion	rising;
     58	struct completion	falling;
     59	const struct ping_cfg	*cfg;
     60};
     61
     62static const struct ping_cfg pa_ping_cfg = {
     63	.trigger_pulse_us	= 5,
     64	.laserping_error	= 0,
     65	.timeout_ns		= 18500000,	/* 3 meters */
     66};
     67
     68static const struct ping_cfg pa_laser_ping_cfg = {
     69	.trigger_pulse_us	= 5,
     70	.laserping_error	= 1,
     71	.timeout_ns		= 15500000,	/* 2 meters plus error codes */
     72};
     73
     74static irqreturn_t ping_handle_irq(int irq, void *dev_id)
     75{
     76	struct iio_dev *indio_dev = dev_id;
     77	struct ping_data *data = iio_priv(indio_dev);
     78	ktime_t now = ktime_get();
     79
     80	if (gpiod_get_value(data->gpiod_ping)) {
     81		data->ts_rising = now;
     82		complete(&data->rising);
     83	} else {
     84		data->ts_falling = now;
     85		complete(&data->falling);
     86	}
     87
     88	return IRQ_HANDLED;
     89}
     90
     91static int ping_read(struct iio_dev *indio_dev)
     92{
     93	struct ping_data *data = iio_priv(indio_dev);
     94	int ret;
     95	ktime_t ktime_dt;
     96	s64 dt_ns;
     97	u32 time_ns, distance_mm;
     98	struct platform_device *pdev = to_platform_device(data->dev);
     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_ping, 1);
    110	udelay(data->cfg->trigger_pulse_us);
    111	gpiod_set_value(data->gpiod_ping, 0);
    112
    113	ret = gpiod_direction_input(data->gpiod_ping);
    114	if (ret < 0) {
    115		mutex_unlock(&data->lock);
    116		return ret;
    117	}
    118
    119	data->irqnr = gpiod_to_irq(data->gpiod_ping);
    120	if (data->irqnr < 0) {
    121		dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr);
    122		mutex_unlock(&data->lock);
    123		return data->irqnr;
    124	}
    125
    126	ret = request_irq(data->irqnr, ping_handle_irq,
    127				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
    128							pdev->name, indio_dev);
    129	if (ret < 0) {
    130		dev_err(data->dev, "request_irq: %d\n", ret);
    131		mutex_unlock(&data->lock);
    132		return ret;
    133	}
    134
    135	/* it should not take more than 20 ms until echo is rising */
    136	ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
    137	if (ret < 0)
    138		goto err_reset_direction;
    139	else if (ret == 0) {
    140		ret = -ETIMEDOUT;
    141		goto err_reset_direction;
    142	}
    143
    144	/* it cannot take more than 50 ms until echo is falling */
    145	ret = wait_for_completion_killable_timeout(&data->falling, HZ/20);
    146	if (ret < 0)
    147		goto err_reset_direction;
    148	else if (ret == 0) {
    149		ret = -ETIMEDOUT;
    150		goto err_reset_direction;
    151	}
    152
    153	ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);
    154
    155	free_irq(data->irqnr, indio_dev);
    156
    157	ret = gpiod_direction_output(data->gpiod_ping, GPIOD_OUT_LOW);
    158	if (ret < 0) {
    159		mutex_unlock(&data->lock);
    160		return ret;
    161	}
    162
    163	mutex_unlock(&data->lock);
    164
    165	dt_ns = ktime_to_ns(ktime_dt);
    166	if (dt_ns > data->cfg->timeout_ns) {
    167		dev_dbg(data->dev, "distance out of range: dt=%lldns\n",
    168								dt_ns);
    169		return -EIO;
    170	}
    171
    172	time_ns = dt_ns;
    173
    174	/*
    175	 * read error code of laser ping sensor and give users chance to
    176	 * figure out error by using dynamic debuggging
    177	 */
    178	if (data->cfg->laserping_error) {
    179		if ((time_ns > 12500000) && (time_ns <= 13500000)) {
    180			dev_dbg(data->dev, "target too close or to far\n");
    181			return -EIO;
    182		}
    183		if ((time_ns > 13500000) && (time_ns <= 14500000)) {
    184			dev_dbg(data->dev, "internal sensor error\n");
    185			return -EIO;
    186		}
    187		if ((time_ns > 14500000) && (time_ns <= 15500000)) {
    188			dev_dbg(data->dev, "internal sensor timeout\n");
    189			return -EIO;
    190		}
    191	}
    192
    193	/*
    194	 * the speed as function of the temperature is approximately:
    195	 *
    196	 * speed = 331,5 + 0,6 * Temp
    197	 *   with Temp in °C
    198	 *   and speed in m/s
    199	 *
    200	 * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the
    201	 * temperature
    202	 *
    203	 * therefore:
    204	 *             time     343,5     time * 232
    205	 * distance = ------ * ------- = ------------
    206	 *             10^6         2        1350800
    207	 *   with time in ns
    208	 *   and distance in mm (one way)
    209	 *
    210	 * because we limit to 3 meters the multiplication with 232 just
    211	 * fits into 32 bit
    212	 */
    213	distance_mm = time_ns * 232 / 1350800;
    214
    215	return distance_mm;
    216
    217err_reset_direction:
    218	free_irq(data->irqnr, indio_dev);
    219	mutex_unlock(&data->lock);
    220
    221	if (gpiod_direction_output(data->gpiod_ping, GPIOD_OUT_LOW))
    222		dev_dbg(data->dev, "error in gpiod_direction_output\n");
    223	return ret;
    224}
    225
    226static int ping_read_raw(struct iio_dev *indio_dev,
    227			    struct iio_chan_spec const *channel, int *val,
    228			    int *val2, long info)
    229{
    230	int ret;
    231
    232	if (channel->type != IIO_DISTANCE)
    233		return -EINVAL;
    234
    235	switch (info) {
    236	case IIO_CHAN_INFO_RAW:
    237		ret = ping_read(indio_dev);
    238		if (ret < 0)
    239			return ret;
    240		*val = ret;
    241		return IIO_VAL_INT;
    242	case IIO_CHAN_INFO_SCALE:
    243		/*
    244		 * maximum resolution in datasheet is 1 mm
    245		 * 1 LSB is 1 mm
    246		 */
    247		*val = 0;
    248		*val2 = 1000;
    249		return IIO_VAL_INT_PLUS_MICRO;
    250	default:
    251		return -EINVAL;
    252	}
    253}
    254
    255static const struct iio_info ping_iio_info = {
    256	.read_raw		= ping_read_raw,
    257};
    258
    259static const struct iio_chan_spec ping_chan_spec[] = {
    260	{
    261		.type = IIO_DISTANCE,
    262		.info_mask_separate =
    263				BIT(IIO_CHAN_INFO_RAW) |
    264				BIT(IIO_CHAN_INFO_SCALE),
    265	},
    266};
    267
    268static const struct of_device_id of_ping_match[] = {
    269	{ .compatible = "parallax,ping", .data = &pa_ping_cfg },
    270	{ .compatible = "parallax,laserping", .data = &pa_laser_ping_cfg },
    271	{},
    272};
    273
    274MODULE_DEVICE_TABLE(of, of_ping_match);
    275
    276static int ping_probe(struct platform_device *pdev)
    277{
    278	struct device *dev = &pdev->dev;
    279	struct ping_data *data;
    280	struct iio_dev *indio_dev;
    281
    282	indio_dev = devm_iio_device_alloc(dev, sizeof(struct ping_data));
    283	if (!indio_dev) {
    284		dev_err(dev, "failed to allocate IIO device\n");
    285		return -ENOMEM;
    286	}
    287
    288	data = iio_priv(indio_dev);
    289	data->dev = dev;
    290	data->cfg = device_get_match_data(dev);
    291
    292	mutex_init(&data->lock);
    293	init_completion(&data->rising);
    294	init_completion(&data->falling);
    295
    296	data->gpiod_ping = devm_gpiod_get(dev, "ping", GPIOD_OUT_LOW);
    297	if (IS_ERR(data->gpiod_ping)) {
    298		dev_err(dev, "failed to get ping-gpios: err=%ld\n",
    299						PTR_ERR(data->gpiod_ping));
    300		return PTR_ERR(data->gpiod_ping);
    301	}
    302
    303	if (gpiod_cansleep(data->gpiod_ping)) {
    304		dev_err(data->dev, "cansleep-GPIOs not supported\n");
    305		return -ENODEV;
    306	}
    307
    308	platform_set_drvdata(pdev, indio_dev);
    309
    310	indio_dev->name = "ping";
    311	indio_dev->info = &ping_iio_info;
    312	indio_dev->modes = INDIO_DIRECT_MODE;
    313	indio_dev->channels = ping_chan_spec;
    314	indio_dev->num_channels = ARRAY_SIZE(ping_chan_spec);
    315
    316	return devm_iio_device_register(dev, indio_dev);
    317}
    318
    319static struct platform_driver ping_driver = {
    320	.probe		= ping_probe,
    321	.driver		= {
    322		.name		= "ping-gpio",
    323		.of_match_table	= of_ping_match,
    324	},
    325};
    326
    327module_platform_driver(ping_driver);
    328
    329MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
    330MODULE_DESCRIPTION("PING sensors for distance measuring using one GPIOs");
    331MODULE_LICENSE("GPL");
    332MODULE_ALIAS("platform:ping");