linux/drivers/iio/potentiometer/tpl0102.c
<<
>>
Prefs
   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 max_pos;
  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, .max_pos = 256, .kohms = 50, },
  32        [CAT5140_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
  33        /* ti parts */
  34        [TPL0102_104] = { .wipers = 2, .max_pos = 256, .kohms = 100 },
  35        [TPL0401_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
  36};
  37
  38struct tpl0102_data {
  39        struct regmap *regmap;
  40        unsigned long devid;
  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}
  56
  57static const struct iio_chan_spec tpl0102_channels[] = {
  58        TPL0102_CHANNEL(0),
  59        TPL0102_CHANNEL(1),
  60};
  61
  62static int tpl0102_read_raw(struct iio_dev *indio_dev,
  63                            struct iio_chan_spec const *chan,
  64                            int *val, int *val2, long mask)
  65{
  66        struct tpl0102_data *data = iio_priv(indio_dev);
  67
  68        switch (mask) {
  69        case IIO_CHAN_INFO_RAW: {
  70                int ret = regmap_read(data->regmap, chan->channel, val);
  71
  72                return ret ? ret : IIO_VAL_INT;
  73        }
  74        case IIO_CHAN_INFO_SCALE:
  75                *val = 1000 * tpl0102_cfg[data->devid].kohms;
  76                *val2 = tpl0102_cfg[data->devid].max_pos;
  77                return IIO_VAL_FRACTIONAL;
  78        }
  79
  80        return -EINVAL;
  81}
  82
  83static int tpl0102_write_raw(struct iio_dev *indio_dev,
  84                             struct iio_chan_spec const *chan,
  85                             int val, int val2, long mask)
  86{
  87        struct tpl0102_data *data = iio_priv(indio_dev);
  88
  89        if (mask != IIO_CHAN_INFO_RAW)
  90                return -EINVAL;
  91
  92        if (val >= tpl0102_cfg[data->devid].max_pos || val < 0)
  93                return -EINVAL;
  94
  95        return regmap_write(data->regmap, chan->channel, val);
  96}
  97
  98static const struct iio_info tpl0102_info = {
  99        .read_raw = tpl0102_read_raw,
 100        .write_raw = tpl0102_write_raw,
 101};
 102
 103static int tpl0102_probe(struct i2c_client *client,
 104                         const struct i2c_device_id *id)
 105{
 106        struct device *dev = &client->dev;
 107        struct tpl0102_data *data;
 108        struct iio_dev *indio_dev;
 109
 110        indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
 111        if (!indio_dev)
 112                return -ENOMEM;
 113        data = iio_priv(indio_dev);
 114        i2c_set_clientdata(client, indio_dev);
 115
 116        data->devid = id->driver_data;
 117        data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
 118        if (IS_ERR(data->regmap)) {
 119                dev_err(dev, "regmap initialization failed\n");
 120                return PTR_ERR(data->regmap);
 121        }
 122
 123        indio_dev->dev.parent = dev;
 124        indio_dev->info = &tpl0102_info;
 125        indio_dev->channels = tpl0102_channels;
 126        indio_dev->num_channels = tpl0102_cfg[data->devid].wipers;
 127        indio_dev->name = client->name;
 128
 129        return devm_iio_device_register(dev, indio_dev);
 130}
 131
 132static const struct i2c_device_id tpl0102_id[] = {
 133        { "cat5140-503", CAT5140_503 },
 134        { "cat5140-104", CAT5140_104 },
 135        { "tpl0102-104", TPL0102_104 },
 136        { "tpl0401-103", TPL0401_103 },
 137        {}
 138};
 139MODULE_DEVICE_TABLE(i2c, tpl0102_id);
 140
 141static struct i2c_driver tpl0102_driver = {
 142        .driver = {
 143                .name = "tpl0102",
 144        },
 145        .probe = tpl0102_probe,
 146        .id_table = tpl0102_id,
 147};
 148
 149module_i2c_driver(tpl0102_driver);
 150
 151MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
 152MODULE_DESCRIPTION("TPL0102 digital potentiometer");
 153MODULE_LICENSE("GPL");
 154