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

srf04.c (10625B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * SRF04: ultrasonic sensor for distance measuring by using GPIOs
      4 *
      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.htm
      9 *
     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                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>
     42#include <linux/of_device.h>
     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/pm_runtime.h>
     49#include <linux/iio/iio.h>
     50#include <linux/iio/sysfs.h>
     51
     52struct srf04_cfg {
     53	unsigned long trigger_pulse_us;
     54};
     55
     56struct srf04_data {
     57	struct device		*dev;
     58	struct gpio_desc	*gpiod_trig;
     59	struct gpio_desc	*gpiod_echo;
     60	struct gpio_desc	*gpiod_power;
     61	struct mutex		lock;
     62	int			irqnr;
     63	ktime_t			ts_rising;
     64	ktime_t			ts_falling;
     65	struct completion	rising;
     66	struct completion	falling;
     67	const struct srf04_cfg	*cfg;
     68	int			startup_time_ms;
     69};
     70
     71static const struct srf04_cfg srf04_cfg = {
     72	.trigger_pulse_us = 10,
     73};
     74
     75static const struct srf04_cfg mb_lv_cfg = {
     76	.trigger_pulse_us = 20,
     77};
     78
     79static irqreturn_t srf04_handle_irq(int irq, void *dev_id)
     80{
     81	struct iio_dev *indio_dev = dev_id;
     82	struct srf04_data *data = iio_priv(indio_dev);
     83	ktime_t now = ktime_get();
     84
     85	if (gpiod_get_value(data->gpiod_echo)) {
     86		data->ts_rising = now;
     87		complete(&data->rising);
     88	} else {
     89		data->ts_falling = now;
     90		complete(&data->falling);
     91	}
     92
     93	return IRQ_HANDLED;
     94}
     95
     96static int srf04_read(struct srf04_data *data)
     97{
     98	int ret;
     99	ktime_t ktime_dt;
    100	u64 dt_ns;
    101	u32 time_ns, distance_mm;
    102
    103	if (data->gpiod_power) {
    104		ret = pm_runtime_resume_and_get(data->dev);
    105		if (ret < 0)
    106			return ret;
    107	}
    108	/*
    109	 * just one read-echo-cycle can take place at a time
    110	 * ==> lock against concurrent reading calls
    111	 */
    112	mutex_lock(&data->lock);
    113
    114	reinit_completion(&data->rising);
    115	reinit_completion(&data->falling);
    116
    117	gpiod_set_value(data->gpiod_trig, 1);
    118	udelay(data->cfg->trigger_pulse_us);
    119	gpiod_set_value(data->gpiod_trig, 0);
    120
    121	if (data->gpiod_power) {
    122		pm_runtime_mark_last_busy(data->dev);
    123		pm_runtime_put_autosuspend(data->dev);
    124	}
    125
    126	/* it should not take more than 20 ms until echo is rising */
    127	ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
    128	if (ret < 0) {
    129		mutex_unlock(&data->lock);
    130		return ret;
    131	} else if (ret == 0) {
    132		mutex_unlock(&data->lock);
    133		return -ETIMEDOUT;
    134	}
    135
    136	/* it cannot take more than 50 ms until echo is falling */
    137	ret = wait_for_completion_killable_timeout(&data->falling, HZ/20);
    138	if (ret < 0) {
    139		mutex_unlock(&data->lock);
    140		return ret;
    141	} else if (ret == 0) {
    142		mutex_unlock(&data->lock);
    143		return -ETIMEDOUT;
    144	}
    145
    146	ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);
    147
    148	mutex_unlock(&data->lock);
    149
    150	dt_ns = ktime_to_ns(ktime_dt);
    151	/*
    152	 * measuring more than 6,45 meters is beyond the capabilities of
    153	 * the supported sensors
    154	 * ==> filter out invalid results for not measuring echos of
    155	 *     another us sensor
    156	 *
    157	 * formula:
    158	 *         distance     6,45 * 2 m
    159	 * time = ---------- = ------------ = 40438871 ns
    160	 *          speed         319 m/s
    161	 *
    162	 * using a minimum speed at -20 °C of 319 m/s
    163	 */
    164	if (dt_ns > 40438871)
    165		return -EIO;
    166
    167	time_ns = dt_ns;
    168
    169	/*
    170	 * the speed as function of the temperature is approximately:
    171	 *
    172	 * speed = 331,5 + 0,6 * Temp
    173	 *   with Temp in °C
    174	 *   and speed in m/s
    175	 *
    176	 * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the
    177	 * temperature
    178	 *
    179	 * therefore:
    180	 *             time     343,5     time * 106
    181	 * distance = ------ * ------- = ------------
    182	 *             10^6         2         617176
    183	 *   with time in ns
    184	 *   and distance in mm (one way)
    185	 *
    186	 * because we limit to 6,45 meters the multiplication with 106 just
    187	 * fits into 32 bit
    188	 */
    189	distance_mm = time_ns * 106 / 617176;
    190
    191	return distance_mm;
    192}
    193
    194static int srf04_read_raw(struct iio_dev *indio_dev,
    195			    struct iio_chan_spec const *channel, int *val,
    196			    int *val2, long info)
    197{
    198	struct srf04_data *data = iio_priv(indio_dev);
    199	int ret;
    200
    201	if (channel->type != IIO_DISTANCE)
    202		return -EINVAL;
    203
    204	switch (info) {
    205	case IIO_CHAN_INFO_RAW:
    206		ret = srf04_read(data);
    207		if (ret < 0)
    208			return ret;
    209		*val = ret;
    210		return IIO_VAL_INT;
    211	case IIO_CHAN_INFO_SCALE:
    212		/*
    213		 * theoretical maximum resolution is 3 mm
    214		 * 1 LSB is 1 mm
    215		 */
    216		*val = 0;
    217		*val2 = 1000;
    218		return IIO_VAL_INT_PLUS_MICRO;
    219	default:
    220		return -EINVAL;
    221	}
    222}
    223
    224static const struct iio_info srf04_iio_info = {
    225	.read_raw		= srf04_read_raw,
    226};
    227
    228static const struct iio_chan_spec srf04_chan_spec[] = {
    229	{
    230		.type = IIO_DISTANCE,
    231		.info_mask_separate =
    232				BIT(IIO_CHAN_INFO_RAW) |
    233				BIT(IIO_CHAN_INFO_SCALE),
    234	},
    235};
    236
    237static const struct of_device_id of_srf04_match[] = {
    238	{ .compatible = "devantech,srf04", .data = &srf04_cfg },
    239	{ .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg },
    240	{ .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg },
    241	{ .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg },
    242	{ .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg },
    243	{ .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg },
    244	{},
    245};
    246
    247MODULE_DEVICE_TABLE(of, of_srf04_match);
    248
    249static int srf04_probe(struct platform_device *pdev)
    250{
    251	struct device *dev = &pdev->dev;
    252	struct srf04_data *data;
    253	struct iio_dev *indio_dev;
    254	int ret;
    255
    256	indio_dev = devm_iio_device_alloc(dev, sizeof(struct srf04_data));
    257	if (!indio_dev) {
    258		dev_err(dev, "failed to allocate IIO device\n");
    259		return -ENOMEM;
    260	}
    261
    262	data = iio_priv(indio_dev);
    263	data->dev = dev;
    264	data->cfg = of_match_device(of_srf04_match, dev)->data;
    265
    266	mutex_init(&data->lock);
    267	init_completion(&data->rising);
    268	init_completion(&data->falling);
    269
    270	data->gpiod_trig = devm_gpiod_get(dev, "trig", GPIOD_OUT_LOW);
    271	if (IS_ERR(data->gpiod_trig)) {
    272		dev_err(dev, "failed to get trig-gpios: err=%ld\n",
    273					PTR_ERR(data->gpiod_trig));
    274		return PTR_ERR(data->gpiod_trig);
    275	}
    276
    277	data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN);
    278	if (IS_ERR(data->gpiod_echo)) {
    279		dev_err(dev, "failed to get echo-gpios: err=%ld\n",
    280					PTR_ERR(data->gpiod_echo));
    281		return PTR_ERR(data->gpiod_echo);
    282	}
    283
    284	data->gpiod_power = devm_gpiod_get_optional(dev, "power",
    285								GPIOD_OUT_LOW);
    286	if (IS_ERR(data->gpiod_power)) {
    287		dev_err(dev, "failed to get power-gpios: err=%ld\n",
    288						PTR_ERR(data->gpiod_power));
    289		return PTR_ERR(data->gpiod_power);
    290	}
    291	if (data->gpiod_power) {
    292
    293		if (of_property_read_u32(dev->of_node, "startup-time-ms",
    294						&data->startup_time_ms))
    295			data->startup_time_ms = 100;
    296		dev_dbg(dev, "using power gpio: startup-time-ms=%d\n",
    297							data->startup_time_ms);
    298	}
    299
    300	if (gpiod_cansleep(data->gpiod_echo)) {
    301		dev_err(data->dev, "cansleep-GPIOs not supported\n");
    302		return -ENODEV;
    303	}
    304
    305	data->irqnr = gpiod_to_irq(data->gpiod_echo);
    306	if (data->irqnr < 0) {
    307		dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr);
    308		return data->irqnr;
    309	}
    310
    311	ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq,
    312			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
    313			pdev->name, indio_dev);
    314	if (ret < 0) {
    315		dev_err(data->dev, "request_irq: %d\n", ret);
    316		return ret;
    317	}
    318
    319	platform_set_drvdata(pdev, indio_dev);
    320
    321	indio_dev->name = "srf04";
    322	indio_dev->info = &srf04_iio_info;
    323	indio_dev->modes = INDIO_DIRECT_MODE;
    324	indio_dev->channels = srf04_chan_spec;
    325	indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec);
    326
    327	ret = iio_device_register(indio_dev);
    328	if (ret < 0) {
    329		dev_err(data->dev, "iio_device_register: %d\n", ret);
    330		return ret;
    331	}
    332
    333	if (data->gpiod_power) {
    334		pm_runtime_set_autosuspend_delay(data->dev, 1000);
    335		pm_runtime_use_autosuspend(data->dev);
    336
    337		ret = pm_runtime_set_active(data->dev);
    338		if (ret) {
    339			dev_err(data->dev, "pm_runtime_set_active: %d\n", ret);
    340			iio_device_unregister(indio_dev);
    341		}
    342
    343		pm_runtime_enable(data->dev);
    344		pm_runtime_idle(data->dev);
    345	}
    346
    347	return ret;
    348}
    349
    350static int srf04_remove(struct platform_device *pdev)
    351{
    352	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
    353	struct srf04_data *data = iio_priv(indio_dev);
    354
    355	iio_device_unregister(indio_dev);
    356
    357	if (data->gpiod_power) {
    358		pm_runtime_disable(data->dev);
    359		pm_runtime_set_suspended(data->dev);
    360	}
    361
    362	return 0;
    363}
    364
    365static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev)
    366{
    367	struct platform_device *pdev = container_of(dev,
    368						struct platform_device, dev);
    369	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
    370	struct srf04_data *data = iio_priv(indio_dev);
    371
    372	gpiod_set_value(data->gpiod_power, 0);
    373
    374	return 0;
    375}
    376
    377static int __maybe_unused srf04_pm_runtime_resume(struct device *dev)
    378{
    379	struct platform_device *pdev = container_of(dev,
    380						struct platform_device, dev);
    381	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
    382	struct srf04_data *data = iio_priv(indio_dev);
    383
    384	gpiod_set_value(data->gpiod_power, 1);
    385	msleep(data->startup_time_ms);
    386
    387	return 0;
    388}
    389
    390static const struct dev_pm_ops srf04_pm_ops = {
    391	SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend,
    392				srf04_pm_runtime_resume, NULL)
    393};
    394
    395static struct platform_driver srf04_driver = {
    396	.probe		= srf04_probe,
    397	.remove		= srf04_remove,
    398	.driver		= {
    399		.name		= "srf04-gpio",
    400		.of_match_table	= of_srf04_match,
    401		.pm		= &srf04_pm_ops,
    402	},
    403};
    404
    405module_platform_driver(srf04_driver);
    406
    407MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
    408MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs");
    409MODULE_LICENSE("GPL");
    410MODULE_ALIAS("platform:srf04");