15214ad6dSPeter Rosin // SPDX-License-Identifier: GPL-2.0 25214ad6dSPeter Rosin /* 35214ad6dSPeter Rosin * Industrial I/O driver for Microchip digital potentiometers 45214ad6dSPeter Rosin * Copyright (c) 2018 Axentia Technologies AB 55214ad6dSPeter Rosin * Author: Peter Rosin <peda@axentia.se> 65214ad6dSPeter Rosin * 75214ad6dSPeter Rosin * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22147a.pdf 85214ad6dSPeter Rosin * 95214ad6dSPeter Rosin * DEVID #Wipers #Positions Resistor Opts (kOhm) 105214ad6dSPeter Rosin * mcp4017 1 128 5, 10, 50, 100 115214ad6dSPeter Rosin * mcp4018 1 128 5, 10, 50, 100 125214ad6dSPeter Rosin * mcp4019 1 128 5, 10, 50, 100 135214ad6dSPeter Rosin */ 145214ad6dSPeter Rosin 155214ad6dSPeter Rosin #include <linux/err.h> 165214ad6dSPeter Rosin #include <linux/i2c.h> 175214ad6dSPeter Rosin #include <linux/iio/iio.h> 185214ad6dSPeter Rosin #include <linux/module.h> 195214ad6dSPeter Rosin #include <linux/of.h> 205214ad6dSPeter Rosin #include <linux/of_device.h> 215214ad6dSPeter Rosin 225214ad6dSPeter Rosin #define MCP4018_WIPER_MAX 127 235214ad6dSPeter Rosin 245214ad6dSPeter Rosin struct mcp4018_cfg { 255214ad6dSPeter Rosin int kohms; 265214ad6dSPeter Rosin }; 275214ad6dSPeter Rosin 285214ad6dSPeter Rosin enum mcp4018_type { 295214ad6dSPeter Rosin MCP4018_502, 305214ad6dSPeter Rosin MCP4018_103, 315214ad6dSPeter Rosin MCP4018_503, 325214ad6dSPeter Rosin MCP4018_104, 335214ad6dSPeter Rosin }; 345214ad6dSPeter Rosin 355214ad6dSPeter Rosin static const struct mcp4018_cfg mcp4018_cfg[] = { 365214ad6dSPeter Rosin [MCP4018_502] = { .kohms = 5, }, 375214ad6dSPeter Rosin [MCP4018_103] = { .kohms = 10, }, 385214ad6dSPeter Rosin [MCP4018_503] = { .kohms = 50, }, 395214ad6dSPeter Rosin [MCP4018_104] = { .kohms = 100, }, 405214ad6dSPeter Rosin }; 415214ad6dSPeter Rosin 425214ad6dSPeter Rosin struct mcp4018_data { 435214ad6dSPeter Rosin struct i2c_client *client; 445214ad6dSPeter Rosin const struct mcp4018_cfg *cfg; 455214ad6dSPeter Rosin }; 465214ad6dSPeter Rosin 475214ad6dSPeter Rosin static const struct iio_chan_spec mcp4018_channel = { 485214ad6dSPeter Rosin .type = IIO_RESISTANCE, 495214ad6dSPeter Rosin .indexed = 1, 505214ad6dSPeter Rosin .output = 1, 515214ad6dSPeter Rosin .channel = 0, 525214ad6dSPeter Rosin .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 535214ad6dSPeter Rosin .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 545214ad6dSPeter Rosin }; 555214ad6dSPeter Rosin 565214ad6dSPeter Rosin static int mcp4018_read_raw(struct iio_dev *indio_dev, 575214ad6dSPeter Rosin struct iio_chan_spec const *chan, 585214ad6dSPeter Rosin int *val, int *val2, long mask) 595214ad6dSPeter Rosin { 605214ad6dSPeter Rosin struct mcp4018_data *data = iio_priv(indio_dev); 615214ad6dSPeter Rosin s32 ret; 625214ad6dSPeter Rosin 635214ad6dSPeter Rosin switch (mask) { 645214ad6dSPeter Rosin case IIO_CHAN_INFO_RAW: 655214ad6dSPeter Rosin ret = i2c_smbus_read_byte(data->client); 665214ad6dSPeter Rosin if (ret < 0) 675214ad6dSPeter Rosin return ret; 685214ad6dSPeter Rosin *val = ret; 695214ad6dSPeter Rosin return IIO_VAL_INT; 705214ad6dSPeter Rosin case IIO_CHAN_INFO_SCALE: 715214ad6dSPeter Rosin *val = 1000 * data->cfg->kohms; 725214ad6dSPeter Rosin *val2 = MCP4018_WIPER_MAX; 735214ad6dSPeter Rosin return IIO_VAL_FRACTIONAL; 745214ad6dSPeter Rosin } 755214ad6dSPeter Rosin 765214ad6dSPeter Rosin return -EINVAL; 775214ad6dSPeter Rosin } 785214ad6dSPeter Rosin 795214ad6dSPeter Rosin static int mcp4018_write_raw(struct iio_dev *indio_dev, 805214ad6dSPeter Rosin struct iio_chan_spec const *chan, 815214ad6dSPeter Rosin int val, int val2, long mask) 825214ad6dSPeter Rosin { 835214ad6dSPeter Rosin struct mcp4018_data *data = iio_priv(indio_dev); 845214ad6dSPeter Rosin 855214ad6dSPeter Rosin switch (mask) { 865214ad6dSPeter Rosin case IIO_CHAN_INFO_RAW: 875214ad6dSPeter Rosin if (val > MCP4018_WIPER_MAX || val < 0) 885214ad6dSPeter Rosin return -EINVAL; 895214ad6dSPeter Rosin break; 905214ad6dSPeter Rosin default: 915214ad6dSPeter Rosin return -EINVAL; 925214ad6dSPeter Rosin } 935214ad6dSPeter Rosin 945214ad6dSPeter Rosin return i2c_smbus_write_byte(data->client, val); 955214ad6dSPeter Rosin } 965214ad6dSPeter Rosin 975214ad6dSPeter Rosin static const struct iio_info mcp4018_info = { 985214ad6dSPeter Rosin .read_raw = mcp4018_read_raw, 995214ad6dSPeter Rosin .write_raw = mcp4018_write_raw, 1005214ad6dSPeter Rosin }; 1015214ad6dSPeter Rosin 1020f6f400eSPeter Rosin static const struct i2c_device_id mcp4018_id[] = { 1030f6f400eSPeter Rosin { "mcp4017-502", MCP4018_502 }, 1040f6f400eSPeter Rosin { "mcp4017-103", MCP4018_103 }, 1050f6f400eSPeter Rosin { "mcp4017-503", MCP4018_503 }, 1060f6f400eSPeter Rosin { "mcp4017-104", MCP4018_104 }, 1070f6f400eSPeter Rosin { "mcp4018-502", MCP4018_502 }, 1080f6f400eSPeter Rosin { "mcp4018-103", MCP4018_103 }, 1090f6f400eSPeter Rosin { "mcp4018-503", MCP4018_503 }, 1100f6f400eSPeter Rosin { "mcp4018-104", MCP4018_104 }, 1110f6f400eSPeter Rosin { "mcp4019-502", MCP4018_502 }, 1120f6f400eSPeter Rosin { "mcp4019-103", MCP4018_103 }, 1130f6f400eSPeter Rosin { "mcp4019-503", MCP4018_503 }, 1140f6f400eSPeter Rosin { "mcp4019-104", MCP4018_104 }, 1150f6f400eSPeter Rosin {} 1160f6f400eSPeter Rosin }; 1170f6f400eSPeter Rosin MODULE_DEVICE_TABLE(i2c, mcp4018_id); 1180f6f400eSPeter Rosin 1195214ad6dSPeter Rosin #ifdef CONFIG_OF 1205214ad6dSPeter Rosin 1215214ad6dSPeter Rosin #define MCP4018_COMPATIBLE(of_compatible, cfg) { \ 1225214ad6dSPeter Rosin .compatible = of_compatible, \ 1235214ad6dSPeter Rosin .data = &mcp4018_cfg[cfg], \ 1245214ad6dSPeter Rosin } 1255214ad6dSPeter Rosin 1265214ad6dSPeter Rosin static const struct of_device_id mcp4018_of_match[] = { 1275214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4017-502", MCP4018_502), 1285214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4017-103", MCP4018_103), 1295214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4017-503", MCP4018_503), 1305214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4017-104", MCP4018_104), 1315214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4018-502", MCP4018_502), 1325214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4018-103", MCP4018_103), 1335214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4018-503", MCP4018_503), 1345214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4018-104", MCP4018_104), 1355214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4019-502", MCP4018_502), 1365214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4019-103", MCP4018_103), 1375214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4019-503", MCP4018_503), 1385214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4019-104", MCP4018_104), 1395214ad6dSPeter Rosin { /* sentinel */ } 1405214ad6dSPeter Rosin }; 1415214ad6dSPeter Rosin MODULE_DEVICE_TABLE(of, mcp4018_of_match); 1425214ad6dSPeter Rosin 1435214ad6dSPeter Rosin #endif 1445214ad6dSPeter Rosin 1450f6f400eSPeter Rosin static int mcp4018_probe(struct i2c_client *client) 1465214ad6dSPeter Rosin { 1475214ad6dSPeter Rosin struct device *dev = &client->dev; 1485214ad6dSPeter Rosin struct mcp4018_data *data; 1495214ad6dSPeter Rosin struct iio_dev *indio_dev; 1505214ad6dSPeter Rosin 1515214ad6dSPeter Rosin if (!i2c_check_functionality(client->adapter, 1525214ad6dSPeter Rosin I2C_FUNC_SMBUS_BYTE)) { 1535214ad6dSPeter Rosin dev_err(dev, "SMBUS Byte transfers not supported\n"); 1545214ad6dSPeter Rosin return -EOPNOTSUPP; 1555214ad6dSPeter Rosin } 1565214ad6dSPeter Rosin 1575214ad6dSPeter Rosin indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 1585214ad6dSPeter Rosin if (!indio_dev) 1595214ad6dSPeter Rosin return -ENOMEM; 1605214ad6dSPeter Rosin data = iio_priv(indio_dev); 1615214ad6dSPeter Rosin i2c_set_clientdata(client, indio_dev); 1625214ad6dSPeter Rosin data->client = client; 1635214ad6dSPeter Rosin 1645214ad6dSPeter Rosin data->cfg = of_device_get_match_data(dev); 165ce7c637aSJulia Lawall if (!data->cfg) 1660f6f400eSPeter Rosin data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data]; 1675214ad6dSPeter Rosin 1685214ad6dSPeter Rosin indio_dev->dev.parent = dev; 1695214ad6dSPeter Rosin indio_dev->info = &mcp4018_info; 1705214ad6dSPeter Rosin indio_dev->channels = &mcp4018_channel; 1715214ad6dSPeter Rosin indio_dev->num_channels = 1; 1725214ad6dSPeter Rosin indio_dev->name = client->name; 1735214ad6dSPeter Rosin 1745214ad6dSPeter Rosin return devm_iio_device_register(dev, indio_dev); 1755214ad6dSPeter Rosin } 1765214ad6dSPeter Rosin 1775214ad6dSPeter Rosin static struct i2c_driver mcp4018_driver = { 1785214ad6dSPeter Rosin .driver = { 1795214ad6dSPeter Rosin .name = "mcp4018", 1805214ad6dSPeter Rosin .of_match_table = of_match_ptr(mcp4018_of_match), 1815214ad6dSPeter Rosin }, 1820f6f400eSPeter Rosin .probe_new = mcp4018_probe, 1835214ad6dSPeter Rosin .id_table = mcp4018_id, 1845214ad6dSPeter Rosin }; 1855214ad6dSPeter Rosin 1865214ad6dSPeter Rosin module_i2c_driver(mcp4018_driver); 1875214ad6dSPeter Rosin 1885214ad6dSPeter Rosin MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 1895214ad6dSPeter Rosin MODULE_DESCRIPTION("MCP4018 digital potentiometer"); 190*b9ea8c31SPeter Rosin MODULE_LICENSE("GPL v2"); 191