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 max_pos; 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, .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 38 struct tpl0102_data { 39 struct regmap *regmap; 40 unsigned long devid; 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 } 56 57 static const struct iio_chan_spec tpl0102_channels[] = { 58 TPL0102_CHANNEL(0), 59 TPL0102_CHANNEL(1), 60 }; 61 62 static 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 83 static 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 98 static const struct iio_info tpl0102_info = { 99 .read_raw = tpl0102_read_raw, 100 .write_raw = tpl0102_write_raw, 101 }; 102 103 static 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 132 static 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 }; 139 MODULE_DEVICE_TABLE(i2c, tpl0102_id); 140 141 static struct i2c_driver tpl0102_driver = { 142 .driver = { 143 .name = "tpl0102", 144 }, 145 .probe = tpl0102_probe, 146 .id_table = tpl0102_id, 147 }; 148 149 module_i2c_driver(tpl0102_driver); 150 151 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); 152 MODULE_DESCRIPTION("TPL0102 digital potentiometer"); 153 MODULE_LICENSE("GPL"); 154