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

tpl0102.c (4182B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * tpl0102.c - Support for Texas Instruments digital potentiometers
      4 *
      5 * Copyright (C) 2016, 2018
      6 * Author: Matt Ranostay <matt.ranostay@konsulko.com>
      7 *
      8 * TODO: enable/disable hi-z output control
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/i2c.h>
     13#include <linux/regmap.h>
     14#include <linux/iio/iio.h>
     15
     16struct tpl0102_cfg {
     17	int wipers;
     18	int avail[3];
     19	int kohms;
     20};
     21
     22enum tpl0102_type {
     23	CAT5140_503,
     24	CAT5140_104,
     25	TPL0102_104,
     26	TPL0401_103,
     27};
     28
     29static const struct tpl0102_cfg tpl0102_cfg[] = {
     30	/* on-semiconductor parts */
     31	[CAT5140_503] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 50, },
     32	[CAT5140_104] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 100, },
     33	/* ti parts */
     34	[TPL0102_104] = { .wipers = 2, .avail = { 0, 1, 255 }, .kohms = 100 },
     35	[TPL0401_103] = { .wipers = 1, .avail = { 0, 1, 127 }, .kohms = 10, },
     36};
     37
     38struct tpl0102_data {
     39	struct regmap *regmap;
     40	const struct tpl0102_cfg *cfg;
     41};
     42
     43static const struct regmap_config tpl0102_regmap_config = {
     44	.reg_bits = 8,
     45	.val_bits = 8,
     46};
     47
     48#define TPL0102_CHANNEL(ch) {					\
     49	.type = IIO_RESISTANCE,					\
     50	.indexed = 1,						\
     51	.output = 1,						\
     52	.channel = (ch),					\
     53	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
     54	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
     55	.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),	\
     56}
     57
     58static const struct iio_chan_spec tpl0102_channels[] = {
     59	TPL0102_CHANNEL(0),
     60	TPL0102_CHANNEL(1),
     61};
     62
     63static int tpl0102_read_raw(struct iio_dev *indio_dev,
     64			    struct iio_chan_spec const *chan,
     65			    int *val, int *val2, long mask)
     66{
     67	struct tpl0102_data *data = iio_priv(indio_dev);
     68
     69	switch (mask) {
     70	case IIO_CHAN_INFO_RAW: {
     71		int ret = regmap_read(data->regmap, chan->channel, val);
     72
     73		return ret ? ret : IIO_VAL_INT;
     74	}
     75	case IIO_CHAN_INFO_SCALE:
     76		*val = 1000 * data->cfg->kohms;
     77		*val2 = data->cfg->avail[2] + 1;
     78		return IIO_VAL_FRACTIONAL;
     79	}
     80
     81	return -EINVAL;
     82}
     83
     84static int tpl0102_read_avail(struct iio_dev *indio_dev,
     85			      struct iio_chan_spec const *chan,
     86			      const int **vals, int *type, int *length,
     87			      long mask)
     88{
     89	struct tpl0102_data *data = iio_priv(indio_dev);
     90
     91	switch (mask) {
     92	case IIO_CHAN_INFO_RAW:
     93		*length = ARRAY_SIZE(data->cfg->avail);
     94		*vals = data->cfg->avail;
     95		*type = IIO_VAL_INT;
     96		return IIO_AVAIL_RANGE;
     97	}
     98
     99	return -EINVAL;
    100}
    101
    102static int tpl0102_write_raw(struct iio_dev *indio_dev,
    103			     struct iio_chan_spec const *chan,
    104			     int val, int val2, long mask)
    105{
    106	struct tpl0102_data *data = iio_priv(indio_dev);
    107
    108	if (mask != IIO_CHAN_INFO_RAW)
    109		return -EINVAL;
    110
    111	if (val > data->cfg->avail[2] || val < 0)
    112		return -EINVAL;
    113
    114	return regmap_write(data->regmap, chan->channel, val);
    115}
    116
    117static const struct iio_info tpl0102_info = {
    118	.read_raw = tpl0102_read_raw,
    119	.read_avail = tpl0102_read_avail,
    120	.write_raw = tpl0102_write_raw,
    121};
    122
    123static int tpl0102_probe(struct i2c_client *client,
    124			 const struct i2c_device_id *id)
    125{
    126	struct device *dev = &client->dev;
    127	struct tpl0102_data *data;
    128	struct iio_dev *indio_dev;
    129
    130	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
    131	if (!indio_dev)
    132		return -ENOMEM;
    133	data = iio_priv(indio_dev);
    134	i2c_set_clientdata(client, indio_dev);
    135
    136	data->cfg = &tpl0102_cfg[id->driver_data];
    137	data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
    138	if (IS_ERR(data->regmap)) {
    139		dev_err(dev, "regmap initialization failed\n");
    140		return PTR_ERR(data->regmap);
    141	}
    142
    143	indio_dev->info = &tpl0102_info;
    144	indio_dev->channels = tpl0102_channels;
    145	indio_dev->num_channels = data->cfg->wipers;
    146	indio_dev->name = client->name;
    147
    148	return devm_iio_device_register(dev, indio_dev);
    149}
    150
    151static const struct i2c_device_id tpl0102_id[] = {
    152	{ "cat5140-503", CAT5140_503 },
    153	{ "cat5140-104", CAT5140_104 },
    154	{ "tpl0102-104", TPL0102_104 },
    155	{ "tpl0401-103", TPL0401_103 },
    156	{}
    157};
    158MODULE_DEVICE_TABLE(i2c, tpl0102_id);
    159
    160static struct i2c_driver tpl0102_driver = {
    161	.driver = {
    162		.name = "tpl0102",
    163	},
    164	.probe = tpl0102_probe,
    165	.id_table = tpl0102_id,
    166};
    167
    168module_i2c_driver(tpl0102_driver);
    169
    170MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
    171MODULE_DESCRIPTION("TPL0102 digital potentiometer");
    172MODULE_LICENSE("GPL");