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

ds1803.c (6334B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Maxim Integrated DS1803 and similar digital potentiometer driver
      4 * Copyright (c) 2016 Slawomir Stepien
      5 * Copyright (c) 2022 Jagath Jog J
      6 *
      7 * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS1803.pdf
      8 * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3502.pdf
      9 *
     10 * DEVID	#Wipers	#Positions	Resistor Opts (kOhm)	i2c address
     11 * ds1803	2	256		10, 50, 100		0101xxx
     12 * ds3502	1	128		10			01010xx
     13 */
     14
     15#include <linux/err.h>
     16#include <linux/export.h>
     17#include <linux/i2c.h>
     18#include <linux/iio/iio.h>
     19#include <linux/module.h>
     20#include <linux/mod_devicetable.h>
     21#include <linux/property.h>
     22
     23#define DS1803_WIPER_0         0xA9
     24#define DS1803_WIPER_1         0xAA
     25#define DS3502_WR_IVR          0x00
     26
     27enum ds1803_type {
     28	DS1803_010,
     29	DS1803_050,
     30	DS1803_100,
     31	DS3502,
     32};
     33
     34struct ds1803_cfg {
     35	int wipers;
     36	int avail[3];
     37	int kohms;
     38	const struct iio_chan_spec *channels;
     39	u8 num_channels;
     40	int (*read)(struct iio_dev *indio_dev,
     41		    struct iio_chan_spec const *chan, int *val);
     42};
     43
     44struct ds1803_data {
     45	struct i2c_client *client;
     46	const struct ds1803_cfg *cfg;
     47};
     48
     49#define DS1803_CHANNEL(ch, addr) {					\
     50	.type = IIO_RESISTANCE,						\
     51	.indexed = 1,							\
     52	.output = 1,							\
     53	.channel = (ch),						\
     54	.address = (addr),						\
     55	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
     56	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),		\
     57	.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW),   \
     58}
     59
     60static const struct iio_chan_spec ds1803_channels[] = {
     61	DS1803_CHANNEL(0, DS1803_WIPER_0),
     62	DS1803_CHANNEL(1, DS1803_WIPER_1),
     63};
     64
     65static const struct iio_chan_spec ds3502_channels[] = {
     66	DS1803_CHANNEL(0, DS3502_WR_IVR),
     67};
     68
     69static int ds1803_read(struct iio_dev *indio_dev,
     70		       struct iio_chan_spec const *chan,
     71		       int *val)
     72{
     73	struct ds1803_data *data = iio_priv(indio_dev);
     74	int ret;
     75	u8 result[ARRAY_SIZE(ds1803_channels)];
     76
     77	ret = i2c_master_recv(data->client, result, indio_dev->num_channels);
     78	if (ret < 0)
     79		return ret;
     80
     81	*val = result[chan->channel];
     82	return ret;
     83}
     84
     85static int ds3502_read(struct iio_dev *indio_dev,
     86		       struct iio_chan_spec const *chan,
     87		       int *val)
     88{
     89	struct ds1803_data *data = iio_priv(indio_dev);
     90	int ret;
     91
     92	ret = i2c_smbus_read_byte_data(data->client, chan->address);
     93	if (ret < 0)
     94		return ret;
     95
     96	*val = ret;
     97	return ret;
     98}
     99
    100static const struct ds1803_cfg ds1803_cfg[] = {
    101	[DS1803_010] = {
    102		.wipers = 2,
    103		.avail = { 0, 1, 255 },
    104		.kohms =  10,
    105		.channels = ds1803_channels,
    106		.num_channels = ARRAY_SIZE(ds1803_channels),
    107		.read = ds1803_read,
    108	},
    109	[DS1803_050] = {
    110		.wipers = 2,
    111		.avail = { 0, 1, 255 },
    112		.kohms =  50,
    113		.channels = ds1803_channels,
    114		.num_channels = ARRAY_SIZE(ds1803_channels),
    115		.read = ds1803_read,
    116	},
    117	[DS1803_100] = {
    118		.wipers = 2,
    119		.avail = { 0, 1, 255 },
    120		.kohms = 100,
    121		.channels = ds1803_channels,
    122		.num_channels = ARRAY_SIZE(ds1803_channels),
    123		.read = ds1803_read,
    124	},
    125	[DS3502] = {
    126	  .wipers = 1,
    127	  .avail = { 0, 1, 127 },
    128	  .kohms =  10,
    129	  .channels = ds3502_channels,
    130	  .num_channels = ARRAY_SIZE(ds3502_channels),
    131	  .read = ds3502_read,
    132	},
    133};
    134
    135static int ds1803_read_raw(struct iio_dev *indio_dev,
    136			   struct iio_chan_spec const *chan,
    137			   int *val, int *val2, long mask)
    138{
    139	struct ds1803_data *data = iio_priv(indio_dev);
    140	int ret;
    141
    142	switch (mask) {
    143	case IIO_CHAN_INFO_RAW:
    144		ret = data->cfg->read(indio_dev, chan, val);
    145		if (ret < 0)
    146			return ret;
    147
    148		return IIO_VAL_INT;
    149
    150	case IIO_CHAN_INFO_SCALE:
    151		*val = 1000 * data->cfg->kohms;
    152		*val2 = data->cfg->avail[2]; /* Max wiper position */
    153		return IIO_VAL_FRACTIONAL;
    154	}
    155
    156	return -EINVAL;
    157}
    158
    159static int ds1803_write_raw(struct iio_dev *indio_dev,
    160			    struct iio_chan_spec const *chan,
    161			    int val, int val2, long mask)
    162{
    163	struct ds1803_data *data = iio_priv(indio_dev);
    164	u8 addr = chan->address;
    165	int max_pos = data->cfg->avail[2];
    166
    167	if (val2 != 0)
    168		return -EINVAL;
    169
    170	switch (mask) {
    171	case IIO_CHAN_INFO_RAW:
    172		if (val > max_pos || val < 0)
    173			return -EINVAL;
    174		break;
    175	default:
    176		return -EINVAL;
    177	}
    178
    179	return i2c_smbus_write_byte_data(data->client, addr, val);
    180}
    181
    182static int ds1803_read_avail(struct iio_dev *indio_dev,
    183			     struct iio_chan_spec const *chan,
    184			     const int **vals, int *type,
    185			     int *length, long mask)
    186{
    187	struct ds1803_data *data = iio_priv(indio_dev);
    188
    189	switch (mask) {
    190	case IIO_CHAN_INFO_RAW:
    191		*vals = data->cfg->avail;
    192		*length = ARRAY_SIZE(data->cfg->avail);
    193		*type = IIO_VAL_INT;
    194		return IIO_AVAIL_RANGE;
    195	}
    196	return -EINVAL;
    197}
    198
    199static const struct iio_info ds1803_info = {
    200	.read_raw = ds1803_read_raw,
    201	.write_raw = ds1803_write_raw,
    202	.read_avail = ds1803_read_avail,
    203};
    204
    205static int ds1803_probe(struct i2c_client *client, const struct i2c_device_id *id)
    206{
    207	struct device *dev = &client->dev;
    208	struct ds1803_data *data;
    209	struct iio_dev *indio_dev;
    210
    211	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
    212	if (!indio_dev)
    213		return -ENOMEM;
    214
    215	i2c_set_clientdata(client, indio_dev);
    216
    217	data = iio_priv(indio_dev);
    218	data->client = client;
    219	data->cfg = device_get_match_data(dev);
    220	if (!data->cfg)
    221		data->cfg = &ds1803_cfg[id->driver_data];
    222
    223	indio_dev->info = &ds1803_info;
    224	indio_dev->channels = data->cfg->channels;
    225	indio_dev->num_channels = data->cfg->num_channels;
    226	indio_dev->name = client->name;
    227
    228	return devm_iio_device_register(dev, indio_dev);
    229}
    230
    231static const struct of_device_id ds1803_dt_ids[] = {
    232	{ .compatible = "maxim,ds1803-010", .data = &ds1803_cfg[DS1803_010] },
    233	{ .compatible = "maxim,ds1803-050", .data = &ds1803_cfg[DS1803_050] },
    234	{ .compatible = "maxim,ds1803-100", .data = &ds1803_cfg[DS1803_100] },
    235	{ .compatible = "maxim,ds3502", .data = &ds1803_cfg[DS3502] },
    236	{}
    237};
    238MODULE_DEVICE_TABLE(of, ds1803_dt_ids);
    239
    240static const struct i2c_device_id ds1803_id[] = {
    241	{ "ds1803-010", DS1803_010 },
    242	{ "ds1803-050", DS1803_050 },
    243	{ "ds1803-100", DS1803_100 },
    244	{ "ds3502", DS3502 },
    245	{}
    246};
    247MODULE_DEVICE_TABLE(i2c, ds1803_id);
    248
    249static struct i2c_driver ds1803_driver = {
    250	.driver = {
    251		.name	= "ds1803",
    252		.of_match_table = ds1803_dt_ids,
    253	},
    254	.probe		= ds1803_probe,
    255	.id_table	= ds1803_id,
    256};
    257
    258module_i2c_driver(ds1803_driver);
    259
    260MODULE_AUTHOR("Slawomir Stepien <sst@poczta.fm>");
    261MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>");
    262MODULE_DESCRIPTION("DS1803 digital potentiometer");
    263MODULE_LICENSE("GPL v2");