1c05dc2ccSPeter Rosin /* 2c05dc2ccSPeter Rosin * Industrial I/O driver for Microchip digital potentiometers 3c05dc2ccSPeter Rosin * Copyright (c) 2015 Axentia Technologies AB 4c05dc2ccSPeter Rosin * Author: Peter Rosin <peda@axentia.se> 5c05dc2ccSPeter Rosin * 6c05dc2ccSPeter Rosin * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf 7c05dc2ccSPeter Rosin * 8c05dc2ccSPeter Rosin * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address 9c05dc2ccSPeter Rosin * mcp4531 1 129 5, 10, 50, 100 010111x 10c05dc2ccSPeter Rosin * mcp4532 1 129 5, 10, 50, 100 01011xx 11c05dc2ccSPeter Rosin * mcp4551 1 257 5, 10, 50, 100 010111x 12c05dc2ccSPeter Rosin * mcp4552 1 257 5, 10, 50, 100 01011xx 13c05dc2ccSPeter Rosin * mcp4631 2 129 5, 10, 50, 100 0101xxx 14c05dc2ccSPeter Rosin * mcp4632 2 129 5, 10, 50, 100 01011xx 15c05dc2ccSPeter Rosin * mcp4651 2 257 5, 10, 50, 100 0101xxx 16c05dc2ccSPeter Rosin * mcp4652 2 257 5, 10, 50, 100 01011xx 17c05dc2ccSPeter Rosin * 18c05dc2ccSPeter Rosin * This program is free software; you can redistribute it and/or modify it 19c05dc2ccSPeter Rosin * under the terms of the GNU General Public License version 2 as published by 20c05dc2ccSPeter Rosin * the Free Software Foundation. 21c05dc2ccSPeter Rosin */ 22c05dc2ccSPeter Rosin 23c05dc2ccSPeter Rosin #include <linux/module.h> 24c05dc2ccSPeter Rosin #include <linux/i2c.h> 25c05dc2ccSPeter Rosin #include <linux/err.h> 26c05dc2ccSPeter Rosin 27c05dc2ccSPeter Rosin #include <linux/iio/iio.h> 28c05dc2ccSPeter Rosin 29c05dc2ccSPeter Rosin struct mcp4531_cfg { 30c05dc2ccSPeter Rosin int wipers; 31c05dc2ccSPeter Rosin int max_pos; 32c05dc2ccSPeter Rosin int kohms; 33c05dc2ccSPeter Rosin }; 34c05dc2ccSPeter Rosin 35c05dc2ccSPeter Rosin enum mcp4531_type { 36c05dc2ccSPeter Rosin MCP453x_502, 37c05dc2ccSPeter Rosin MCP453x_103, 38c05dc2ccSPeter Rosin MCP453x_503, 39c05dc2ccSPeter Rosin MCP453x_104, 40c05dc2ccSPeter Rosin MCP455x_502, 41c05dc2ccSPeter Rosin MCP455x_103, 42c05dc2ccSPeter Rosin MCP455x_503, 43c05dc2ccSPeter Rosin MCP455x_104, 44c05dc2ccSPeter Rosin MCP463x_502, 45c05dc2ccSPeter Rosin MCP463x_103, 46c05dc2ccSPeter Rosin MCP463x_503, 47c05dc2ccSPeter Rosin MCP463x_104, 48c05dc2ccSPeter Rosin MCP465x_502, 49c05dc2ccSPeter Rosin MCP465x_103, 50c05dc2ccSPeter Rosin MCP465x_503, 51c05dc2ccSPeter Rosin MCP465x_104, 52c05dc2ccSPeter Rosin }; 53c05dc2ccSPeter Rosin 54c05dc2ccSPeter Rosin static const struct mcp4531_cfg mcp4531_cfg[] = { 55c05dc2ccSPeter Rosin [MCP453x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, }, 56c05dc2ccSPeter Rosin [MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, }, 57c05dc2ccSPeter Rosin [MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, }, 58c05dc2ccSPeter Rosin [MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, }, 59c05dc2ccSPeter Rosin [MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, }, 60c05dc2ccSPeter Rosin [MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, }, 61c05dc2ccSPeter Rosin [MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, }, 62c05dc2ccSPeter Rosin [MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, }, 63c05dc2ccSPeter Rosin [MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, }, 64c05dc2ccSPeter Rosin [MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, }, 65c05dc2ccSPeter Rosin [MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, }, 66c05dc2ccSPeter Rosin [MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, }, 67c05dc2ccSPeter Rosin [MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, }, 68c05dc2ccSPeter Rosin [MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, }, 69c05dc2ccSPeter Rosin [MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, }, 70c05dc2ccSPeter Rosin [MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, }, 71c05dc2ccSPeter Rosin }; 72c05dc2ccSPeter Rosin 73c05dc2ccSPeter Rosin #define MCP4531_WRITE (0 << 2) 74c05dc2ccSPeter Rosin #define MCP4531_INCR (1 << 2) 75c05dc2ccSPeter Rosin #define MCP4531_DECR (2 << 2) 76c05dc2ccSPeter Rosin #define MCP4531_READ (3 << 2) 77c05dc2ccSPeter Rosin 78c05dc2ccSPeter Rosin #define MCP4531_WIPER_SHIFT (4) 79c05dc2ccSPeter Rosin 80c05dc2ccSPeter Rosin struct mcp4531_data { 81c05dc2ccSPeter Rosin struct i2c_client *client; 82*91307cbeSSlawomir Stepien const struct mcp4531_cfg *cfg; 83c05dc2ccSPeter Rosin }; 84c05dc2ccSPeter Rosin 85c05dc2ccSPeter Rosin #define MCP4531_CHANNEL(ch) { \ 86c05dc2ccSPeter Rosin .type = IIO_RESISTANCE, \ 87c05dc2ccSPeter Rosin .indexed = 1, \ 88c05dc2ccSPeter Rosin .output = 1, \ 89c05dc2ccSPeter Rosin .channel = (ch), \ 90c05dc2ccSPeter Rosin .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 91c05dc2ccSPeter Rosin .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 92c05dc2ccSPeter Rosin } 93c05dc2ccSPeter Rosin 94c05dc2ccSPeter Rosin static const struct iio_chan_spec mcp4531_channels[] = { 95c05dc2ccSPeter Rosin MCP4531_CHANNEL(0), 96c05dc2ccSPeter Rosin MCP4531_CHANNEL(1), 97c05dc2ccSPeter Rosin }; 98c05dc2ccSPeter Rosin 99c05dc2ccSPeter Rosin static int mcp4531_read_raw(struct iio_dev *indio_dev, 100c05dc2ccSPeter Rosin struct iio_chan_spec const *chan, 101c05dc2ccSPeter Rosin int *val, int *val2, long mask) 102c05dc2ccSPeter Rosin { 103c05dc2ccSPeter Rosin struct mcp4531_data *data = iio_priv(indio_dev); 104c05dc2ccSPeter Rosin int address = chan->channel << MCP4531_WIPER_SHIFT; 105c05dc2ccSPeter Rosin s32 ret; 106c05dc2ccSPeter Rosin 107c05dc2ccSPeter Rosin switch (mask) { 108c05dc2ccSPeter Rosin case IIO_CHAN_INFO_RAW: 109c05dc2ccSPeter Rosin ret = i2c_smbus_read_word_swapped(data->client, 110c05dc2ccSPeter Rosin MCP4531_READ | address); 111c05dc2ccSPeter Rosin if (ret < 0) 112c05dc2ccSPeter Rosin return ret; 113c05dc2ccSPeter Rosin *val = ret; 114c05dc2ccSPeter Rosin return IIO_VAL_INT; 115c05dc2ccSPeter Rosin case IIO_CHAN_INFO_SCALE: 116*91307cbeSSlawomir Stepien *val = 1000 * data->cfg->kohms; 117*91307cbeSSlawomir Stepien *val2 = data->cfg->max_pos; 118c05dc2ccSPeter Rosin return IIO_VAL_FRACTIONAL; 119c05dc2ccSPeter Rosin } 120c05dc2ccSPeter Rosin 121c05dc2ccSPeter Rosin return -EINVAL; 122c05dc2ccSPeter Rosin } 123c05dc2ccSPeter Rosin 124c05dc2ccSPeter Rosin static int mcp4531_write_raw(struct iio_dev *indio_dev, 125c05dc2ccSPeter Rosin struct iio_chan_spec const *chan, 126c05dc2ccSPeter Rosin int val, int val2, long mask) 127c05dc2ccSPeter Rosin { 128c05dc2ccSPeter Rosin struct mcp4531_data *data = iio_priv(indio_dev); 129c05dc2ccSPeter Rosin int address = chan->channel << MCP4531_WIPER_SHIFT; 130c05dc2ccSPeter Rosin 131c05dc2ccSPeter Rosin switch (mask) { 132c05dc2ccSPeter Rosin case IIO_CHAN_INFO_RAW: 133*91307cbeSSlawomir Stepien if (val > data->cfg->max_pos || val < 0) 134c05dc2ccSPeter Rosin return -EINVAL; 135c05dc2ccSPeter Rosin break; 136c05dc2ccSPeter Rosin default: 137c05dc2ccSPeter Rosin return -EINVAL; 138c05dc2ccSPeter Rosin } 139c05dc2ccSPeter Rosin 140c05dc2ccSPeter Rosin return i2c_smbus_write_byte_data(data->client, 141c05dc2ccSPeter Rosin MCP4531_WRITE | address | (val >> 8), 142c05dc2ccSPeter Rosin val & 0xff); 143c05dc2ccSPeter Rosin } 144c05dc2ccSPeter Rosin 145c05dc2ccSPeter Rosin static const struct iio_info mcp4531_info = { 146c05dc2ccSPeter Rosin .read_raw = mcp4531_read_raw, 147c05dc2ccSPeter Rosin .write_raw = mcp4531_write_raw, 148c05dc2ccSPeter Rosin .driver_module = THIS_MODULE, 149c05dc2ccSPeter Rosin }; 150c05dc2ccSPeter Rosin 151c05dc2ccSPeter Rosin static int mcp4531_probe(struct i2c_client *client, 152c05dc2ccSPeter Rosin const struct i2c_device_id *id) 153c05dc2ccSPeter Rosin { 154c05dc2ccSPeter Rosin struct device *dev = &client->dev; 155c05dc2ccSPeter Rosin struct mcp4531_data *data; 156c05dc2ccSPeter Rosin struct iio_dev *indio_dev; 157c05dc2ccSPeter Rosin 158c05dc2ccSPeter Rosin if (!i2c_check_functionality(client->adapter, 159c05dc2ccSPeter Rosin I2C_FUNC_SMBUS_WORD_DATA)) { 160c05dc2ccSPeter Rosin dev_err(dev, "SMBUS Word Data not supported\n"); 161f8d9d3b4SMatt Ranostay return -EOPNOTSUPP; 162c05dc2ccSPeter Rosin } 163c05dc2ccSPeter Rosin 164c05dc2ccSPeter Rosin indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 165c05dc2ccSPeter Rosin if (!indio_dev) 166c05dc2ccSPeter Rosin return -ENOMEM; 167c05dc2ccSPeter Rosin data = iio_priv(indio_dev); 168c05dc2ccSPeter Rosin i2c_set_clientdata(client, indio_dev); 169c05dc2ccSPeter Rosin data->client = client; 170*91307cbeSSlawomir Stepien data->cfg = &mcp4531_cfg[id->driver_data]; 171c05dc2ccSPeter Rosin 172c05dc2ccSPeter Rosin indio_dev->dev.parent = dev; 173c05dc2ccSPeter Rosin indio_dev->info = &mcp4531_info; 174c05dc2ccSPeter Rosin indio_dev->channels = mcp4531_channels; 175*91307cbeSSlawomir Stepien indio_dev->num_channels = data->cfg->wipers; 176c05dc2ccSPeter Rosin indio_dev->name = client->name; 177c05dc2ccSPeter Rosin 178c05dc2ccSPeter Rosin return devm_iio_device_register(dev, indio_dev); 179c05dc2ccSPeter Rosin } 180c05dc2ccSPeter Rosin 181c05dc2ccSPeter Rosin static const struct i2c_device_id mcp4531_id[] = { 182c05dc2ccSPeter Rosin { "mcp4531-502", MCP453x_502 }, 183c05dc2ccSPeter Rosin { "mcp4531-103", MCP453x_103 }, 184c05dc2ccSPeter Rosin { "mcp4531-503", MCP453x_503 }, 185c05dc2ccSPeter Rosin { "mcp4531-104", MCP453x_104 }, 186c05dc2ccSPeter Rosin { "mcp4532-502", MCP453x_502 }, 187c05dc2ccSPeter Rosin { "mcp4532-103", MCP453x_103 }, 188c05dc2ccSPeter Rosin { "mcp4532-503", MCP453x_503 }, 189c05dc2ccSPeter Rosin { "mcp4532-104", MCP453x_104 }, 190c05dc2ccSPeter Rosin { "mcp4551-502", MCP455x_502 }, 191c05dc2ccSPeter Rosin { "mcp4551-103", MCP455x_103 }, 192c05dc2ccSPeter Rosin { "mcp4551-503", MCP455x_503 }, 193c05dc2ccSPeter Rosin { "mcp4551-104", MCP455x_104 }, 194c05dc2ccSPeter Rosin { "mcp4552-502", MCP455x_502 }, 195c05dc2ccSPeter Rosin { "mcp4552-103", MCP455x_103 }, 196c05dc2ccSPeter Rosin { "mcp4552-503", MCP455x_503 }, 197c05dc2ccSPeter Rosin { "mcp4552-104", MCP455x_104 }, 198c05dc2ccSPeter Rosin { "mcp4631-502", MCP463x_502 }, 199c05dc2ccSPeter Rosin { "mcp4631-103", MCP463x_103 }, 200c05dc2ccSPeter Rosin { "mcp4631-503", MCP463x_503 }, 201c05dc2ccSPeter Rosin { "mcp4631-104", MCP463x_104 }, 202c05dc2ccSPeter Rosin { "mcp4632-502", MCP463x_502 }, 203c05dc2ccSPeter Rosin { "mcp4632-103", MCP463x_103 }, 204c05dc2ccSPeter Rosin { "mcp4632-503", MCP463x_503 }, 205c05dc2ccSPeter Rosin { "mcp4632-104", MCP463x_104 }, 206c05dc2ccSPeter Rosin { "mcp4651-502", MCP465x_502 }, 207c05dc2ccSPeter Rosin { "mcp4651-103", MCP465x_103 }, 208c05dc2ccSPeter Rosin { "mcp4651-503", MCP465x_503 }, 209c05dc2ccSPeter Rosin { "mcp4651-104", MCP465x_104 }, 210c05dc2ccSPeter Rosin { "mcp4652-502", MCP465x_502 }, 211c05dc2ccSPeter Rosin { "mcp4652-103", MCP465x_103 }, 212c05dc2ccSPeter Rosin { "mcp4652-503", MCP465x_503 }, 213c05dc2ccSPeter Rosin { "mcp4652-104", MCP465x_104 }, 214c05dc2ccSPeter Rosin {} 215c05dc2ccSPeter Rosin }; 216c05dc2ccSPeter Rosin MODULE_DEVICE_TABLE(i2c, mcp4531_id); 217c05dc2ccSPeter Rosin 218c05dc2ccSPeter Rosin static struct i2c_driver mcp4531_driver = { 219c05dc2ccSPeter Rosin .driver = { 220c05dc2ccSPeter Rosin .name = "mcp4531", 221c05dc2ccSPeter Rosin }, 222c05dc2ccSPeter Rosin .probe = mcp4531_probe, 223c05dc2ccSPeter Rosin .id_table = mcp4531_id, 224c05dc2ccSPeter Rosin }; 225c05dc2ccSPeter Rosin 226c05dc2ccSPeter Rosin module_i2c_driver(mcp4531_driver); 227c05dc2ccSPeter Rosin 228c05dc2ccSPeter Rosin MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 229c05dc2ccSPeter Rosin MODULE_DESCRIPTION("MCP4531 digital potentiometer"); 230c05dc2ccSPeter Rosin MODULE_LICENSE("GPL"); 231