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 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 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 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 123*4c1142ccSUwe Kleine-König static int tpl0102_probe(struct i2c_client *client) 1242edbd295SMatt Ranostay { 125*4c1142ccSUwe 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*4c1142ccSUwe Kleine-König .probe_new = 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