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

t5403.c (6308B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * t5403.c - Support for EPCOS T5403 pressure/temperature sensor
      4 *
      5 * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
      6 *
      7 * (7-bit I2C slave address 0x77)
      8 *
      9 * TODO: end-of-conversion irq
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/i2c.h>
     14#include <linux/iio/iio.h>
     15#include <linux/iio/sysfs.h>
     16#include <linux/delay.h>
     17
     18#define T5403_DATA 0xf5 /* data, LSB first, 16 bit */
     19#define T5403_CALIB_DATA 0x8e /* 10 calibration coeff., LSB first, 16 bit */
     20#define T5403_SLAVE_ADDR 0x88 /* I2C slave address, 0x77 */
     21#define T5403_COMMAND 0xf1
     22
     23/* command bits */
     24#define T5403_MODE_SHIFT 3 /* conversion time: 2, 8, 16, 66 ms */
     25#define T5403_PT BIT(1) /* 0 .. pressure, 1 .. temperature measurement */
     26#define T5403_SCO BIT(0) /* start conversion */
     27
     28#define T5403_MODE_LOW 0
     29#define T5403_MODE_STANDARD 1
     30#define T5403_MODE_HIGH 2
     31#define T5403_MODE_ULTRA_HIGH 3
     32
     33#define T5403_I2C_MASK (~BIT(7))
     34#define T5403_I2C_ADDR 0x77
     35
     36static const int t5403_pressure_conv_ms[] = {2, 8, 16, 66};
     37
     38struct t5403_data {
     39	struct i2c_client *client;
     40	struct mutex lock;
     41	int mode;
     42	__le16 c[10];
     43};
     44
     45#define T5403_C_U16(i) le16_to_cpu(data->c[(i) - 1])
     46#define T5403_C(i) sign_extend32(T5403_C_U16(i), 15)
     47
     48static int t5403_read(struct t5403_data *data, bool pressure)
     49{
     50	int wait_time = 3;  /* wakeup time in ms */
     51
     52	int ret = i2c_smbus_write_byte_data(data->client, T5403_COMMAND,
     53		(pressure ? (data->mode << T5403_MODE_SHIFT) : T5403_PT) |
     54		T5403_SCO);
     55	if (ret < 0)
     56		return ret;
     57
     58	wait_time += pressure ? t5403_pressure_conv_ms[data->mode] : 2;
     59
     60	msleep(wait_time);
     61
     62	return i2c_smbus_read_word_data(data->client, T5403_DATA);
     63}
     64
     65static int t5403_comp_pressure(struct t5403_data *data, int *val, int *val2)
     66{
     67	int ret;
     68	s16 t_r;
     69	u16 p_r;
     70	s32 S, O, X;
     71
     72	mutex_lock(&data->lock);
     73
     74	ret = t5403_read(data, false);
     75	if (ret < 0)
     76		goto done;
     77	t_r = ret;
     78
     79	ret = t5403_read(data, true);
     80	if (ret < 0)
     81		goto done;
     82	p_r = ret;
     83
     84	/* see EPCOS application note */
     85	S = T5403_C_U16(3) + (s32) T5403_C_U16(4) * t_r / 0x20000 +
     86		T5403_C(5) * t_r / 0x8000 * t_r / 0x80000 +
     87		T5403_C(9) * t_r / 0x8000 * t_r / 0x8000 * t_r / 0x10000;
     88
     89	O = T5403_C(6) * 0x4000 + T5403_C(7) * t_r / 8 +
     90		T5403_C(8) * t_r / 0x8000 * t_r / 16 +
     91		T5403_C(9) * t_r / 0x8000 * t_r / 0x10000 * t_r;
     92
     93	X = (S * p_r + O) / 0x4000;
     94
     95	X += ((X - 75000) * (X - 75000) / 0x10000 - 9537) *
     96	    T5403_C(10) / 0x10000;
     97
     98	*val = X / 1000;
     99	*val2 = (X % 1000) * 1000;
    100
    101done:
    102	mutex_unlock(&data->lock);
    103	return ret;
    104}
    105
    106static int t5403_comp_temp(struct t5403_data *data, int *val)
    107{
    108	int ret;
    109	s16 t_r;
    110
    111	mutex_lock(&data->lock);
    112	ret = t5403_read(data, false);
    113	if (ret < 0)
    114		goto done;
    115	t_r = ret;
    116
    117	/* see EPCOS application note */
    118	*val = ((s32) T5403_C_U16(1) * t_r / 0x100 +
    119		(s32) T5403_C_U16(2) * 0x40) * 1000 / 0x10000;
    120
    121done:
    122	mutex_unlock(&data->lock);
    123	return ret;
    124}
    125
    126static int t5403_read_raw(struct iio_dev *indio_dev,
    127			  struct iio_chan_spec const *chan,
    128			  int *val, int *val2, long mask)
    129{
    130	struct t5403_data *data = iio_priv(indio_dev);
    131	int ret;
    132
    133	switch (mask) {
    134	case IIO_CHAN_INFO_PROCESSED:
    135		switch (chan->type) {
    136		case IIO_PRESSURE:
    137			ret = t5403_comp_pressure(data, val, val2);
    138			if (ret < 0)
    139				return ret;
    140			return IIO_VAL_INT_PLUS_MICRO;
    141		case IIO_TEMP:
    142			ret = t5403_comp_temp(data, val);
    143			if (ret < 0)
    144				return ret;
    145			return IIO_VAL_INT;
    146		default:
    147			return -EINVAL;
    148	    }
    149	case IIO_CHAN_INFO_INT_TIME:
    150		*val = 0;
    151		*val2 = t5403_pressure_conv_ms[data->mode] * 1000;
    152		return IIO_VAL_INT_PLUS_MICRO;
    153	default:
    154		return -EINVAL;
    155	}
    156}
    157
    158static int t5403_write_raw(struct iio_dev *indio_dev,
    159			   struct iio_chan_spec const *chan,
    160			   int val, int val2, long mask)
    161{
    162	struct t5403_data *data = iio_priv(indio_dev);
    163	int i;
    164
    165	switch (mask) {
    166	case IIO_CHAN_INFO_INT_TIME:
    167		if (val != 0)
    168			return -EINVAL;
    169		for (i = 0; i < ARRAY_SIZE(t5403_pressure_conv_ms); i++)
    170			if (val2 == t5403_pressure_conv_ms[i] * 1000) {
    171				mutex_lock(&data->lock);
    172				data->mode = i;
    173				mutex_unlock(&data->lock);
    174				return 0;
    175			}
    176		return -EINVAL;
    177	default:
    178		return -EINVAL;
    179	}
    180}
    181
    182static const struct iio_chan_spec t5403_channels[] = {
    183	{
    184		.type = IIO_PRESSURE,
    185		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
    186		    BIT(IIO_CHAN_INFO_INT_TIME),
    187	},
    188	{
    189		.type = IIO_TEMP,
    190		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
    191	},
    192};
    193
    194static IIO_CONST_ATTR_INT_TIME_AVAIL("0.002 0.008 0.016 0.066");
    195
    196static struct attribute *t5403_attributes[] = {
    197	&iio_const_attr_integration_time_available.dev_attr.attr,
    198	NULL
    199};
    200
    201static const struct attribute_group t5403_attribute_group = {
    202	.attrs = t5403_attributes,
    203};
    204
    205static const struct iio_info t5403_info = {
    206	.read_raw = &t5403_read_raw,
    207	.write_raw = &t5403_write_raw,
    208	.attrs = &t5403_attribute_group,
    209};
    210
    211static int t5403_probe(struct i2c_client *client,
    212			 const struct i2c_device_id *id)
    213{
    214	struct t5403_data *data;
    215	struct iio_dev *indio_dev;
    216	int ret;
    217
    218	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
    219	    I2C_FUNC_SMBUS_I2C_BLOCK))
    220		return -EOPNOTSUPP;
    221
    222	ret = i2c_smbus_read_byte_data(client, T5403_SLAVE_ADDR);
    223	if (ret < 0)
    224		return ret;
    225	if ((ret & T5403_I2C_MASK) != T5403_I2C_ADDR)
    226		return -ENODEV;
    227
    228	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    229	if (!indio_dev)
    230		return -ENOMEM;
    231
    232	data = iio_priv(indio_dev);
    233	data->client = client;
    234	mutex_init(&data->lock);
    235
    236	i2c_set_clientdata(client, indio_dev);
    237	indio_dev->info = &t5403_info;
    238	indio_dev->name = id->name;
    239	indio_dev->modes = INDIO_DIRECT_MODE;
    240	indio_dev->channels = t5403_channels;
    241	indio_dev->num_channels = ARRAY_SIZE(t5403_channels);
    242
    243	data->mode = T5403_MODE_STANDARD;
    244
    245	ret = i2c_smbus_read_i2c_block_data(data->client, T5403_CALIB_DATA,
    246	    sizeof(data->c), (u8 *) data->c);
    247	if (ret < 0)
    248		return ret;
    249
    250	return devm_iio_device_register(&client->dev, indio_dev);
    251}
    252
    253static const struct i2c_device_id t5403_id[] = {
    254	{ "t5403", 0 },
    255	{ }
    256};
    257MODULE_DEVICE_TABLE(i2c, t5403_id);
    258
    259static struct i2c_driver t5403_driver = {
    260	.driver = {
    261		.name	= "t5403",
    262	},
    263	.probe = t5403_probe,
    264	.id_table = t5403_id,
    265};
    266module_i2c_driver(t5403_driver);
    267
    268MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
    269MODULE_DESCRIPTION("EPCOS T5403 pressure/temperature sensor driver");
    270MODULE_LICENSE("GPL");