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

veml6070.c (4982B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * veml6070.c - Support for Vishay VEML6070 UV A light sensor
      4 *
      5 * Copyright 2016 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
      6 *
      7 * IIO driver for VEML6070 (7-bit I2C slave addresses 0x38 and 0x39)
      8 *
      9 * TODO: integration time, ACK signal
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/i2c.h>
     14#include <linux/mutex.h>
     15#include <linux/err.h>
     16#include <linux/delay.h>
     17
     18#include <linux/iio/iio.h>
     19#include <linux/iio/sysfs.h>
     20
     21#define VEML6070_DRV_NAME "veml6070"
     22
     23#define VEML6070_ADDR_CONFIG_DATA_MSB 0x38 /* read: MSB data, write: config */
     24#define VEML6070_ADDR_DATA_LSB	0x39 /* LSB data */
     25
     26#define VEML6070_COMMAND_ACK	BIT(5) /* raise interrupt when over threshold */
     27#define VEML6070_COMMAND_IT	GENMASK(3, 2) /* bit mask integration time */
     28#define VEML6070_COMMAND_RSRVD	BIT(1) /* reserved, set to 1 */
     29#define VEML6070_COMMAND_SD	BIT(0) /* shutdown mode when set */
     30
     31#define VEML6070_IT_10	0x04 /* integration time 1x */
     32
     33struct veml6070_data {
     34	struct i2c_client *client1;
     35	struct i2c_client *client2;
     36	u8 config;
     37	struct mutex lock;
     38};
     39
     40static int veml6070_read(struct veml6070_data *data)
     41{
     42	int ret;
     43	u8 msb, lsb;
     44
     45	mutex_lock(&data->lock);
     46
     47	/* disable shutdown */
     48	ret = i2c_smbus_write_byte(data->client1,
     49	    data->config & ~VEML6070_COMMAND_SD);
     50	if (ret < 0)
     51		goto out;
     52
     53	msleep(125 + 10); /* measurement takes up to 125 ms for IT 1x */
     54
     55	ret = i2c_smbus_read_byte(data->client2); /* read MSB, address 0x39 */
     56	if (ret < 0)
     57		goto out;
     58	msb = ret;
     59
     60	ret = i2c_smbus_read_byte(data->client1); /* read LSB, address 0x38 */
     61	if (ret < 0)
     62		goto out;
     63	lsb = ret;
     64
     65	/* shutdown again */
     66	ret = i2c_smbus_write_byte(data->client1, data->config);
     67	if (ret < 0)
     68		goto out;
     69
     70	ret = (msb << 8) | lsb;
     71
     72out:
     73	mutex_unlock(&data->lock);
     74	return ret;
     75}
     76
     77static const struct iio_chan_spec veml6070_channels[] = {
     78	{
     79		.type = IIO_INTENSITY,
     80		.modified = 1,
     81		.channel2 = IIO_MOD_LIGHT_UV,
     82		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
     83	},
     84	{
     85		.type = IIO_UVINDEX,
     86		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
     87	}
     88};
     89
     90static int veml6070_to_uv_index(unsigned val)
     91{
     92	/*
     93	 * conversion of raw UV intensity values to UV index depends on
     94	 * integration time (IT) and value of the resistor connected to
     95	 * the RSET pin (default: 270 KOhm)
     96	 */
     97	unsigned uvi[11] = {
     98		187, 373, 560, /* low */
     99		746, 933, 1120, /* moderate */
    100		1308, 1494, /* high */
    101		1681, 1868, 2054}; /* very high */
    102	int i;
    103
    104	for (i = 0; i < ARRAY_SIZE(uvi); i++)
    105		if (val <= uvi[i])
    106			return i;
    107
    108	return 11; /* extreme */
    109}
    110
    111static int veml6070_read_raw(struct iio_dev *indio_dev,
    112				struct iio_chan_spec const *chan,
    113				int *val, int *val2, long mask)
    114{
    115	struct veml6070_data *data = iio_priv(indio_dev);
    116	int ret;
    117
    118	switch (mask) {
    119	case IIO_CHAN_INFO_RAW:
    120	case IIO_CHAN_INFO_PROCESSED:
    121		ret = veml6070_read(data);
    122		if (ret < 0)
    123			return ret;
    124		if (mask == IIO_CHAN_INFO_PROCESSED)
    125			*val = veml6070_to_uv_index(ret);
    126		else
    127			*val = ret;
    128		return IIO_VAL_INT;
    129	default:
    130		return -EINVAL;
    131	}
    132}
    133
    134static const struct iio_info veml6070_info = {
    135	.read_raw = veml6070_read_raw,
    136};
    137
    138static int veml6070_probe(struct i2c_client *client,
    139			  const struct i2c_device_id *id)
    140{
    141	struct veml6070_data *data;
    142	struct iio_dev *indio_dev;
    143	int ret;
    144
    145	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    146	if (!indio_dev)
    147		return -ENOMEM;
    148
    149	data = iio_priv(indio_dev);
    150	i2c_set_clientdata(client, indio_dev);
    151	data->client1 = client;
    152	mutex_init(&data->lock);
    153
    154	indio_dev->info = &veml6070_info;
    155	indio_dev->channels = veml6070_channels;
    156	indio_dev->num_channels = ARRAY_SIZE(veml6070_channels);
    157	indio_dev->name = VEML6070_DRV_NAME;
    158	indio_dev->modes = INDIO_DIRECT_MODE;
    159
    160	data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB);
    161	if (IS_ERR(data->client2)) {
    162		dev_err(&client->dev, "i2c device for second chip address failed\n");
    163		return PTR_ERR(data->client2);
    164	}
    165
    166	data->config = VEML6070_IT_10 | VEML6070_COMMAND_RSRVD |
    167		VEML6070_COMMAND_SD;
    168	ret = i2c_smbus_write_byte(data->client1, data->config);
    169	if (ret < 0)
    170		goto fail;
    171
    172	ret = iio_device_register(indio_dev);
    173	if (ret < 0)
    174		goto fail;
    175
    176	return ret;
    177
    178fail:
    179	i2c_unregister_device(data->client2);
    180	return ret;
    181}
    182
    183static int veml6070_remove(struct i2c_client *client)
    184{
    185	struct iio_dev *indio_dev = i2c_get_clientdata(client);
    186	struct veml6070_data *data = iio_priv(indio_dev);
    187
    188	iio_device_unregister(indio_dev);
    189	i2c_unregister_device(data->client2);
    190
    191	return 0;
    192}
    193
    194static const struct i2c_device_id veml6070_id[] = {
    195	{ "veml6070", 0 },
    196	{ }
    197};
    198MODULE_DEVICE_TABLE(i2c, veml6070_id);
    199
    200static struct i2c_driver veml6070_driver = {
    201	.driver = {
    202		.name   = VEML6070_DRV_NAME,
    203	},
    204	.probe  = veml6070_probe,
    205	.remove  = veml6070_remove,
    206	.id_table = veml6070_id,
    207};
    208
    209module_i2c_driver(veml6070_driver);
    210
    211MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
    212MODULE_DESCRIPTION("Vishay VEML6070 UV A light sensor driver");
    213MODULE_LICENSE("GPL");