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

ad5272.c (5498B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Analog Devices AD5272 digital potentiometer driver
      4 * Copyright (C) 2018 Phil Reid <preid@electromag.com.au>
      5 *
      6 * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/AD5272_5274.pdf
      7 *
      8 * DEVID	#Wipers	#Positions	Resistor Opts (kOhm)	i2c address
      9 * ad5272	1	1024		20, 50, 100		01011xx
     10 * ad5274	1	256		20, 100			01011xx
     11 */
     12
     13#include <linux/delay.h>
     14#include <linux/gpio/consumer.h>
     15#include <linux/i2c.h>
     16#include <linux/iio/iio.h>
     17#include <linux/module.h>
     18#include <linux/mod_devicetable.h>
     19
     20#define  AD5272_RDAC_WR  1
     21#define  AD5272_RDAC_RD  2
     22#define  AD5272_RESET    4
     23#define  AD5272_CTL      7
     24
     25#define  AD5272_RDAC_WR_EN  BIT(1)
     26
     27struct ad5272_cfg {
     28	int max_pos;
     29	int kohms;
     30	int shift;
     31};
     32
     33enum ad5272_type {
     34	AD5272_020,
     35	AD5272_050,
     36	AD5272_100,
     37	AD5274_020,
     38	AD5274_100,
     39};
     40
     41static const struct ad5272_cfg ad5272_cfg[] = {
     42	[AD5272_020] = { .max_pos = 1024, .kohms = 20 },
     43	[AD5272_050] = { .max_pos = 1024, .kohms = 50 },
     44	[AD5272_100] = { .max_pos = 1024, .kohms = 100 },
     45	[AD5274_020] = { .max_pos = 256,  .kohms = 20,  .shift = 2 },
     46	[AD5274_100] = { .max_pos = 256,  .kohms = 100, .shift = 2 },
     47};
     48
     49struct ad5272_data {
     50	struct i2c_client       *client;
     51	struct mutex            lock;
     52	const struct ad5272_cfg *cfg;
     53	u8                      buf[2] ____cacheline_aligned;
     54};
     55
     56static const struct iio_chan_spec ad5272_channel = {
     57	.type = IIO_RESISTANCE,
     58	.output = 1,
     59	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
     60	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
     61};
     62
     63static int ad5272_write(struct ad5272_data *data, int reg, int val)
     64{
     65	int ret;
     66
     67	data->buf[0] = (reg << 2) | ((val >> 8) & 0x3);
     68	data->buf[1] = (u8)val;
     69
     70	mutex_lock(&data->lock);
     71	ret = i2c_master_send(data->client, data->buf, sizeof(data->buf));
     72	mutex_unlock(&data->lock);
     73	return ret < 0 ? ret : 0;
     74}
     75
     76static int ad5272_read(struct ad5272_data *data, int reg, int *val)
     77{
     78	int ret;
     79
     80	data->buf[0] = reg << 2;
     81	data->buf[1] = 0;
     82
     83	mutex_lock(&data->lock);
     84	ret = i2c_master_send(data->client, data->buf, sizeof(data->buf));
     85	if (ret < 0)
     86		goto error;
     87
     88	ret = i2c_master_recv(data->client, data->buf, sizeof(data->buf));
     89	if (ret < 0)
     90		goto error;
     91
     92	*val = ((data->buf[0] & 0x3) << 8) | data->buf[1];
     93	ret = 0;
     94error:
     95	mutex_unlock(&data->lock);
     96	return ret;
     97}
     98
     99static int ad5272_read_raw(struct iio_dev *indio_dev,
    100			   struct iio_chan_spec const *chan,
    101			   int *val, int *val2, long mask)
    102{
    103	struct ad5272_data *data = iio_priv(indio_dev);
    104	int ret;
    105
    106	switch (mask) {
    107	case IIO_CHAN_INFO_RAW: {
    108		ret = ad5272_read(data, AD5272_RDAC_RD, val);
    109		*val = *val >> data->cfg->shift;
    110		return ret ? ret : IIO_VAL_INT;
    111	}
    112	case IIO_CHAN_INFO_SCALE:
    113		*val = 1000 * data->cfg->kohms;
    114		*val2 = data->cfg->max_pos;
    115		return IIO_VAL_FRACTIONAL;
    116	}
    117
    118	return -EINVAL;
    119}
    120
    121static int ad5272_write_raw(struct iio_dev *indio_dev,
    122			    struct iio_chan_spec const *chan,
    123			    int val, int val2, long mask)
    124{
    125	struct ad5272_data *data = iio_priv(indio_dev);
    126
    127	if (mask != IIO_CHAN_INFO_RAW)
    128		return -EINVAL;
    129
    130	if (val >= data->cfg->max_pos || val < 0 || val2)
    131		return -EINVAL;
    132
    133	return ad5272_write(data, AD5272_RDAC_WR, val << data->cfg->shift);
    134}
    135
    136static const struct iio_info ad5272_info = {
    137	.read_raw = ad5272_read_raw,
    138	.write_raw = ad5272_write_raw,
    139};
    140
    141static int ad5272_reset(struct ad5272_data *data)
    142{
    143	struct gpio_desc *reset_gpio;
    144
    145	reset_gpio = devm_gpiod_get_optional(&data->client->dev, "reset",
    146		GPIOD_OUT_HIGH);
    147	if (IS_ERR(reset_gpio))
    148		return PTR_ERR(reset_gpio);
    149
    150	if (reset_gpio) {
    151		udelay(1);
    152		gpiod_set_value(reset_gpio, 0);
    153	} else {
    154		ad5272_write(data, AD5272_RESET, 0);
    155	}
    156	usleep_range(1000, 2000);
    157
    158	return 0;
    159}
    160
    161static int ad5272_probe(struct i2c_client *client,
    162			const struct i2c_device_id *id)
    163{
    164	struct device *dev = &client->dev;
    165	struct iio_dev *indio_dev;
    166	struct ad5272_data *data;
    167	int ret;
    168
    169	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
    170	if (!indio_dev)
    171		return -ENOMEM;
    172
    173	i2c_set_clientdata(client, indio_dev);
    174
    175	data = iio_priv(indio_dev);
    176	data->client = client;
    177	mutex_init(&data->lock);
    178	data->cfg = &ad5272_cfg[id->driver_data];
    179
    180	ret = ad5272_reset(data);
    181	if (ret)
    182		return ret;
    183
    184	ret = ad5272_write(data, AD5272_CTL, AD5272_RDAC_WR_EN);
    185	if (ret < 0)
    186		return -ENODEV;
    187
    188	indio_dev->info = &ad5272_info;
    189	indio_dev->channels = &ad5272_channel;
    190	indio_dev->num_channels = 1;
    191	indio_dev->name = client->name;
    192
    193	return devm_iio_device_register(dev, indio_dev);
    194}
    195
    196static const struct of_device_id ad5272_dt_ids[] = {
    197	{ .compatible = "adi,ad5272-020", .data = (void *)AD5272_020 },
    198	{ .compatible = "adi,ad5272-050", .data = (void *)AD5272_050 },
    199	{ .compatible = "adi,ad5272-100", .data = (void *)AD5272_100 },
    200	{ .compatible = "adi,ad5274-020", .data = (void *)AD5274_020 },
    201	{ .compatible = "adi,ad5274-100", .data = (void *)AD5274_100 },
    202	{}
    203};
    204MODULE_DEVICE_TABLE(of, ad5272_dt_ids);
    205
    206static const struct i2c_device_id ad5272_id[] = {
    207	{ "ad5272-020", AD5272_020 },
    208	{ "ad5272-050", AD5272_050 },
    209	{ "ad5272-100", AD5272_100 },
    210	{ "ad5274-020", AD5274_020 },
    211	{ "ad5274-100", AD5274_100 },
    212	{}
    213};
    214MODULE_DEVICE_TABLE(i2c, ad5272_id);
    215
    216static struct i2c_driver ad5272_driver = {
    217	.driver = {
    218		.name	= "ad5272",
    219		.of_match_table = ad5272_dt_ids,
    220	},
    221	.probe		= ad5272_probe,
    222	.id_table	= ad5272_id,
    223};
    224
    225module_i2c_driver(ad5272_driver);
    226
    227MODULE_AUTHOR("Phil Reid <preid@eletromag.com.au>");
    228MODULE_DESCRIPTION("AD5272 digital potentiometer");
    229MODULE_LICENSE("GPL v2");