1d6ad8058SMatt Ranostay // SPDX-License-Identifier: GPL-2.0+
22edbd295SMatt Ranostay /*
32edbd295SMatt Ranostay * tpl0102.c - Support for Texas Instruments digital potentiometers
42edbd295SMatt Ranostay *
5d6ad8058SMatt Ranostay * Copyright (C) 2016, 2018
6d6ad8058SMatt Ranostay * Author: Matt Ranostay <matt.ranostay@konsulko.com>
72edbd295SMatt Ranostay *
82edbd295SMatt Ranostay * TODO: enable/disable hi-z output control
92edbd295SMatt Ranostay */
102edbd295SMatt Ranostay
112edbd295SMatt Ranostay #include <linux/module.h>
122edbd295SMatt Ranostay #include <linux/i2c.h>
132edbd295SMatt Ranostay #include <linux/regmap.h>
142edbd295SMatt Ranostay #include <linux/iio/iio.h>
152edbd295SMatt Ranostay
162edbd295SMatt Ranostay struct tpl0102_cfg {
172edbd295SMatt Ranostay int wipers;
1874cf7b86SMatt Ranostay int avail[3];
192edbd295SMatt Ranostay int kohms;
202edbd295SMatt Ranostay };
212edbd295SMatt Ranostay
222edbd295SMatt Ranostay enum tpl0102_type {
232edbd295SMatt Ranostay CAT5140_503,
242edbd295SMatt Ranostay CAT5140_104,
252edbd295SMatt Ranostay TPL0102_104,
262edbd295SMatt Ranostay TPL0401_103,
272edbd295SMatt Ranostay };
282edbd295SMatt Ranostay
292edbd295SMatt Ranostay static const struct tpl0102_cfg tpl0102_cfg[] = {
302edbd295SMatt Ranostay /* on-semiconductor parts */
3174cf7b86SMatt Ranostay [CAT5140_503] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 50, },
3274cf7b86SMatt Ranostay [CAT5140_104] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 100, },
332edbd295SMatt Ranostay /* ti parts */
3474cf7b86SMatt Ranostay [TPL0102_104] = { .wipers = 2, .avail = { 0, 1, 255 }, .kohms = 100 },
3574cf7b86SMatt Ranostay [TPL0401_103] = { .wipers = 1, .avail = { 0, 1, 127 }, .kohms = 10, },
362edbd295SMatt Ranostay };
372edbd295SMatt Ranostay
382edbd295SMatt Ranostay struct tpl0102_data {
392edbd295SMatt Ranostay struct regmap *regmap;
40ee230351SMatt Ranostay const struct tpl0102_cfg *cfg;
412edbd295SMatt Ranostay };
422edbd295SMatt Ranostay
432edbd295SMatt Ranostay static const struct regmap_config tpl0102_regmap_config = {
442edbd295SMatt Ranostay .reg_bits = 8,
452edbd295SMatt Ranostay .val_bits = 8,
462edbd295SMatt Ranostay };
472edbd295SMatt Ranostay
482edbd295SMatt Ranostay #define TPL0102_CHANNEL(ch) { \
492edbd295SMatt Ranostay .type = IIO_RESISTANCE, \
502edbd295SMatt Ranostay .indexed = 1, \
512edbd295SMatt Ranostay .output = 1, \
522edbd295SMatt Ranostay .channel = (ch), \
532edbd295SMatt Ranostay .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
542edbd295SMatt Ranostay .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
5574cf7b86SMatt Ranostay .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \
562edbd295SMatt Ranostay }
572edbd295SMatt Ranostay
582edbd295SMatt Ranostay static const struct iio_chan_spec tpl0102_channels[] = {
592edbd295SMatt Ranostay TPL0102_CHANNEL(0),
602edbd295SMatt Ranostay TPL0102_CHANNEL(1),
612edbd295SMatt Ranostay };
622edbd295SMatt Ranostay
tpl0102_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)632edbd295SMatt Ranostay static int tpl0102_read_raw(struct iio_dev *indio_dev,
642edbd295SMatt Ranostay struct iio_chan_spec const *chan,
652edbd295SMatt Ranostay int *val, int *val2, long mask)
662edbd295SMatt Ranostay {
672edbd295SMatt Ranostay struct tpl0102_data *data = iio_priv(indio_dev);
682edbd295SMatt Ranostay
692edbd295SMatt Ranostay switch (mask) {
702edbd295SMatt Ranostay case IIO_CHAN_INFO_RAW: {
712edbd295SMatt Ranostay int ret = regmap_read(data->regmap, chan->channel, val);
722edbd295SMatt Ranostay
732edbd295SMatt Ranostay return ret ? ret : IIO_VAL_INT;
742edbd295SMatt Ranostay }
752edbd295SMatt Ranostay case IIO_CHAN_INFO_SCALE:
76ee230351SMatt Ranostay *val = 1000 * data->cfg->kohms;
7774cf7b86SMatt Ranostay *val2 = data->cfg->avail[2] + 1;
782edbd295SMatt Ranostay return IIO_VAL_FRACTIONAL;
792edbd295SMatt Ranostay }
802edbd295SMatt Ranostay
812edbd295SMatt Ranostay return -EINVAL;
822edbd295SMatt Ranostay }
832edbd295SMatt Ranostay
tpl0102_read_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)8474cf7b86SMatt Ranostay static int tpl0102_read_avail(struct iio_dev *indio_dev,
8574cf7b86SMatt Ranostay struct iio_chan_spec const *chan,
8674cf7b86SMatt Ranostay const int **vals, int *type, int *length,
8774cf7b86SMatt Ranostay long mask)
8874cf7b86SMatt Ranostay {
8974cf7b86SMatt Ranostay struct tpl0102_data *data = iio_priv(indio_dev);
9074cf7b86SMatt Ranostay
9174cf7b86SMatt Ranostay switch (mask) {
9274cf7b86SMatt Ranostay case IIO_CHAN_INFO_RAW:
9374cf7b86SMatt Ranostay *length = ARRAY_SIZE(data->cfg->avail);
9474cf7b86SMatt Ranostay *vals = data->cfg->avail;
9574cf7b86SMatt Ranostay *type = IIO_VAL_INT;
9674cf7b86SMatt Ranostay return IIO_AVAIL_RANGE;
9774cf7b86SMatt Ranostay }
9874cf7b86SMatt Ranostay
9974cf7b86SMatt Ranostay return -EINVAL;
10074cf7b86SMatt Ranostay }
10174cf7b86SMatt Ranostay
tpl0102_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)1022edbd295SMatt Ranostay static int tpl0102_write_raw(struct iio_dev *indio_dev,
1032edbd295SMatt Ranostay struct iio_chan_spec const *chan,
1042edbd295SMatt Ranostay int val, int val2, long mask)
1052edbd295SMatt Ranostay {
1062edbd295SMatt Ranostay struct tpl0102_data *data = iio_priv(indio_dev);
1072edbd295SMatt Ranostay
1082edbd295SMatt Ranostay if (mask != IIO_CHAN_INFO_RAW)
1092edbd295SMatt Ranostay return -EINVAL;
1102edbd295SMatt Ranostay
11174cf7b86SMatt Ranostay if (val > data->cfg->avail[2] || val < 0)
1122edbd295SMatt Ranostay return -EINVAL;
1132edbd295SMatt Ranostay
1142edbd295SMatt Ranostay return regmap_write(data->regmap, chan->channel, val);
1152edbd295SMatt Ranostay }
1162edbd295SMatt Ranostay
1172edbd295SMatt Ranostay static const struct iio_info tpl0102_info = {
1182edbd295SMatt Ranostay .read_raw = tpl0102_read_raw,
11974cf7b86SMatt Ranostay .read_avail = tpl0102_read_avail,
1202edbd295SMatt Ranostay .write_raw = tpl0102_write_raw,
1212edbd295SMatt Ranostay };
1222edbd295SMatt Ranostay
tpl0102_probe(struct i2c_client * client)1234c1142ccSUwe Kleine-König static int tpl0102_probe(struct i2c_client *client)
1242edbd295SMatt Ranostay {
1254c1142ccSUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client);
1262edbd295SMatt Ranostay struct device *dev = &client->dev;
1272edbd295SMatt Ranostay struct tpl0102_data *data;
1282edbd295SMatt Ranostay struct iio_dev *indio_dev;
1292edbd295SMatt Ranostay
1302edbd295SMatt Ranostay indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
1312edbd295SMatt Ranostay if (!indio_dev)
1322edbd295SMatt Ranostay return -ENOMEM;
1332edbd295SMatt Ranostay data = iio_priv(indio_dev);
1342edbd295SMatt Ranostay i2c_set_clientdata(client, indio_dev);
1352edbd295SMatt Ranostay
136ee230351SMatt Ranostay data->cfg = &tpl0102_cfg[id->driver_data];
1372edbd295SMatt Ranostay data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
1382edbd295SMatt Ranostay if (IS_ERR(data->regmap)) {
1392edbd295SMatt Ranostay dev_err(dev, "regmap initialization failed\n");
1402edbd295SMatt Ranostay return PTR_ERR(data->regmap);
1412edbd295SMatt Ranostay }
1422edbd295SMatt Ranostay
1432edbd295SMatt Ranostay indio_dev->info = &tpl0102_info;
1442edbd295SMatt Ranostay indio_dev->channels = tpl0102_channels;
145ee230351SMatt Ranostay indio_dev->num_channels = data->cfg->wipers;
1462edbd295SMatt Ranostay indio_dev->name = client->name;
1472edbd295SMatt Ranostay
1482edbd295SMatt Ranostay return devm_iio_device_register(dev, indio_dev);
1492edbd295SMatt Ranostay }
1502edbd295SMatt Ranostay
1512edbd295SMatt Ranostay static const struct i2c_device_id tpl0102_id[] = {
1522edbd295SMatt Ranostay { "cat5140-503", CAT5140_503 },
1532edbd295SMatt Ranostay { "cat5140-104", CAT5140_104 },
1542edbd295SMatt Ranostay { "tpl0102-104", TPL0102_104 },
1552edbd295SMatt Ranostay { "tpl0401-103", TPL0401_103 },
1562edbd295SMatt Ranostay {}
1572edbd295SMatt Ranostay };
1582edbd295SMatt Ranostay MODULE_DEVICE_TABLE(i2c, tpl0102_id);
1592edbd295SMatt Ranostay
1602edbd295SMatt Ranostay static struct i2c_driver tpl0102_driver = {
1612edbd295SMatt Ranostay .driver = {
1622edbd295SMatt Ranostay .name = "tpl0102",
1632edbd295SMatt Ranostay },
164*7cf15f42SUwe Kleine-König .probe = tpl0102_probe,
1652edbd295SMatt Ranostay .id_table = tpl0102_id,
1662edbd295SMatt Ranostay };
1672edbd295SMatt Ranostay
1682edbd295SMatt Ranostay module_i2c_driver(tpl0102_driver);
1692edbd295SMatt Ranostay
170d6ad8058SMatt Ranostay MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
1712edbd295SMatt Ranostay MODULE_DESCRIPTION("TPL0102 digital potentiometer");
1722edbd295SMatt Ranostay MODULE_LICENSE("GPL");
173