Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * tpl0102.c - Support for Texas Instruments digital potentiometers
0004  *
0005  * Copyright (C) 2016, 2018
0006  * Author: Matt Ranostay <matt.ranostay@konsulko.com>
0007  *
0008  * TODO: enable/disable hi-z output control
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/i2c.h>
0013 #include <linux/regmap.h>
0014 #include <linux/iio/iio.h>
0015 
0016 struct tpl0102_cfg {
0017     int wipers;
0018     int avail[3];
0019     int kohms;
0020 };
0021 
0022 enum tpl0102_type {
0023     CAT5140_503,
0024     CAT5140_104,
0025     TPL0102_104,
0026     TPL0401_103,
0027 };
0028 
0029 static const struct tpl0102_cfg tpl0102_cfg[] = {
0030     /* on-semiconductor parts */
0031     [CAT5140_503] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 50, },
0032     [CAT5140_104] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 100, },
0033     /* ti parts */
0034     [TPL0102_104] = { .wipers = 2, .avail = { 0, 1, 255 }, .kohms = 100 },
0035     [TPL0401_103] = { .wipers = 1, .avail = { 0, 1, 127 }, .kohms = 10, },
0036 };
0037 
0038 struct tpl0102_data {
0039     struct regmap *regmap;
0040     const struct tpl0102_cfg *cfg;
0041 };
0042 
0043 static const struct regmap_config tpl0102_regmap_config = {
0044     .reg_bits = 8,
0045     .val_bits = 8,
0046 };
0047 
0048 #define TPL0102_CHANNEL(ch) {                   \
0049     .type = IIO_RESISTANCE,                 \
0050     .indexed = 1,                       \
0051     .output = 1,                        \
0052     .channel = (ch),                    \
0053     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),       \
0054     .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
0055     .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \
0056 }
0057 
0058 static const struct iio_chan_spec tpl0102_channels[] = {
0059     TPL0102_CHANNEL(0),
0060     TPL0102_CHANNEL(1),
0061 };
0062 
0063 static int tpl0102_read_raw(struct iio_dev *indio_dev,
0064                 struct iio_chan_spec const *chan,
0065                 int *val, int *val2, long mask)
0066 {
0067     struct tpl0102_data *data = iio_priv(indio_dev);
0068 
0069     switch (mask) {
0070     case IIO_CHAN_INFO_RAW: {
0071         int ret = regmap_read(data->regmap, chan->channel, val);
0072 
0073         return ret ? ret : IIO_VAL_INT;
0074     }
0075     case IIO_CHAN_INFO_SCALE:
0076         *val = 1000 * data->cfg->kohms;
0077         *val2 = data->cfg->avail[2] + 1;
0078         return IIO_VAL_FRACTIONAL;
0079     }
0080 
0081     return -EINVAL;
0082 }
0083 
0084 static int tpl0102_read_avail(struct iio_dev *indio_dev,
0085                   struct iio_chan_spec const *chan,
0086                   const int **vals, int *type, int *length,
0087                   long mask)
0088 {
0089     struct tpl0102_data *data = iio_priv(indio_dev);
0090 
0091     switch (mask) {
0092     case IIO_CHAN_INFO_RAW:
0093         *length = ARRAY_SIZE(data->cfg->avail);
0094         *vals = data->cfg->avail;
0095         *type = IIO_VAL_INT;
0096         return IIO_AVAIL_RANGE;
0097     }
0098 
0099     return -EINVAL;
0100 }
0101 
0102 static int tpl0102_write_raw(struct iio_dev *indio_dev,
0103                  struct iio_chan_spec const *chan,
0104                  int val, int val2, long mask)
0105 {
0106     struct tpl0102_data *data = iio_priv(indio_dev);
0107 
0108     if (mask != IIO_CHAN_INFO_RAW)
0109         return -EINVAL;
0110 
0111     if (val > data->cfg->avail[2] || val < 0)
0112         return -EINVAL;
0113 
0114     return regmap_write(data->regmap, chan->channel, val);
0115 }
0116 
0117 static const struct iio_info tpl0102_info = {
0118     .read_raw = tpl0102_read_raw,
0119     .read_avail = tpl0102_read_avail,
0120     .write_raw = tpl0102_write_raw,
0121 };
0122 
0123 static int tpl0102_probe(struct i2c_client *client,
0124              const struct i2c_device_id *id)
0125 {
0126     struct device *dev = &client->dev;
0127     struct tpl0102_data *data;
0128     struct iio_dev *indio_dev;
0129 
0130     indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
0131     if (!indio_dev)
0132         return -ENOMEM;
0133     data = iio_priv(indio_dev);
0134     i2c_set_clientdata(client, indio_dev);
0135 
0136     data->cfg = &tpl0102_cfg[id->driver_data];
0137     data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
0138     if (IS_ERR(data->regmap)) {
0139         dev_err(dev, "regmap initialization failed\n");
0140         return PTR_ERR(data->regmap);
0141     }
0142 
0143     indio_dev->info = &tpl0102_info;
0144     indio_dev->channels = tpl0102_channels;
0145     indio_dev->num_channels = data->cfg->wipers;
0146     indio_dev->name = client->name;
0147 
0148     return devm_iio_device_register(dev, indio_dev);
0149 }
0150 
0151 static const struct i2c_device_id tpl0102_id[] = {
0152     { "cat5140-503", CAT5140_503 },
0153     { "cat5140-104", CAT5140_104 },
0154     { "tpl0102-104", TPL0102_104 },
0155     { "tpl0401-103", TPL0401_103 },
0156     {}
0157 };
0158 MODULE_DEVICE_TABLE(i2c, tpl0102_id);
0159 
0160 static struct i2c_driver tpl0102_driver = {
0161     .driver = {
0162         .name = "tpl0102",
0163     },
0164     .probe = tpl0102_probe,
0165     .id_table = tpl0102_id,
0166 };
0167 
0168 module_i2c_driver(tpl0102_driver);
0169 
0170 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
0171 MODULE_DESCRIPTION("TPL0102 digital potentiometer");
0172 MODULE_LICENSE("GPL");