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

pulsedlight-lidar-lite-v2.c (8606B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
      4 *
      5 * Copyright (C) 2015, 2017-2018
      6 * Author: Matt Ranostay <matt.ranostay@konsulko.com>
      7 *
      8 * TODO: interrupt mode, and signal strength reporting
      9 */
     10
     11#include <linux/err.h>
     12#include <linux/init.h>
     13#include <linux/i2c.h>
     14#include <linux/delay.h>
     15#include <linux/module.h>
     16#include <linux/mod_devicetable.h>
     17#include <linux/pm_runtime.h>
     18#include <linux/iio/iio.h>
     19#include <linux/iio/sysfs.h>
     20#include <linux/iio/buffer.h>
     21#include <linux/iio/trigger.h>
     22#include <linux/iio/triggered_buffer.h>
     23#include <linux/iio/trigger_consumer.h>
     24
     25#define LIDAR_REG_CONTROL		0x00
     26#define LIDAR_REG_CONTROL_ACQUIRE	BIT(2)
     27
     28#define LIDAR_REG_STATUS		0x01
     29#define LIDAR_REG_STATUS_INVALID	BIT(3)
     30#define LIDAR_REG_STATUS_READY		BIT(0)
     31
     32#define LIDAR_REG_DATA_HBYTE		0x0f
     33#define LIDAR_REG_DATA_LBYTE		0x10
     34#define LIDAR_REG_DATA_WORD_READ	BIT(7)
     35
     36#define LIDAR_REG_PWR_CONTROL	0x65
     37
     38#define LIDAR_DRV_NAME "lidar"
     39
     40struct lidar_data {
     41	struct iio_dev *indio_dev;
     42	struct i2c_client *client;
     43
     44	int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len);
     45	int i2c_enabled;
     46
     47	/* Ensure timestamp is naturally aligned */
     48	struct {
     49		u16 chan;
     50		s64 timestamp __aligned(8);
     51	} scan;
     52};
     53
     54static const struct iio_chan_spec lidar_channels[] = {
     55	{
     56		.type = IIO_DISTANCE,
     57		.info_mask_separate =
     58			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
     59		.scan_index = 0,
     60		.scan_type = {
     61			.sign = 'u',
     62			.realbits = 16,
     63			.storagebits = 16,
     64		},
     65	},
     66	IIO_CHAN_SOFT_TIMESTAMP(1),
     67};
     68
     69static int lidar_i2c_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
     70{
     71	struct i2c_client *client = data->client;
     72	struct i2c_msg msg[2];
     73	int ret;
     74
     75	msg[0].addr = client->addr;
     76	msg[0].flags = client->flags | I2C_M_STOP;
     77	msg[0].len = 1;
     78	msg[0].buf  = (char *) &reg;
     79
     80	msg[1].addr = client->addr;
     81	msg[1].flags = client->flags | I2C_M_RD;
     82	msg[1].len = len;
     83	msg[1].buf = (char *) val;
     84
     85	ret = i2c_transfer(client->adapter, msg, 2);
     86
     87	return (ret == 2) ? 0 : -EIO;
     88}
     89
     90static int lidar_smbus_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
     91{
     92	struct i2c_client *client = data->client;
     93	int ret;
     94
     95	/*
     96	 * Device needs a STOP condition between address write, and data read
     97	 * so in turn i2c_smbus_read_byte_data cannot be used
     98	 */
     99
    100	while (len--) {
    101		ret = i2c_smbus_write_byte(client, reg++);
    102		if (ret < 0) {
    103			dev_err(&client->dev, "cannot write addr value");
    104			return ret;
    105		}
    106
    107		ret = i2c_smbus_read_byte(client);
    108		if (ret < 0) {
    109			dev_err(&client->dev, "cannot read data value");
    110			return ret;
    111		}
    112
    113		*(val++) = ret;
    114	}
    115
    116	return 0;
    117}
    118
    119static int lidar_read_byte(struct lidar_data *data, u8 reg)
    120{
    121	int ret;
    122	u8 val;
    123
    124	ret = data->xfer(data, reg, &val, 1);
    125	if (ret < 0)
    126		return ret;
    127
    128	return val;
    129}
    130
    131static inline int lidar_write_control(struct lidar_data *data, int val)
    132{
    133	return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
    134}
    135
    136static inline int lidar_write_power(struct lidar_data *data, int val)
    137{
    138	return i2c_smbus_write_byte_data(data->client,
    139					 LIDAR_REG_PWR_CONTROL, val);
    140}
    141
    142static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
    143{
    144	__be16 value;
    145	int ret = data->xfer(data, LIDAR_REG_DATA_HBYTE |
    146			(data->i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0),
    147			(u8 *) &value, 2);
    148
    149	if (!ret)
    150		*reg = be16_to_cpu(value);
    151
    152	return ret;
    153}
    154
    155static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
    156{
    157	struct i2c_client *client = data->client;
    158	int tries = 10;
    159	int ret;
    160
    161	ret = pm_runtime_resume_and_get(&client->dev);
    162	if (ret < 0)
    163		return ret;
    164
    165	/* start sample */
    166	ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
    167	if (ret < 0) {
    168		dev_err(&client->dev, "cannot send start measurement command");
    169		pm_runtime_put_noidle(&client->dev);
    170		return ret;
    171	}
    172
    173	while (tries--) {
    174		usleep_range(1000, 2000);
    175
    176		ret = lidar_read_byte(data, LIDAR_REG_STATUS);
    177		if (ret < 0)
    178			break;
    179
    180		/* return -EINVAL since laser is likely pointed out of range */
    181		if (ret & LIDAR_REG_STATUS_INVALID) {
    182			*reg = 0;
    183			ret = -EINVAL;
    184			break;
    185		}
    186
    187		/* sample ready to read */
    188		if (!(ret & LIDAR_REG_STATUS_READY)) {
    189			ret = lidar_read_measurement(data, reg);
    190			break;
    191		}
    192		ret = -EIO;
    193	}
    194	pm_runtime_mark_last_busy(&client->dev);
    195	pm_runtime_put_autosuspend(&client->dev);
    196
    197	return ret;
    198}
    199
    200static int lidar_read_raw(struct iio_dev *indio_dev,
    201			  struct iio_chan_spec const *chan,
    202			  int *val, int *val2, long mask)
    203{
    204	struct lidar_data *data = iio_priv(indio_dev);
    205	int ret = -EINVAL;
    206
    207	switch (mask) {
    208	case IIO_CHAN_INFO_RAW: {
    209		u16 reg;
    210
    211		if (iio_device_claim_direct_mode(indio_dev))
    212			return -EBUSY;
    213
    214		ret = lidar_get_measurement(data, &reg);
    215		if (!ret) {
    216			*val = reg;
    217			ret = IIO_VAL_INT;
    218		}
    219		iio_device_release_direct_mode(indio_dev);
    220		break;
    221	}
    222	case IIO_CHAN_INFO_SCALE:
    223		*val = 0;
    224		*val2 = 10000;
    225		ret = IIO_VAL_INT_PLUS_MICRO;
    226		break;
    227	}
    228
    229	return ret;
    230}
    231
    232static irqreturn_t lidar_trigger_handler(int irq, void *private)
    233{
    234	struct iio_poll_func *pf = private;
    235	struct iio_dev *indio_dev = pf->indio_dev;
    236	struct lidar_data *data = iio_priv(indio_dev);
    237	int ret;
    238
    239	ret = lidar_get_measurement(data, &data->scan.chan);
    240	if (!ret) {
    241		iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
    242						   iio_get_time_ns(indio_dev));
    243	} else if (ret != -EINVAL) {
    244		dev_err(&data->client->dev, "cannot read LIDAR measurement");
    245	}
    246
    247	iio_trigger_notify_done(indio_dev->trig);
    248
    249	return IRQ_HANDLED;
    250}
    251
    252static const struct iio_info lidar_info = {
    253	.read_raw = lidar_read_raw,
    254};
    255
    256static int lidar_probe(struct i2c_client *client,
    257		       const struct i2c_device_id *id)
    258{
    259	struct lidar_data *data;
    260	struct iio_dev *indio_dev;
    261	int ret;
    262
    263	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    264	if (!indio_dev)
    265		return -ENOMEM;
    266	data = iio_priv(indio_dev);
    267
    268	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
    269		data->xfer = lidar_i2c_xfer;
    270		data->i2c_enabled = 1;
    271	} else if (i2c_check_functionality(client->adapter,
    272				I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
    273		data->xfer = lidar_smbus_xfer;
    274	else
    275		return -EOPNOTSUPP;
    276
    277	indio_dev->info = &lidar_info;
    278	indio_dev->name = LIDAR_DRV_NAME;
    279	indio_dev->channels = lidar_channels;
    280	indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
    281	indio_dev->modes = INDIO_DIRECT_MODE;
    282
    283	i2c_set_clientdata(client, indio_dev);
    284
    285	data->client = client;
    286	data->indio_dev = indio_dev;
    287
    288	ret = iio_triggered_buffer_setup(indio_dev, NULL,
    289					 lidar_trigger_handler, NULL);
    290	if (ret)
    291		return ret;
    292
    293	ret = iio_device_register(indio_dev);
    294	if (ret)
    295		goto error_unreg_buffer;
    296
    297	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
    298	pm_runtime_use_autosuspend(&client->dev);
    299
    300	ret = pm_runtime_set_active(&client->dev);
    301	if (ret)
    302		goto error_unreg_buffer;
    303	pm_runtime_enable(&client->dev);
    304	pm_runtime_idle(&client->dev);
    305
    306	return 0;
    307
    308error_unreg_buffer:
    309	iio_triggered_buffer_cleanup(indio_dev);
    310
    311	return ret;
    312}
    313
    314static int lidar_remove(struct i2c_client *client)
    315{
    316	struct iio_dev *indio_dev = i2c_get_clientdata(client);
    317
    318	iio_device_unregister(indio_dev);
    319	iio_triggered_buffer_cleanup(indio_dev);
    320
    321	pm_runtime_disable(&client->dev);
    322	pm_runtime_set_suspended(&client->dev);
    323
    324	return 0;
    325}
    326
    327static const struct i2c_device_id lidar_id[] = {
    328	{"lidar-lite-v2", 0},
    329	{"lidar-lite-v3", 0},
    330	{ },
    331};
    332MODULE_DEVICE_TABLE(i2c, lidar_id);
    333
    334static const struct of_device_id lidar_dt_ids[] = {
    335	{ .compatible = "pulsedlight,lidar-lite-v2" },
    336	{ .compatible = "grmn,lidar-lite-v3" },
    337	{ }
    338};
    339MODULE_DEVICE_TABLE(of, lidar_dt_ids);
    340
    341static int lidar_pm_runtime_suspend(struct device *dev)
    342{
    343	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
    344	struct lidar_data *data = iio_priv(indio_dev);
    345
    346	return lidar_write_power(data, 0x0f);
    347}
    348
    349static int lidar_pm_runtime_resume(struct device *dev)
    350{
    351	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
    352	struct lidar_data *data = iio_priv(indio_dev);
    353	int ret = lidar_write_power(data, 0);
    354
    355	/* regulator and FPGA needs settling time */
    356	usleep_range(15000, 20000);
    357
    358	return ret;
    359}
    360
    361static const struct dev_pm_ops lidar_pm_ops = {
    362	RUNTIME_PM_OPS(lidar_pm_runtime_suspend, lidar_pm_runtime_resume, NULL)
    363};
    364
    365static struct i2c_driver lidar_driver = {
    366	.driver = {
    367		.name	= LIDAR_DRV_NAME,
    368		.of_match_table	= lidar_dt_ids,
    369		.pm	= pm_ptr(&lidar_pm_ops),
    370	},
    371	.probe		= lidar_probe,
    372	.remove		= lidar_remove,
    373	.id_table	= lidar_id,
    374};
    375module_i2c_driver(lidar_driver);
    376
    377MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
    378MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
    379MODULE_LICENSE("GPL");