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 16 struct tpl0102_cfg { 17 int wipers; 18 int avail[3]; 19 int kohms; 20 }; 21 22 enum tpl0102_type { 23 CAT5140_503, 24 CAT5140_104, 25 TPL0102_104, 26 TPL0401_103, 27 }; 28 29 static const struct tpl0102_cfg tpl0102_cfg[] = { 30 /* on-semiconductor parts */ 31 [CAT5140_503] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 50, }, 32 [CAT5140_104] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 100, }, 33 /* ti parts */ 34 [TPL0102_104] = { .wipers = 2, .avail = { 0, 1, 255 }, .kohms = 100 }, 35 [TPL0401_103] = { .wipers = 1, .avail = { 0, 1, 127 }, .kohms = 10, }, 36 }; 37 38 struct tpl0102_data { 39 struct regmap *regmap; 40 const struct tpl0102_cfg *cfg; 41 }; 42 43 static 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 .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \ 56 } 57 58 static const struct iio_chan_spec tpl0102_channels[] = { 59 TPL0102_CHANNEL(0), 60 TPL0102_CHANNEL(1), 61 }; 62 63 static int tpl0102_read_raw(struct iio_dev *indio_dev, 64 struct iio_chan_spec const *chan, 65 int *val, int *val2, long mask) 66 { 67 struct tpl0102_data *data = iio_priv(indio_dev); 68 69 switch (mask) { 70 case IIO_CHAN_INFO_RAW: { 71 int ret = regmap_read(data->regmap, chan->channel, val); 72 73 return ret ? ret : IIO_VAL_INT; 74 } 75 case IIO_CHAN_INFO_SCALE: 76 *val = 1000 * data->cfg->kohms; 77 *val2 = data->cfg->avail[2] + 1; 78 return IIO_VAL_FRACTIONAL; 79 } 80 81 return -EINVAL; 82 } 83 84 static int tpl0102_read_avail(struct iio_dev *indio_dev, 85 struct iio_chan_spec const *chan, 86 const int **vals, int *type, int *length, 87 long mask) 88 { 89 struct tpl0102_data *data = iio_priv(indio_dev); 90 91 switch (mask) { 92 case IIO_CHAN_INFO_RAW: 93 *length = ARRAY_SIZE(data->cfg->avail); 94 *vals = data->cfg->avail; 95 *type = IIO_VAL_INT; 96 return IIO_AVAIL_RANGE; 97 } 98 99 return -EINVAL; 100 } 101 102 static int tpl0102_write_raw(struct iio_dev *indio_dev, 103 struct iio_chan_spec const *chan, 104 int val, int val2, long mask) 105 { 106 struct tpl0102_data *data = iio_priv(indio_dev); 107 108 if (mask != IIO_CHAN_INFO_RAW) 109 return -EINVAL; 110 111 if (val > data->cfg->avail[2] || val < 0) 112 return -EINVAL; 113 114 return regmap_write(data->regmap, chan->channel, val); 115 } 116 117 static const struct iio_info tpl0102_info = { 118 .read_raw = tpl0102_read_raw, 119 .read_avail = tpl0102_read_avail, 120 .write_raw = tpl0102_write_raw, 121 }; 122 123 static int tpl0102_probe(struct i2c_client *client) 124 { 125 const struct i2c_device_id *id = i2c_client_get_device_id(client); 126 struct device *dev = &client->dev; 127 struct tpl0102_data *data; 128 struct iio_dev *indio_dev; 129 130 indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 131 if (!indio_dev) 132 return -ENOMEM; 133 data = iio_priv(indio_dev); 134 i2c_set_clientdata(client, indio_dev); 135 136 data->cfg = &tpl0102_cfg[id->driver_data]; 137 data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config); 138 if (IS_ERR(data->regmap)) { 139 dev_err(dev, "regmap initialization failed\n"); 140 return PTR_ERR(data->regmap); 141 } 142 143 indio_dev->info = &tpl0102_info; 144 indio_dev->channels = tpl0102_channels; 145 indio_dev->num_channels = data->cfg->wipers; 146 indio_dev->name = client->name; 147 148 return devm_iio_device_register(dev, indio_dev); 149 } 150 151 static const struct i2c_device_id tpl0102_id[] = { 152 { "cat5140-503", CAT5140_503 }, 153 { "cat5140-104", CAT5140_104 }, 154 { "tpl0102-104", TPL0102_104 }, 155 { "tpl0401-103", TPL0401_103 }, 156 {} 157 }; 158 MODULE_DEVICE_TABLE(i2c, tpl0102_id); 159 160 static struct i2c_driver tpl0102_driver = { 161 .driver = { 162 .name = "tpl0102", 163 }, 164 .probe_new = tpl0102_probe, 165 .id_table = tpl0102_id, 166 }; 167 168 module_i2c_driver(tpl0102_driver); 169 170 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); 171 MODULE_DESCRIPTION("TPL0102 digital potentiometer"); 172 MODULE_LICENSE("GPL"); 173