1*5214ad6dSPeter Rosin // SPDX-License-Identifier: GPL-2.0 2*5214ad6dSPeter Rosin /* 3*5214ad6dSPeter Rosin * Industrial I/O driver for Microchip digital potentiometers 4*5214ad6dSPeter Rosin * Copyright (c) 2018 Axentia Technologies AB 5*5214ad6dSPeter Rosin * Author: Peter Rosin <peda@axentia.se> 6*5214ad6dSPeter Rosin * 7*5214ad6dSPeter Rosin * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22147a.pdf 8*5214ad6dSPeter Rosin * 9*5214ad6dSPeter Rosin * DEVID #Wipers #Positions Resistor Opts (kOhm) 10*5214ad6dSPeter Rosin * mcp4017 1 128 5, 10, 50, 100 11*5214ad6dSPeter Rosin * mcp4018 1 128 5, 10, 50, 100 12*5214ad6dSPeter Rosin * mcp4019 1 128 5, 10, 50, 100 13*5214ad6dSPeter Rosin */ 14*5214ad6dSPeter Rosin 15*5214ad6dSPeter Rosin #include <linux/err.h> 16*5214ad6dSPeter Rosin #include <linux/i2c.h> 17*5214ad6dSPeter Rosin #include <linux/iio/iio.h> 18*5214ad6dSPeter Rosin #include <linux/module.h> 19*5214ad6dSPeter Rosin #include <linux/of.h> 20*5214ad6dSPeter Rosin #include <linux/of_device.h> 21*5214ad6dSPeter Rosin 22*5214ad6dSPeter Rosin #define MCP4018_WIPER_MAX 127 23*5214ad6dSPeter Rosin 24*5214ad6dSPeter Rosin struct mcp4018_cfg { 25*5214ad6dSPeter Rosin int kohms; 26*5214ad6dSPeter Rosin }; 27*5214ad6dSPeter Rosin 28*5214ad6dSPeter Rosin enum mcp4018_type { 29*5214ad6dSPeter Rosin MCP4018_502, 30*5214ad6dSPeter Rosin MCP4018_103, 31*5214ad6dSPeter Rosin MCP4018_503, 32*5214ad6dSPeter Rosin MCP4018_104, 33*5214ad6dSPeter Rosin }; 34*5214ad6dSPeter Rosin 35*5214ad6dSPeter Rosin static const struct mcp4018_cfg mcp4018_cfg[] = { 36*5214ad6dSPeter Rosin [MCP4018_502] = { .kohms = 5, }, 37*5214ad6dSPeter Rosin [MCP4018_103] = { .kohms = 10, }, 38*5214ad6dSPeter Rosin [MCP4018_503] = { .kohms = 50, }, 39*5214ad6dSPeter Rosin [MCP4018_104] = { .kohms = 100, }, 40*5214ad6dSPeter Rosin }; 41*5214ad6dSPeter Rosin 42*5214ad6dSPeter Rosin struct mcp4018_data { 43*5214ad6dSPeter Rosin struct i2c_client *client; 44*5214ad6dSPeter Rosin const struct mcp4018_cfg *cfg; 45*5214ad6dSPeter Rosin }; 46*5214ad6dSPeter Rosin 47*5214ad6dSPeter Rosin static const struct iio_chan_spec mcp4018_channel = { 48*5214ad6dSPeter Rosin .type = IIO_RESISTANCE, 49*5214ad6dSPeter Rosin .indexed = 1, 50*5214ad6dSPeter Rosin .output = 1, 51*5214ad6dSPeter Rosin .channel = 0, 52*5214ad6dSPeter Rosin .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 53*5214ad6dSPeter Rosin .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 54*5214ad6dSPeter Rosin }; 55*5214ad6dSPeter Rosin 56*5214ad6dSPeter Rosin static int mcp4018_read_raw(struct iio_dev *indio_dev, 57*5214ad6dSPeter Rosin struct iio_chan_spec const *chan, 58*5214ad6dSPeter Rosin int *val, int *val2, long mask) 59*5214ad6dSPeter Rosin { 60*5214ad6dSPeter Rosin struct mcp4018_data *data = iio_priv(indio_dev); 61*5214ad6dSPeter Rosin s32 ret; 62*5214ad6dSPeter Rosin 63*5214ad6dSPeter Rosin switch (mask) { 64*5214ad6dSPeter Rosin case IIO_CHAN_INFO_RAW: 65*5214ad6dSPeter Rosin ret = i2c_smbus_read_byte(data->client); 66*5214ad6dSPeter Rosin if (ret < 0) 67*5214ad6dSPeter Rosin return ret; 68*5214ad6dSPeter Rosin *val = ret; 69*5214ad6dSPeter Rosin return IIO_VAL_INT; 70*5214ad6dSPeter Rosin case IIO_CHAN_INFO_SCALE: 71*5214ad6dSPeter Rosin *val = 1000 * data->cfg->kohms; 72*5214ad6dSPeter Rosin *val2 = MCP4018_WIPER_MAX; 73*5214ad6dSPeter Rosin return IIO_VAL_FRACTIONAL; 74*5214ad6dSPeter Rosin } 75*5214ad6dSPeter Rosin 76*5214ad6dSPeter Rosin return -EINVAL; 77*5214ad6dSPeter Rosin } 78*5214ad6dSPeter Rosin 79*5214ad6dSPeter Rosin static int mcp4018_write_raw(struct iio_dev *indio_dev, 80*5214ad6dSPeter Rosin struct iio_chan_spec const *chan, 81*5214ad6dSPeter Rosin int val, int val2, long mask) 82*5214ad6dSPeter Rosin { 83*5214ad6dSPeter Rosin struct mcp4018_data *data = iio_priv(indio_dev); 84*5214ad6dSPeter Rosin 85*5214ad6dSPeter Rosin switch (mask) { 86*5214ad6dSPeter Rosin case IIO_CHAN_INFO_RAW: 87*5214ad6dSPeter Rosin if (val > MCP4018_WIPER_MAX || val < 0) 88*5214ad6dSPeter Rosin return -EINVAL; 89*5214ad6dSPeter Rosin break; 90*5214ad6dSPeter Rosin default: 91*5214ad6dSPeter Rosin return -EINVAL; 92*5214ad6dSPeter Rosin } 93*5214ad6dSPeter Rosin 94*5214ad6dSPeter Rosin return i2c_smbus_write_byte(data->client, val); 95*5214ad6dSPeter Rosin } 96*5214ad6dSPeter Rosin 97*5214ad6dSPeter Rosin static const struct iio_info mcp4018_info = { 98*5214ad6dSPeter Rosin .read_raw = mcp4018_read_raw, 99*5214ad6dSPeter Rosin .write_raw = mcp4018_write_raw, 100*5214ad6dSPeter Rosin }; 101*5214ad6dSPeter Rosin 102*5214ad6dSPeter Rosin #ifdef CONFIG_OF 103*5214ad6dSPeter Rosin 104*5214ad6dSPeter Rosin #define MCP4018_COMPATIBLE(of_compatible, cfg) { \ 105*5214ad6dSPeter Rosin .compatible = of_compatible, \ 106*5214ad6dSPeter Rosin .data = &mcp4018_cfg[cfg], \ 107*5214ad6dSPeter Rosin } 108*5214ad6dSPeter Rosin 109*5214ad6dSPeter Rosin static const struct of_device_id mcp4018_of_match[] = { 110*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4017-502", MCP4018_502), 111*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4017-103", MCP4018_103), 112*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4017-503", MCP4018_503), 113*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4017-104", MCP4018_104), 114*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4018-502", MCP4018_502), 115*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4018-103", MCP4018_103), 116*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4018-503", MCP4018_503), 117*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4018-104", MCP4018_104), 118*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4019-502", MCP4018_502), 119*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4019-103", MCP4018_103), 120*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4019-503", MCP4018_503), 121*5214ad6dSPeter Rosin MCP4018_COMPATIBLE("microchip,mcp4019-104", MCP4018_104), 122*5214ad6dSPeter Rosin { /* sentinel */ } 123*5214ad6dSPeter Rosin }; 124*5214ad6dSPeter Rosin MODULE_DEVICE_TABLE(of, mcp4018_of_match); 125*5214ad6dSPeter Rosin 126*5214ad6dSPeter Rosin #endif 127*5214ad6dSPeter Rosin 128*5214ad6dSPeter Rosin static int mcp4018_probe(struct i2c_client *client, 129*5214ad6dSPeter Rosin const struct i2c_device_id *id) 130*5214ad6dSPeter Rosin { 131*5214ad6dSPeter Rosin struct device *dev = &client->dev; 132*5214ad6dSPeter Rosin struct mcp4018_data *data; 133*5214ad6dSPeter Rosin struct iio_dev *indio_dev; 134*5214ad6dSPeter Rosin const struct of_device_id *match; 135*5214ad6dSPeter Rosin 136*5214ad6dSPeter Rosin if (!i2c_check_functionality(client->adapter, 137*5214ad6dSPeter Rosin I2C_FUNC_SMBUS_BYTE)) { 138*5214ad6dSPeter Rosin dev_err(dev, "SMBUS Byte transfers not supported\n"); 139*5214ad6dSPeter Rosin return -EOPNOTSUPP; 140*5214ad6dSPeter Rosin } 141*5214ad6dSPeter Rosin 142*5214ad6dSPeter Rosin indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 143*5214ad6dSPeter Rosin if (!indio_dev) 144*5214ad6dSPeter Rosin return -ENOMEM; 145*5214ad6dSPeter Rosin data = iio_priv(indio_dev); 146*5214ad6dSPeter Rosin i2c_set_clientdata(client, indio_dev); 147*5214ad6dSPeter Rosin data->client = client; 148*5214ad6dSPeter Rosin 149*5214ad6dSPeter Rosin match = of_match_device(of_match_ptr(mcp4018_of_match), dev); 150*5214ad6dSPeter Rosin if (match) 151*5214ad6dSPeter Rosin data->cfg = of_device_get_match_data(dev); 152*5214ad6dSPeter Rosin else 153*5214ad6dSPeter Rosin data->cfg = &mcp4018_cfg[id->driver_data]; 154*5214ad6dSPeter Rosin 155*5214ad6dSPeter Rosin indio_dev->dev.parent = dev; 156*5214ad6dSPeter Rosin indio_dev->info = &mcp4018_info; 157*5214ad6dSPeter Rosin indio_dev->channels = &mcp4018_channel; 158*5214ad6dSPeter Rosin indio_dev->num_channels = 1; 159*5214ad6dSPeter Rosin indio_dev->name = client->name; 160*5214ad6dSPeter Rosin 161*5214ad6dSPeter Rosin return devm_iio_device_register(dev, indio_dev); 162*5214ad6dSPeter Rosin } 163*5214ad6dSPeter Rosin 164*5214ad6dSPeter Rosin static const struct i2c_device_id mcp4018_id[] = { 165*5214ad6dSPeter Rosin { "mcp4017-502", MCP4018_502 }, 166*5214ad6dSPeter Rosin { "mcp4017-103", MCP4018_103 }, 167*5214ad6dSPeter Rosin { "mcp4017-503", MCP4018_503 }, 168*5214ad6dSPeter Rosin { "mcp4017-104", MCP4018_104 }, 169*5214ad6dSPeter Rosin { "mcp4018-502", MCP4018_502 }, 170*5214ad6dSPeter Rosin { "mcp4018-103", MCP4018_103 }, 171*5214ad6dSPeter Rosin { "mcp4018-503", MCP4018_503 }, 172*5214ad6dSPeter Rosin { "mcp4018-104", MCP4018_104 }, 173*5214ad6dSPeter Rosin { "mcp4019-502", MCP4018_502 }, 174*5214ad6dSPeter Rosin { "mcp4019-103", MCP4018_103 }, 175*5214ad6dSPeter Rosin { "mcp4019-503", MCP4018_503 }, 176*5214ad6dSPeter Rosin { "mcp4019-104", MCP4018_104 }, 177*5214ad6dSPeter Rosin {} 178*5214ad6dSPeter Rosin }; 179*5214ad6dSPeter Rosin MODULE_DEVICE_TABLE(i2c, mcp4018_id); 180*5214ad6dSPeter Rosin 181*5214ad6dSPeter Rosin static struct i2c_driver mcp4018_driver = { 182*5214ad6dSPeter Rosin .driver = { 183*5214ad6dSPeter Rosin .name = "mcp4018", 184*5214ad6dSPeter Rosin .of_match_table = of_match_ptr(mcp4018_of_match), 185*5214ad6dSPeter Rosin }, 186*5214ad6dSPeter Rosin .probe = mcp4018_probe, 187*5214ad6dSPeter Rosin .id_table = mcp4018_id, 188*5214ad6dSPeter Rosin }; 189*5214ad6dSPeter Rosin 190*5214ad6dSPeter Rosin module_i2c_driver(mcp4018_driver); 191*5214ad6dSPeter Rosin 192*5214ad6dSPeter Rosin MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 193*5214ad6dSPeter Rosin MODULE_DESCRIPTION("MCP4018 digital potentiometer"); 194*5214ad6dSPeter Rosin MODULE_LICENSE("GPL"); 195