linux/drivers/iio/potentiometer/tpl0102.c
<<
>>
Prefs
   1/*
   2 * tpl0102.c - Support for Texas Instruments digital potentiometers
   3 *
   4 * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 * GNU General Public License for more details.
  15 *
  16 * TODO: enable/disable hi-z output control
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/i2c.h>
  21#include <linux/regmap.h>
  22#include <linux/iio/iio.h>
  23
  24struct tpl0102_cfg {
  25        int wipers;
  26        int max_pos;
  27        int kohms;
  28};
  29
  30enum tpl0102_type {
  31        CAT5140_503,
  32        CAT5140_104,
  33        TPL0102_104,
  34        TPL0401_103,
  35};
  36
  37static const struct tpl0102_cfg tpl0102_cfg[] = {
  38        /* on-semiconductor parts */
  39        [CAT5140_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
  40        [CAT5140_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
  41        /* ti parts */
  42        [TPL0102_104] = { .wipers = 2, .max_pos = 256, .kohms = 100 },
  43        [TPL0401_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
  44};
  45
  46struct tpl0102_data {
  47        struct regmap *regmap;
  48        unsigned long devid;
  49};
  50
  51static const struct regmap_config tpl0102_regmap_config = {
  52        .reg_bits = 8,
  53        .val_bits = 8,
  54};
  55
  56#define TPL0102_CHANNEL(ch) {                                   \
  57        .type = IIO_RESISTANCE,                                 \
  58        .indexed = 1,                                           \
  59        .output = 1,                                            \
  60        .channel = (ch),                                        \
  61        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
  62        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
  63}
  64
  65static const struct iio_chan_spec tpl0102_channels[] = {
  66        TPL0102_CHANNEL(0),
  67        TPL0102_CHANNEL(1),
  68};
  69
  70static int tpl0102_read_raw(struct iio_dev *indio_dev,
  71                            struct iio_chan_spec const *chan,
  72                            int *val, int *val2, long mask)
  73{
  74        struct tpl0102_data *data = iio_priv(indio_dev);
  75
  76        switch (mask) {
  77        case IIO_CHAN_INFO_RAW: {
  78                int ret = regmap_read(data->regmap, chan->channel, val);
  79
  80                return ret ? ret : IIO_VAL_INT;
  81        }
  82        case IIO_CHAN_INFO_SCALE:
  83                *val = 1000 * tpl0102_cfg[data->devid].kohms;
  84                *val2 = tpl0102_cfg[data->devid].max_pos;
  85                return IIO_VAL_FRACTIONAL;
  86        }
  87
  88        return -EINVAL;
  89}
  90
  91static int tpl0102_write_raw(struct iio_dev *indio_dev,
  92                             struct iio_chan_spec const *chan,
  93                             int val, int val2, long mask)
  94{
  95        struct tpl0102_data *data = iio_priv(indio_dev);
  96
  97        if (mask != IIO_CHAN_INFO_RAW)
  98                return -EINVAL;
  99
 100        if (val >= tpl0102_cfg[data->devid].max_pos || val < 0)
 101                return -EINVAL;
 102
 103        return regmap_write(data->regmap, chan->channel, val);
 104}
 105
 106static const struct iio_info tpl0102_info = {
 107        .read_raw = tpl0102_read_raw,
 108        .write_raw = tpl0102_write_raw,
 109        .driver_module = THIS_MODULE,
 110};
 111
 112static int tpl0102_probe(struct i2c_client *client,
 113                         const struct i2c_device_id *id)
 114{
 115        struct device *dev = &client->dev;
 116        struct tpl0102_data *data;
 117        struct iio_dev *indio_dev;
 118
 119        if (!i2c_check_functionality(client->adapter,
 120                                     I2C_FUNC_SMBUS_WORD_DATA))
 121                return -ENOTSUPP;
 122
 123        indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
 124        if (!indio_dev)
 125                return -ENOMEM;
 126        data = iio_priv(indio_dev);
 127        i2c_set_clientdata(client, indio_dev);
 128
 129        data->devid = id->driver_data;
 130        data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
 131        if (IS_ERR(data->regmap)) {
 132                dev_err(dev, "regmap initialization failed\n");
 133                return PTR_ERR(data->regmap);
 134        }
 135
 136        indio_dev->dev.parent = dev;
 137        indio_dev->info = &tpl0102_info;
 138        indio_dev->channels = tpl0102_channels;
 139        indio_dev->num_channels = tpl0102_cfg[data->devid].wipers;
 140        indio_dev->name = client->name;
 141
 142        return devm_iio_device_register(dev, indio_dev);
 143}
 144
 145static const struct i2c_device_id tpl0102_id[] = {
 146        { "cat5140-503", CAT5140_503 },
 147        { "cat5140-104", CAT5140_104 },
 148        { "tpl0102-104", TPL0102_104 },
 149        { "tpl0401-103", TPL0401_103 },
 150        {}
 151};
 152MODULE_DEVICE_TABLE(i2c, tpl0102_id);
 153
 154static struct i2c_driver tpl0102_driver = {
 155        .driver = {
 156                .name = "tpl0102",
 157        },
 158        .probe = tpl0102_probe,
 159        .id_table = tpl0102_id,
 160};
 161
 162module_i2c_driver(tpl0102_driver);
 163
 164MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
 165MODULE_DESCRIPTION("TPL0102 digital potentiometer");
 166MODULE_LICENSE("GPL");
 167