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

vl6180.c (12899B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity
      4 * sensor
      5 *
      6 * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
      7 * Copyright 2017 Manivannan Sadhasivam <manivannanece23@gmail.com>
      8 *
      9 * IIO driver for VL6180 (7-bit I2C slave address 0x29)
     10 *
     11 * Range: 0 to 100mm
     12 * ALS: < 1 Lux up to 100 kLux
     13 * IR: 850nm
     14 *
     15 * TODO: irq, threshold events, continuous mode, hardware buffer
     16 */
     17
     18#include <linux/module.h>
     19#include <linux/mod_devicetable.h>
     20#include <linux/i2c.h>
     21#include <linux/mutex.h>
     22#include <linux/err.h>
     23#include <linux/of.h>
     24#include <linux/delay.h>
     25#include <linux/util_macros.h>
     26
     27#include <linux/iio/iio.h>
     28#include <linux/iio/sysfs.h>
     29
     30#define VL6180_DRV_NAME "vl6180"
     31
     32/* Device identification register and value */
     33#define VL6180_MODEL_ID	0x000
     34#define VL6180_MODEL_ID_VAL 0xb4
     35
     36/* Configuration registers */
     37#define VL6180_INTR_CONFIG 0x014
     38#define VL6180_INTR_CLEAR 0x015
     39#define VL6180_OUT_OF_RESET 0x016
     40#define VL6180_HOLD 0x017
     41#define VL6180_RANGE_START 0x018
     42#define VL6180_ALS_START 0x038
     43#define VL6180_ALS_GAIN 0x03f
     44#define VL6180_ALS_IT 0x040
     45
     46/* Status registers */
     47#define VL6180_RANGE_STATUS 0x04d
     48#define VL6180_ALS_STATUS 0x04e
     49#define VL6180_INTR_STATUS 0x04f
     50
     51/* Result value registers */
     52#define VL6180_ALS_VALUE 0x050
     53#define VL6180_RANGE_VALUE 0x062
     54#define VL6180_RANGE_RATE 0x066
     55
     56/* bits of the RANGE_START and ALS_START register */
     57#define VL6180_MODE_CONT BIT(1) /* continuous mode */
     58#define VL6180_STARTSTOP BIT(0) /* start measurement, auto-reset */
     59
     60/* bits of the INTR_STATUS and INTR_CONFIG register */
     61#define VL6180_ALS_READY BIT(5)
     62#define VL6180_RANGE_READY BIT(2)
     63
     64/* bits of the INTR_CLEAR register */
     65#define VL6180_CLEAR_ERROR BIT(2)
     66#define VL6180_CLEAR_ALS BIT(1)
     67#define VL6180_CLEAR_RANGE BIT(0)
     68
     69/* bits of the HOLD register */
     70#define VL6180_HOLD_ON BIT(0)
     71
     72/* default value for the ALS_IT register */
     73#define VL6180_ALS_IT_100 0x63 /* 100 ms */
     74
     75/* values for the ALS_GAIN register */
     76#define VL6180_ALS_GAIN_1 0x46
     77#define VL6180_ALS_GAIN_1_25 0x45
     78#define VL6180_ALS_GAIN_1_67 0x44
     79#define VL6180_ALS_GAIN_2_5 0x43
     80#define VL6180_ALS_GAIN_5 0x42
     81#define VL6180_ALS_GAIN_10 0x41
     82#define VL6180_ALS_GAIN_20 0x40
     83#define VL6180_ALS_GAIN_40 0x47
     84
     85struct vl6180_data {
     86	struct i2c_client *client;
     87	struct mutex lock;
     88	unsigned int als_gain_milli;
     89	unsigned int als_it_ms;
     90};
     91
     92enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX };
     93
     94/**
     95 * struct vl6180_chan_regs - Registers for accessing channels
     96 * @drdy_mask:			Data ready bit in status register
     97 * @start_reg:			Conversion start register
     98 * @value_reg:			Result value register
     99 * @word:			Register word length
    100 */
    101struct vl6180_chan_regs {
    102	u8 drdy_mask;
    103	u16 start_reg, value_reg;
    104	bool word;
    105};
    106
    107static const struct vl6180_chan_regs vl6180_chan_regs_table[] = {
    108	[VL6180_ALS] = {
    109		.drdy_mask = VL6180_ALS_READY,
    110		.start_reg = VL6180_ALS_START,
    111		.value_reg = VL6180_ALS_VALUE,
    112		.word = true,
    113	},
    114	[VL6180_RANGE] = {
    115		.drdy_mask = VL6180_RANGE_READY,
    116		.start_reg = VL6180_RANGE_START,
    117		.value_reg = VL6180_RANGE_VALUE,
    118		.word = false,
    119	},
    120	[VL6180_PROX] = {
    121		.drdy_mask = VL6180_RANGE_READY,
    122		.start_reg = VL6180_RANGE_START,
    123		.value_reg = VL6180_RANGE_RATE,
    124		.word = true,
    125	},
    126};
    127
    128static int vl6180_read(struct i2c_client *client, u16 cmd, void *databuf,
    129		       u8 len)
    130{
    131	__be16 cmdbuf = cpu_to_be16(cmd);
    132	struct i2c_msg msgs[2] = {
    133		{ .addr = client->addr, .len = sizeof(cmdbuf), .buf = (u8 *) &cmdbuf },
    134		{ .addr = client->addr, .len = len, .buf = databuf,
    135		  .flags = I2C_M_RD } };
    136	int ret;
    137
    138	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
    139	if (ret < 0)
    140		dev_err(&client->dev, "failed reading register 0x%04x\n", cmd);
    141
    142	return ret;
    143}
    144
    145static int vl6180_read_byte(struct i2c_client *client, u16 cmd)
    146{
    147	u8 data;
    148	int ret;
    149
    150	ret = vl6180_read(client, cmd, &data, sizeof(data));
    151	if (ret < 0)
    152		return ret;
    153
    154	return data;
    155}
    156
    157static int vl6180_read_word(struct i2c_client *client, u16 cmd)
    158{
    159	__be16 data;
    160	int ret;
    161
    162	ret = vl6180_read(client, cmd, &data, sizeof(data));
    163	if (ret < 0)
    164		return ret;
    165
    166	return be16_to_cpu(data);
    167}
    168
    169static int vl6180_write_byte(struct i2c_client *client, u16 cmd, u8 val)
    170{
    171	u8 buf[3];
    172	struct i2c_msg msgs[1] = {
    173		{ .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } };
    174	int ret;
    175
    176	buf[0] = cmd >> 8;
    177	buf[1] = cmd & 0xff;
    178	buf[2] = val;
    179
    180	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
    181	if (ret < 0) {
    182		dev_err(&client->dev, "failed writing register 0x%04x\n", cmd);
    183		return ret;
    184	}
    185
    186	return 0;
    187}
    188
    189static int vl6180_write_word(struct i2c_client *client, u16 cmd, u16 val)
    190{
    191	__be16 buf[2];
    192	struct i2c_msg msgs[1] = {
    193		{ .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } };
    194	int ret;
    195
    196	buf[0] = cpu_to_be16(cmd);
    197	buf[1] = cpu_to_be16(val);
    198
    199	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
    200	if (ret < 0) {
    201		dev_err(&client->dev, "failed writing register 0x%04x\n", cmd);
    202		return ret;
    203	}
    204
    205	return 0;
    206}
    207
    208static int vl6180_measure(struct vl6180_data *data, int addr)
    209{
    210	struct i2c_client *client = data->client;
    211	int tries = 20, ret;
    212	u16 value;
    213
    214	mutex_lock(&data->lock);
    215	/* Start single shot measurement */
    216	ret = vl6180_write_byte(client,
    217		vl6180_chan_regs_table[addr].start_reg, VL6180_STARTSTOP);
    218	if (ret < 0)
    219		goto fail;
    220
    221	while (tries--) {
    222		ret = vl6180_read_byte(client, VL6180_INTR_STATUS);
    223		if (ret < 0)
    224			goto fail;
    225
    226		if (ret & vl6180_chan_regs_table[addr].drdy_mask)
    227			break;
    228		msleep(20);
    229	}
    230
    231	if (tries < 0) {
    232		ret = -EIO;
    233		goto fail;
    234	}
    235
    236	/* Read result value from appropriate registers */
    237	ret = vl6180_chan_regs_table[addr].word ?
    238		vl6180_read_word(client, vl6180_chan_regs_table[addr].value_reg) :
    239		vl6180_read_byte(client, vl6180_chan_regs_table[addr].value_reg);
    240	if (ret < 0)
    241		goto fail;
    242	value = ret;
    243
    244	/* Clear the interrupt flag after data read */
    245	ret = vl6180_write_byte(client, VL6180_INTR_CLEAR,
    246		VL6180_CLEAR_ERROR | VL6180_CLEAR_ALS | VL6180_CLEAR_RANGE);
    247	if (ret < 0)
    248		goto fail;
    249
    250	ret = value;
    251
    252fail:
    253	mutex_unlock(&data->lock);
    254
    255	return ret;
    256}
    257
    258static const struct iio_chan_spec vl6180_channels[] = {
    259	{
    260		.type = IIO_LIGHT,
    261		.address = VL6180_ALS,
    262		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
    263			BIT(IIO_CHAN_INFO_INT_TIME) |
    264			BIT(IIO_CHAN_INFO_SCALE) |
    265			BIT(IIO_CHAN_INFO_HARDWAREGAIN),
    266	}, {
    267		.type = IIO_DISTANCE,
    268		.address = VL6180_RANGE,
    269		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
    270			BIT(IIO_CHAN_INFO_SCALE),
    271	}, {
    272		.type = IIO_PROXIMITY,
    273		.address = VL6180_PROX,
    274		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    275	}
    276};
    277
    278/*
    279 * Available Ambient Light Sensor gain settings, 1/1000th, and
    280 * corresponding setting for the VL6180_ALS_GAIN register
    281 */
    282static const int vl6180_als_gain_tab[8] = {
    283	1000, 1250, 1670, 2500, 5000, 10000, 20000, 40000
    284};
    285static const u8 vl6180_als_gain_tab_bits[8] = {
    286	VL6180_ALS_GAIN_1,    VL6180_ALS_GAIN_1_25,
    287	VL6180_ALS_GAIN_1_67, VL6180_ALS_GAIN_2_5,
    288	VL6180_ALS_GAIN_5,    VL6180_ALS_GAIN_10,
    289	VL6180_ALS_GAIN_20,   VL6180_ALS_GAIN_40
    290};
    291
    292static int vl6180_read_raw(struct iio_dev *indio_dev,
    293				struct iio_chan_spec const *chan,
    294				int *val, int *val2, long mask)
    295{
    296	struct vl6180_data *data = iio_priv(indio_dev);
    297	int ret;
    298
    299	switch (mask) {
    300	case IIO_CHAN_INFO_RAW:
    301		ret = vl6180_measure(data, chan->address);
    302		if (ret < 0)
    303			return ret;
    304		*val = ret;
    305
    306		return IIO_VAL_INT;
    307	case IIO_CHAN_INFO_INT_TIME:
    308		*val = data->als_it_ms;
    309		*val2 = 1000;
    310
    311		return IIO_VAL_FRACTIONAL;
    312
    313	case IIO_CHAN_INFO_SCALE:
    314		switch (chan->type) {
    315		case IIO_LIGHT:
    316			/* one ALS count is 0.32 Lux @ gain 1, IT 100 ms */
    317			*val = 32000; /* 0.32 * 1000 * 100 */
    318			*val2 = data->als_gain_milli * data->als_it_ms;
    319
    320			return IIO_VAL_FRACTIONAL;
    321
    322		case IIO_DISTANCE:
    323			*val = 0; /* sensor reports mm, scale to meter */
    324			*val2 = 1000;
    325			break;
    326		default:
    327			return -EINVAL;
    328		}
    329
    330		return IIO_VAL_INT_PLUS_MICRO;
    331	case IIO_CHAN_INFO_HARDWAREGAIN:
    332		*val = data->als_gain_milli;
    333		*val2 = 1000;
    334
    335		return IIO_VAL_FRACTIONAL;
    336
    337	default:
    338		return -EINVAL;
    339	}
    340}
    341
    342static IIO_CONST_ATTR(als_gain_available, "1 1.25 1.67 2.5 5 10 20 40");
    343
    344static struct attribute *vl6180_attributes[] = {
    345	&iio_const_attr_als_gain_available.dev_attr.attr,
    346	NULL
    347};
    348
    349static const struct attribute_group vl6180_attribute_group = {
    350	.attrs = vl6180_attributes,
    351};
    352
    353/* HOLD is needed before updating any config registers */
    354static int vl6180_hold(struct vl6180_data *data, bool hold)
    355{
    356	return vl6180_write_byte(data->client, VL6180_HOLD,
    357		hold ? VL6180_HOLD_ON : 0);
    358}
    359
    360static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2)
    361{
    362	int i, ret, gain;
    363
    364	if (val < 1 || val > 40)
    365		return -EINVAL;
    366
    367	gain = (val * 1000000 + val2) / 1000;
    368	if (gain < 1 || gain > 40000)
    369		return -EINVAL;
    370
    371	i = find_closest(gain, vl6180_als_gain_tab,
    372			 ARRAY_SIZE(vl6180_als_gain_tab));
    373
    374	mutex_lock(&data->lock);
    375	ret = vl6180_hold(data, true);
    376	if (ret < 0)
    377		goto fail;
    378
    379	ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN,
    380				vl6180_als_gain_tab_bits[i]);
    381
    382	if (ret >= 0)
    383		data->als_gain_milli = vl6180_als_gain_tab[i];
    384
    385fail:
    386	vl6180_hold(data, false);
    387	mutex_unlock(&data->lock);
    388	return ret;
    389}
    390
    391static int vl6180_set_it(struct vl6180_data *data, int val, int val2)
    392{
    393	int ret, it_ms;
    394
    395	it_ms = DIV_ROUND_CLOSEST(val2, 1000); /* round to ms */
    396	if (val != 0 || it_ms < 1 || it_ms > 512)
    397		return -EINVAL;
    398
    399	mutex_lock(&data->lock);
    400	ret = vl6180_hold(data, true);
    401	if (ret < 0)
    402		goto fail;
    403
    404	ret = vl6180_write_word(data->client, VL6180_ALS_IT, it_ms - 1);
    405
    406	if (ret >= 0)
    407		data->als_it_ms = it_ms;
    408
    409fail:
    410	vl6180_hold(data, false);
    411	mutex_unlock(&data->lock);
    412
    413	return ret;
    414}
    415
    416static int vl6180_write_raw(struct iio_dev *indio_dev,
    417			     struct iio_chan_spec const *chan,
    418			     int val, int val2, long mask)
    419{
    420	struct vl6180_data *data = iio_priv(indio_dev);
    421
    422	switch (mask) {
    423	case IIO_CHAN_INFO_INT_TIME:
    424		return vl6180_set_it(data, val, val2);
    425
    426	case IIO_CHAN_INFO_HARDWAREGAIN:
    427		if (chan->type != IIO_LIGHT)
    428			return -EINVAL;
    429
    430		return vl6180_set_als_gain(data, val, val2);
    431	default:
    432		return -EINVAL;
    433	}
    434}
    435
    436static const struct iio_info vl6180_info = {
    437	.read_raw = vl6180_read_raw,
    438	.write_raw = vl6180_write_raw,
    439	.attrs = &vl6180_attribute_group,
    440};
    441
    442static int vl6180_init(struct vl6180_data *data)
    443{
    444	struct i2c_client *client = data->client;
    445	int ret;
    446
    447	ret = vl6180_read_byte(client, VL6180_MODEL_ID);
    448	if (ret < 0)
    449		return ret;
    450
    451	if (ret != VL6180_MODEL_ID_VAL) {
    452		dev_err(&client->dev, "invalid model ID %02x\n", ret);
    453		return -ENODEV;
    454	}
    455
    456	ret = vl6180_hold(data, true);
    457	if (ret < 0)
    458		return ret;
    459
    460	ret = vl6180_read_byte(client, VL6180_OUT_OF_RESET);
    461	if (ret < 0)
    462		return ret;
    463
    464	/*
    465	 * Detect false reset condition here. This bit is always set when the
    466	 * system comes out of reset.
    467	 */
    468	if (ret != 0x01)
    469		dev_info(&client->dev, "device is not fresh out of reset\n");
    470
    471	/* Enable ALS and Range ready interrupts */
    472	ret = vl6180_write_byte(client, VL6180_INTR_CONFIG,
    473				VL6180_ALS_READY | VL6180_RANGE_READY);
    474	if (ret < 0)
    475		return ret;
    476
    477	/* ALS integration time: 100ms */
    478	data->als_it_ms = 100;
    479	ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100);
    480	if (ret < 0)
    481		return ret;
    482
    483	/* ALS gain: 1 */
    484	data->als_gain_milli = 1000;
    485	ret = vl6180_write_byte(client, VL6180_ALS_GAIN, VL6180_ALS_GAIN_1);
    486	if (ret < 0)
    487		return ret;
    488
    489	ret = vl6180_write_byte(client, VL6180_OUT_OF_RESET, 0x00);
    490	if (ret < 0)
    491		return ret;
    492
    493	return vl6180_hold(data, false);
    494}
    495
    496static int vl6180_probe(struct i2c_client *client,
    497			  const struct i2c_device_id *id)
    498{
    499	struct vl6180_data *data;
    500	struct iio_dev *indio_dev;
    501	int ret;
    502
    503	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    504	if (!indio_dev)
    505		return -ENOMEM;
    506
    507	data = iio_priv(indio_dev);
    508	i2c_set_clientdata(client, indio_dev);
    509	data->client = client;
    510	mutex_init(&data->lock);
    511
    512	indio_dev->info = &vl6180_info;
    513	indio_dev->channels = vl6180_channels;
    514	indio_dev->num_channels = ARRAY_SIZE(vl6180_channels);
    515	indio_dev->name = VL6180_DRV_NAME;
    516	indio_dev->modes = INDIO_DIRECT_MODE;
    517
    518	ret = vl6180_init(data);
    519	if (ret < 0)
    520		return ret;
    521
    522	return devm_iio_device_register(&client->dev, indio_dev);
    523}
    524
    525static const struct of_device_id vl6180_of_match[] = {
    526	{ .compatible = "st,vl6180", },
    527	{ },
    528};
    529MODULE_DEVICE_TABLE(of, vl6180_of_match);
    530
    531static const struct i2c_device_id vl6180_id[] = {
    532	{ "vl6180", 0 },
    533	{ }
    534};
    535MODULE_DEVICE_TABLE(i2c, vl6180_id);
    536
    537static struct i2c_driver vl6180_driver = {
    538	.driver = {
    539		.name   = VL6180_DRV_NAME,
    540		.of_match_table = vl6180_of_match,
    541	},
    542	.probe  = vl6180_probe,
    543	.id_table = vl6180_id,
    544};
    545
    546module_i2c_driver(vl6180_driver);
    547
    548MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
    549MODULE_AUTHOR("Manivannan Sadhasivam <manivannanece23@gmail.com>");
    550MODULE_DESCRIPTION("STMicro VL6180 ALS, range and proximity sensor driver");
    551MODULE_LICENSE("GPL");