400 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Industrial I/O driver for Microchip digital potentiometers4 * Copyright (c) 2015 Axentia Technologies AB5 * Author: Peter Rosin <peda@axentia.se>6 *7 * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf8 *9 * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address10 * mcp4531 1 129 5, 10, 50, 100 010111x11 * mcp4532 1 129 5, 10, 50, 100 01011xx12 * mcp4541 1 129 5, 10, 50, 100 010111x13 * mcp4542 1 129 5, 10, 50, 100 01011xx14 * mcp4551 1 257 5, 10, 50, 100 010111x15 * mcp4552 1 257 5, 10, 50, 100 01011xx16 * mcp4561 1 257 5, 10, 50, 100 010111x17 * mcp4562 1 257 5, 10, 50, 100 01011xx18 * mcp4631 2 129 5, 10, 50, 100 0101xxx19 * mcp4632 2 129 5, 10, 50, 100 01011xx20 * mcp4641 2 129 5, 10, 50, 100 0101xxx21 * mcp4642 2 129 5, 10, 50, 100 01011xx22 * mcp4651 2 257 5, 10, 50, 100 0101xxx23 * mcp4652 2 257 5, 10, 50, 100 01011xx24 * mcp4661 2 257 5, 10, 50, 100 0101xxx25 * mcp4662 2 257 5, 10, 50, 100 01011xx26 */27 28#include <linux/module.h>29#include <linux/i2c.h>30#include <linux/err.h>31#include <linux/mod_devicetable.h>32#include <linux/property.h>33 34#include <linux/iio/iio.h>35 36struct mcp4531_cfg {37 int wipers;38 int avail[3];39 int kohms;40};41 42enum mcp4531_type {43 MCP453x_502,44 MCP453x_103,45 MCP453x_503,46 MCP453x_104,47 MCP454x_502,48 MCP454x_103,49 MCP454x_503,50 MCP454x_104,51 MCP455x_502,52 MCP455x_103,53 MCP455x_503,54 MCP455x_104,55 MCP456x_502,56 MCP456x_103,57 MCP456x_503,58 MCP456x_104,59 MCP463x_502,60 MCP463x_103,61 MCP463x_503,62 MCP463x_104,63 MCP464x_502,64 MCP464x_103,65 MCP464x_503,66 MCP464x_104,67 MCP465x_502,68 MCP465x_103,69 MCP465x_503,70 MCP465x_104,71 MCP466x_502,72 MCP466x_103,73 MCP466x_503,74 MCP466x_104,75};76 77static const struct mcp4531_cfg mcp4531_cfg[] = {78 [MCP453x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 5, },79 [MCP453x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 10, },80 [MCP453x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 50, },81 [MCP453x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },82 [MCP454x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 5, },83 [MCP454x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 10, },84 [MCP454x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 50, },85 [MCP454x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },86 [MCP455x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 5, },87 [MCP455x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 10, },88 [MCP455x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 50, },89 [MCP455x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },90 [MCP456x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 5, },91 [MCP456x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 10, },92 [MCP456x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 50, },93 [MCP456x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },94 [MCP463x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 5, },95 [MCP463x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 10, },96 [MCP463x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 50, },97 [MCP463x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },98 [MCP464x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 5, },99 [MCP464x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 10, },100 [MCP464x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 50, },101 [MCP464x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },102 [MCP465x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 5, },103 [MCP465x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 10, },104 [MCP465x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 50, },105 [MCP465x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },106 [MCP466x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 5, },107 [MCP466x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 10, },108 [MCP466x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 50, },109 [MCP466x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },110};111 112#define MCP4531_WRITE (0 << 2)113#define MCP4531_INCR (1 << 2)114#define MCP4531_DECR (2 << 2)115#define MCP4531_READ (3 << 2)116 117#define MCP4531_WIPER_SHIFT (4)118 119struct mcp4531_data {120 struct i2c_client *client;121 const struct mcp4531_cfg *cfg;122};123 124#define MCP4531_CHANNEL(ch) { \125 .type = IIO_RESISTANCE, \126 .indexed = 1, \127 .output = 1, \128 .channel = (ch), \129 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \130 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \131 .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW), \132}133 134static const struct iio_chan_spec mcp4531_channels[] = {135 MCP4531_CHANNEL(0),136 MCP4531_CHANNEL(1),137};138 139static int mcp4531_read_raw(struct iio_dev *indio_dev,140 struct iio_chan_spec const *chan,141 int *val, int *val2, long mask)142{143 struct mcp4531_data *data = iio_priv(indio_dev);144 int address = chan->channel << MCP4531_WIPER_SHIFT;145 s32 ret;146 147 switch (mask) {148 case IIO_CHAN_INFO_RAW:149 ret = i2c_smbus_read_word_swapped(data->client,150 MCP4531_READ | address);151 if (ret < 0)152 return ret;153 *val = ret;154 return IIO_VAL_INT;155 case IIO_CHAN_INFO_SCALE:156 *val = 1000 * data->cfg->kohms;157 *val2 = data->cfg->avail[2];158 return IIO_VAL_FRACTIONAL;159 }160 161 return -EINVAL;162}163 164static int mcp4531_read_avail(struct iio_dev *indio_dev,165 struct iio_chan_spec const *chan,166 const int **vals, int *type, int *length,167 long mask)168{169 struct mcp4531_data *data = iio_priv(indio_dev);170 171 switch (mask) {172 case IIO_CHAN_INFO_RAW:173 *length = ARRAY_SIZE(data->cfg->avail);174 *vals = data->cfg->avail;175 *type = IIO_VAL_INT;176 return IIO_AVAIL_RANGE;177 }178 179 return -EINVAL;180}181 182static int mcp4531_write_raw(struct iio_dev *indio_dev,183 struct iio_chan_spec const *chan,184 int val, int val2, long mask)185{186 struct mcp4531_data *data = iio_priv(indio_dev);187 int address = chan->channel << MCP4531_WIPER_SHIFT;188 189 switch (mask) {190 case IIO_CHAN_INFO_RAW:191 if (val > data->cfg->avail[2] || val < 0)192 return -EINVAL;193 break;194 default:195 return -EINVAL;196 }197 198 return i2c_smbus_write_byte_data(data->client,199 MCP4531_WRITE | address | (val >> 8),200 val & 0xff);201}202 203static const struct iio_info mcp4531_info = {204 .read_raw = mcp4531_read_raw,205 .read_avail = mcp4531_read_avail,206 .write_raw = mcp4531_write_raw,207};208 209#define MCP4531_ID_TABLE(_name, cfg) { \210 .name = _name, \211 .driver_data = (kernel_ulong_t)&mcp4531_cfg[cfg], \212}213 214static const struct i2c_device_id mcp4531_id[] = {215 MCP4531_ID_TABLE("mcp4531-502", MCP453x_502),216 MCP4531_ID_TABLE("mcp4531-103", MCP453x_103),217 MCP4531_ID_TABLE("mcp4531-503", MCP453x_503),218 MCP4531_ID_TABLE("mcp4531-104", MCP453x_104),219 MCP4531_ID_TABLE("mcp4532-502", MCP453x_502),220 MCP4531_ID_TABLE("mcp4532-103", MCP453x_103),221 MCP4531_ID_TABLE("mcp4532-503", MCP453x_503),222 MCP4531_ID_TABLE("mcp4532-104", MCP453x_104),223 MCP4531_ID_TABLE("mcp4541-502", MCP454x_502),224 MCP4531_ID_TABLE("mcp4541-103", MCP454x_103),225 MCP4531_ID_TABLE("mcp4541-503", MCP454x_503),226 MCP4531_ID_TABLE("mcp4541-104", MCP454x_104),227 MCP4531_ID_TABLE("mcp4542-502", MCP454x_502),228 MCP4531_ID_TABLE("mcp4542-103", MCP454x_103),229 MCP4531_ID_TABLE("mcp4542-503", MCP454x_503),230 MCP4531_ID_TABLE("mcp4542-104", MCP454x_104),231 MCP4531_ID_TABLE("mcp4551-502", MCP455x_502),232 MCP4531_ID_TABLE("mcp4551-103", MCP455x_103),233 MCP4531_ID_TABLE("mcp4551-503", MCP455x_503),234 MCP4531_ID_TABLE("mcp4551-104", MCP455x_104),235 MCP4531_ID_TABLE("mcp4552-502", MCP455x_502),236 MCP4531_ID_TABLE("mcp4552-103", MCP455x_103),237 MCP4531_ID_TABLE("mcp4552-503", MCP455x_503),238 MCP4531_ID_TABLE("mcp4552-104", MCP455x_104),239 MCP4531_ID_TABLE("mcp4561-502", MCP456x_502),240 MCP4531_ID_TABLE("mcp4561-103", MCP456x_103),241 MCP4531_ID_TABLE("mcp4561-503", MCP456x_503),242 MCP4531_ID_TABLE("mcp4561-104", MCP456x_104),243 MCP4531_ID_TABLE("mcp4562-502", MCP456x_502),244 MCP4531_ID_TABLE("mcp4562-103", MCP456x_103),245 MCP4531_ID_TABLE("mcp4562-503", MCP456x_503),246 MCP4531_ID_TABLE("mcp4562-104", MCP456x_104),247 MCP4531_ID_TABLE("mcp4631-502", MCP463x_502),248 MCP4531_ID_TABLE("mcp4631-103", MCP463x_103),249 MCP4531_ID_TABLE("mcp4631-503", MCP463x_503),250 MCP4531_ID_TABLE("mcp4631-104", MCP463x_104),251 MCP4531_ID_TABLE("mcp4632-502", MCP463x_502),252 MCP4531_ID_TABLE("mcp4632-103", MCP463x_103),253 MCP4531_ID_TABLE("mcp4632-503", MCP463x_503),254 MCP4531_ID_TABLE("mcp4632-104", MCP463x_104),255 MCP4531_ID_TABLE("mcp4641-502", MCP464x_502),256 MCP4531_ID_TABLE("mcp4641-103", MCP464x_103),257 MCP4531_ID_TABLE("mcp4641-503", MCP464x_503),258 MCP4531_ID_TABLE("mcp4641-104", MCP464x_104),259 MCP4531_ID_TABLE("mcp4642-502", MCP464x_502),260 MCP4531_ID_TABLE("mcp4642-103", MCP464x_103),261 MCP4531_ID_TABLE("mcp4642-503", MCP464x_503),262 MCP4531_ID_TABLE("mcp4642-104", MCP464x_104),263 MCP4531_ID_TABLE("mcp4651-502", MCP465x_502),264 MCP4531_ID_TABLE("mcp4651-103", MCP465x_103),265 MCP4531_ID_TABLE("mcp4651-503", MCP465x_503),266 MCP4531_ID_TABLE("mcp4651-104", MCP465x_104),267 MCP4531_ID_TABLE("mcp4652-502", MCP465x_502),268 MCP4531_ID_TABLE("mcp4652-103", MCP465x_103),269 MCP4531_ID_TABLE("mcp4652-503", MCP465x_503),270 MCP4531_ID_TABLE("mcp4652-104", MCP465x_104),271 MCP4531_ID_TABLE("mcp4661-502", MCP466x_502),272 MCP4531_ID_TABLE("mcp4661-103", MCP466x_103),273 MCP4531_ID_TABLE("mcp4661-503", MCP466x_503),274 MCP4531_ID_TABLE("mcp4661-104", MCP466x_104),275 MCP4531_ID_TABLE("mcp4662-502", MCP466x_502),276 MCP4531_ID_TABLE("mcp4662-103", MCP466x_103),277 MCP4531_ID_TABLE("mcp4662-503", MCP466x_503),278 MCP4531_ID_TABLE("mcp4662-104", MCP466x_104),279 { /* sentinel */ }280};281MODULE_DEVICE_TABLE(i2c, mcp4531_id);282 283#define MCP4531_COMPATIBLE(of_compatible, cfg) { \284 .compatible = of_compatible, \285 .data = &mcp4531_cfg[cfg], \286}287 288static const struct of_device_id mcp4531_of_match[] = {289 MCP4531_COMPATIBLE("microchip,mcp4531-502", MCP453x_502),290 MCP4531_COMPATIBLE("microchip,mcp4531-103", MCP453x_103),291 MCP4531_COMPATIBLE("microchip,mcp4531-503", MCP453x_503),292 MCP4531_COMPATIBLE("microchip,mcp4531-104", MCP453x_104),293 MCP4531_COMPATIBLE("microchip,mcp4532-502", MCP453x_502),294 MCP4531_COMPATIBLE("microchip,mcp4532-103", MCP453x_103),295 MCP4531_COMPATIBLE("microchip,mcp4532-503", MCP453x_503),296 MCP4531_COMPATIBLE("microchip,mcp4532-104", MCP453x_104),297 MCP4531_COMPATIBLE("microchip,mcp4541-502", MCP454x_502),298 MCP4531_COMPATIBLE("microchip,mcp4541-103", MCP454x_103),299 MCP4531_COMPATIBLE("microchip,mcp4541-503", MCP454x_503),300 MCP4531_COMPATIBLE("microchip,mcp4541-104", MCP454x_104),301 MCP4531_COMPATIBLE("microchip,mcp4542-502", MCP454x_502),302 MCP4531_COMPATIBLE("microchip,mcp4542-103", MCP454x_103),303 MCP4531_COMPATIBLE("microchip,mcp4542-503", MCP454x_503),304 MCP4531_COMPATIBLE("microchip,mcp4542-104", MCP454x_104),305 MCP4531_COMPATIBLE("microchip,mcp4551-502", MCP455x_502),306 MCP4531_COMPATIBLE("microchip,mcp4551-103", MCP455x_103),307 MCP4531_COMPATIBLE("microchip,mcp4551-503", MCP455x_503),308 MCP4531_COMPATIBLE("microchip,mcp4551-104", MCP455x_104),309 MCP4531_COMPATIBLE("microchip,mcp4552-502", MCP455x_502),310 MCP4531_COMPATIBLE("microchip,mcp4552-103", MCP455x_103),311 MCP4531_COMPATIBLE("microchip,mcp4552-503", MCP455x_503),312 MCP4531_COMPATIBLE("microchip,mcp4552-104", MCP455x_104),313 MCP4531_COMPATIBLE("microchip,mcp4561-502", MCP456x_502),314 MCP4531_COMPATIBLE("microchip,mcp4561-103", MCP456x_103),315 MCP4531_COMPATIBLE("microchip,mcp4561-503", MCP456x_503),316 MCP4531_COMPATIBLE("microchip,mcp4561-104", MCP456x_104),317 MCP4531_COMPATIBLE("microchip,mcp4562-502", MCP456x_502),318 MCP4531_COMPATIBLE("microchip,mcp4562-103", MCP456x_103),319 MCP4531_COMPATIBLE("microchip,mcp4562-503", MCP456x_503),320 MCP4531_COMPATIBLE("microchip,mcp4562-104", MCP456x_104),321 MCP4531_COMPATIBLE("microchip,mcp4631-502", MCP463x_502),322 MCP4531_COMPATIBLE("microchip,mcp4631-103", MCP463x_103),323 MCP4531_COMPATIBLE("microchip,mcp4631-503", MCP463x_503),324 MCP4531_COMPATIBLE("microchip,mcp4631-104", MCP463x_104),325 MCP4531_COMPATIBLE("microchip,mcp4632-502", MCP463x_502),326 MCP4531_COMPATIBLE("microchip,mcp4632-103", MCP463x_103),327 MCP4531_COMPATIBLE("microchip,mcp4632-503", MCP463x_503),328 MCP4531_COMPATIBLE("microchip,mcp4632-104", MCP463x_104),329 MCP4531_COMPATIBLE("microchip,mcp4641-502", MCP464x_502),330 MCP4531_COMPATIBLE("microchip,mcp4641-103", MCP464x_103),331 MCP4531_COMPATIBLE("microchip,mcp4641-503", MCP464x_503),332 MCP4531_COMPATIBLE("microchip,mcp4641-104", MCP464x_104),333 MCP4531_COMPATIBLE("microchip,mcp4642-502", MCP464x_502),334 MCP4531_COMPATIBLE("microchip,mcp4642-103", MCP464x_103),335 MCP4531_COMPATIBLE("microchip,mcp4642-503", MCP464x_503),336 MCP4531_COMPATIBLE("microchip,mcp4642-104", MCP464x_104),337 MCP4531_COMPATIBLE("microchip,mcp4651-502", MCP465x_502),338 MCP4531_COMPATIBLE("microchip,mcp4651-103", MCP465x_103),339 MCP4531_COMPATIBLE("microchip,mcp4651-503", MCP465x_503),340 MCP4531_COMPATIBLE("microchip,mcp4651-104", MCP465x_104),341 MCP4531_COMPATIBLE("microchip,mcp4652-502", MCP465x_502),342 MCP4531_COMPATIBLE("microchip,mcp4652-103", MCP465x_103),343 MCP4531_COMPATIBLE("microchip,mcp4652-503", MCP465x_503),344 MCP4531_COMPATIBLE("microchip,mcp4652-104", MCP465x_104),345 MCP4531_COMPATIBLE("microchip,mcp4661-502", MCP466x_502),346 MCP4531_COMPATIBLE("microchip,mcp4661-103", MCP466x_103),347 MCP4531_COMPATIBLE("microchip,mcp4661-503", MCP466x_503),348 MCP4531_COMPATIBLE("microchip,mcp4661-104", MCP466x_104),349 MCP4531_COMPATIBLE("microchip,mcp4662-502", MCP466x_502),350 MCP4531_COMPATIBLE("microchip,mcp4662-103", MCP466x_103),351 MCP4531_COMPATIBLE("microchip,mcp4662-503", MCP466x_503),352 MCP4531_COMPATIBLE("microchip,mcp4662-104", MCP466x_104),353 { /* sentinel */ }354};355MODULE_DEVICE_TABLE(of, mcp4531_of_match);356 357static int mcp4531_probe(struct i2c_client *client)358{359 struct device *dev = &client->dev;360 struct mcp4531_data *data;361 struct iio_dev *indio_dev;362 363 if (!i2c_check_functionality(client->adapter,364 I2C_FUNC_SMBUS_WORD_DATA)) {365 dev_err(dev, "SMBUS Word Data not supported\n");366 return -EOPNOTSUPP;367 }368 369 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));370 if (!indio_dev)371 return -ENOMEM;372 data = iio_priv(indio_dev);373 i2c_set_clientdata(client, indio_dev);374 data->client = client;375 376 data->cfg = i2c_get_match_data(client);377 378 indio_dev->info = &mcp4531_info;379 indio_dev->channels = mcp4531_channels;380 indio_dev->num_channels = data->cfg->wipers;381 indio_dev->name = client->name;382 383 return devm_iio_device_register(dev, indio_dev);384}385 386static struct i2c_driver mcp4531_driver = {387 .driver = {388 .name = "mcp4531",389 .of_match_table = mcp4531_of_match,390 },391 .probe = mcp4531_probe,392 .id_table = mcp4531_id,393};394 395module_i2c_driver(mcp4531_driver);396 397MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");398MODULE_DESCRIPTION("MCP4531 digital potentiometer");399MODULE_LICENSE("GPL v2");400