1*092cb71aSChris Coffey // SPDX-License-Identifier: GPL-2.0 2*092cb71aSChris Coffey /* 3*092cb71aSChris Coffey * Industrial I/O driver for Microchip digital potentiometers 4*092cb71aSChris Coffey * 5*092cb71aSChris Coffey * Copyright (c) 2018 Chris Coffey <cmc@babblebit.net> 6*092cb71aSChris Coffey * Based on: Slawomir Stepien's code from mcp4131.c 7*092cb71aSChris Coffey * 8*092cb71aSChris Coffey * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/11195c.pdf 9*092cb71aSChris Coffey * 10*092cb71aSChris Coffey * DEVID #Wipers #Positions Resistance (kOhm) 11*092cb71aSChris Coffey * mcp41010 1 256 10 12*092cb71aSChris Coffey * mcp41050 1 256 50 13*092cb71aSChris Coffey * mcp41100 1 256 100 14*092cb71aSChris Coffey * mcp42010 2 256 10 15*092cb71aSChris Coffey * mcp42050 2 256 50 16*092cb71aSChris Coffey * mcp42100 2 256 100 17*092cb71aSChris Coffey */ 18*092cb71aSChris Coffey 19*092cb71aSChris Coffey #include <linux/cache.h> 20*092cb71aSChris Coffey #include <linux/err.h> 21*092cb71aSChris Coffey #include <linux/iio/iio.h> 22*092cb71aSChris Coffey #include <linux/iio/types.h> 23*092cb71aSChris Coffey #include <linux/module.h> 24*092cb71aSChris Coffey #include <linux/mutex.h> 25*092cb71aSChris Coffey #include <linux/of.h> 26*092cb71aSChris Coffey #include <linux/of_device.h> 27*092cb71aSChris Coffey #include <linux/spi/spi.h> 28*092cb71aSChris Coffey 29*092cb71aSChris Coffey #define MCP41010_MAX_WIPERS 2 30*092cb71aSChris Coffey #define MCP41010_WRITE BIT(4) 31*092cb71aSChris Coffey #define MCP41010_WIPER_MAX 255 32*092cb71aSChris Coffey #define MCP41010_WIPER_CHANNEL BIT(0) 33*092cb71aSChris Coffey 34*092cb71aSChris Coffey struct mcp41010_cfg { 35*092cb71aSChris Coffey char name[16]; 36*092cb71aSChris Coffey int wipers; 37*092cb71aSChris Coffey int kohms; 38*092cb71aSChris Coffey }; 39*092cb71aSChris Coffey 40*092cb71aSChris Coffey enum mcp41010_type { 41*092cb71aSChris Coffey MCP41010, 42*092cb71aSChris Coffey MCP41050, 43*092cb71aSChris Coffey MCP41100, 44*092cb71aSChris Coffey MCP42010, 45*092cb71aSChris Coffey MCP42050, 46*092cb71aSChris Coffey MCP42100, 47*092cb71aSChris Coffey }; 48*092cb71aSChris Coffey 49*092cb71aSChris Coffey static const struct mcp41010_cfg mcp41010_cfg[] = { 50*092cb71aSChris Coffey [MCP41010] = { .name = "mcp41010", .wipers = 1, .kohms = 10, }, 51*092cb71aSChris Coffey [MCP41050] = { .name = "mcp41050", .wipers = 1, .kohms = 50, }, 52*092cb71aSChris Coffey [MCP41100] = { .name = "mcp41100", .wipers = 1, .kohms = 100, }, 53*092cb71aSChris Coffey [MCP42010] = { .name = "mcp42010", .wipers = 2, .kohms = 10, }, 54*092cb71aSChris Coffey [MCP42050] = { .name = "mcp42050", .wipers = 2, .kohms = 50, }, 55*092cb71aSChris Coffey [MCP42100] = { .name = "mcp42100", .wipers = 2, .kohms = 100, }, 56*092cb71aSChris Coffey }; 57*092cb71aSChris Coffey 58*092cb71aSChris Coffey struct mcp41010_data { 59*092cb71aSChris Coffey struct spi_device *spi; 60*092cb71aSChris Coffey const struct mcp41010_cfg *cfg; 61*092cb71aSChris Coffey struct mutex lock; /* Protect write sequences */ 62*092cb71aSChris Coffey unsigned int value[MCP41010_MAX_WIPERS]; /* Cache wiper values */ 63*092cb71aSChris Coffey u8 buf[2] ____cacheline_aligned; 64*092cb71aSChris Coffey }; 65*092cb71aSChris Coffey 66*092cb71aSChris Coffey #define MCP41010_CHANNEL(ch) { \ 67*092cb71aSChris Coffey .type = IIO_RESISTANCE, \ 68*092cb71aSChris Coffey .indexed = 1, \ 69*092cb71aSChris Coffey .output = 1, \ 70*092cb71aSChris Coffey .channel = (ch), \ 71*092cb71aSChris Coffey .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 72*092cb71aSChris Coffey .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 73*092cb71aSChris Coffey } 74*092cb71aSChris Coffey 75*092cb71aSChris Coffey static const struct iio_chan_spec mcp41010_channels[] = { 76*092cb71aSChris Coffey MCP41010_CHANNEL(0), 77*092cb71aSChris Coffey MCP41010_CHANNEL(1), 78*092cb71aSChris Coffey }; 79*092cb71aSChris Coffey 80*092cb71aSChris Coffey static int mcp41010_read_raw(struct iio_dev *indio_dev, 81*092cb71aSChris Coffey struct iio_chan_spec const *chan, 82*092cb71aSChris Coffey int *val, int *val2, long mask) 83*092cb71aSChris Coffey { 84*092cb71aSChris Coffey struct mcp41010_data *data = iio_priv(indio_dev); 85*092cb71aSChris Coffey int channel = chan->channel; 86*092cb71aSChris Coffey 87*092cb71aSChris Coffey switch (mask) { 88*092cb71aSChris Coffey case IIO_CHAN_INFO_RAW: 89*092cb71aSChris Coffey *val = data->value[channel]; 90*092cb71aSChris Coffey return IIO_VAL_INT; 91*092cb71aSChris Coffey 92*092cb71aSChris Coffey case IIO_CHAN_INFO_SCALE: 93*092cb71aSChris Coffey *val = 1000 * data->cfg->kohms; 94*092cb71aSChris Coffey *val2 = MCP41010_WIPER_MAX; 95*092cb71aSChris Coffey return IIO_VAL_FRACTIONAL; 96*092cb71aSChris Coffey } 97*092cb71aSChris Coffey 98*092cb71aSChris Coffey return -EINVAL; 99*092cb71aSChris Coffey } 100*092cb71aSChris Coffey 101*092cb71aSChris Coffey static int mcp41010_write_raw(struct iio_dev *indio_dev, 102*092cb71aSChris Coffey struct iio_chan_spec const *chan, 103*092cb71aSChris Coffey int val, int val2, long mask) 104*092cb71aSChris Coffey { 105*092cb71aSChris Coffey int err; 106*092cb71aSChris Coffey struct mcp41010_data *data = iio_priv(indio_dev); 107*092cb71aSChris Coffey int channel = chan->channel; 108*092cb71aSChris Coffey 109*092cb71aSChris Coffey if (mask != IIO_CHAN_INFO_RAW) 110*092cb71aSChris Coffey return -EINVAL; 111*092cb71aSChris Coffey 112*092cb71aSChris Coffey if (val > MCP41010_WIPER_MAX || val < 0) 113*092cb71aSChris Coffey return -EINVAL; 114*092cb71aSChris Coffey 115*092cb71aSChris Coffey mutex_lock(&data->lock); 116*092cb71aSChris Coffey 117*092cb71aSChris Coffey data->buf[0] = MCP41010_WIPER_CHANNEL << channel; 118*092cb71aSChris Coffey data->buf[0] |= MCP41010_WRITE; 119*092cb71aSChris Coffey data->buf[1] = val & 0xff; 120*092cb71aSChris Coffey 121*092cb71aSChris Coffey err = spi_write(data->spi, data->buf, sizeof(data->buf)); 122*092cb71aSChris Coffey if (!err) 123*092cb71aSChris Coffey data->value[channel] = val; 124*092cb71aSChris Coffey 125*092cb71aSChris Coffey mutex_unlock(&data->lock); 126*092cb71aSChris Coffey 127*092cb71aSChris Coffey return err; 128*092cb71aSChris Coffey } 129*092cb71aSChris Coffey 130*092cb71aSChris Coffey static const struct iio_info mcp41010_info = { 131*092cb71aSChris Coffey .read_raw = mcp41010_read_raw, 132*092cb71aSChris Coffey .write_raw = mcp41010_write_raw, 133*092cb71aSChris Coffey }; 134*092cb71aSChris Coffey 135*092cb71aSChris Coffey static int mcp41010_probe(struct spi_device *spi) 136*092cb71aSChris Coffey { 137*092cb71aSChris Coffey int err; 138*092cb71aSChris Coffey struct device *dev = &spi->dev; 139*092cb71aSChris Coffey struct mcp41010_data *data; 140*092cb71aSChris Coffey struct iio_dev *indio_dev; 141*092cb71aSChris Coffey 142*092cb71aSChris Coffey indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 143*092cb71aSChris Coffey if (!indio_dev) 144*092cb71aSChris Coffey return -ENOMEM; 145*092cb71aSChris Coffey 146*092cb71aSChris Coffey data = iio_priv(indio_dev); 147*092cb71aSChris Coffey spi_set_drvdata(spi, indio_dev); 148*092cb71aSChris Coffey data->spi = spi; 149*092cb71aSChris Coffey data->cfg = of_device_get_match_data(&spi->dev); 150*092cb71aSChris Coffey if (!data->cfg) 151*092cb71aSChris Coffey data->cfg = &mcp41010_cfg[spi_get_device_id(spi)->driver_data]; 152*092cb71aSChris Coffey 153*092cb71aSChris Coffey mutex_init(&data->lock); 154*092cb71aSChris Coffey 155*092cb71aSChris Coffey indio_dev->dev.parent = dev; 156*092cb71aSChris Coffey indio_dev->info = &mcp41010_info; 157*092cb71aSChris Coffey indio_dev->channels = mcp41010_channels; 158*092cb71aSChris Coffey indio_dev->num_channels = data->cfg->wipers; 159*092cb71aSChris Coffey indio_dev->name = data->cfg->name; 160*092cb71aSChris Coffey 161*092cb71aSChris Coffey err = devm_iio_device_register(dev, indio_dev); 162*092cb71aSChris Coffey if (err) 163*092cb71aSChris Coffey dev_info(&spi->dev, "Unable to register %s\n", indio_dev->name); 164*092cb71aSChris Coffey 165*092cb71aSChris Coffey return err; 166*092cb71aSChris Coffey } 167*092cb71aSChris Coffey 168*092cb71aSChris Coffey static const struct of_device_id mcp41010_match[] = { 169*092cb71aSChris Coffey { .compatible = "microchip,mcp41010", .data = &mcp41010_cfg[MCP41010] }, 170*092cb71aSChris Coffey { .compatible = "microchip,mcp41050", .data = &mcp41010_cfg[MCP41050] }, 171*092cb71aSChris Coffey { .compatible = "microchip,mcp41100", .data = &mcp41010_cfg[MCP41100] }, 172*092cb71aSChris Coffey { .compatible = "microchip,mcp42010", .data = &mcp41010_cfg[MCP42010] }, 173*092cb71aSChris Coffey { .compatible = "microchip,mcp42050", .data = &mcp41010_cfg[MCP42050] }, 174*092cb71aSChris Coffey { .compatible = "microchip,mcp42100", .data = &mcp41010_cfg[MCP42100] }, 175*092cb71aSChris Coffey {} 176*092cb71aSChris Coffey }; 177*092cb71aSChris Coffey MODULE_DEVICE_TABLE(of, mcp41010_match); 178*092cb71aSChris Coffey 179*092cb71aSChris Coffey static const struct spi_device_id mcp41010_id[] = { 180*092cb71aSChris Coffey { "mcp41010", MCP41010 }, 181*092cb71aSChris Coffey { "mcp41050", MCP41050 }, 182*092cb71aSChris Coffey { "mcp41100", MCP41100 }, 183*092cb71aSChris Coffey { "mcp42010", MCP42010 }, 184*092cb71aSChris Coffey { "mcp42050", MCP42050 }, 185*092cb71aSChris Coffey { "mcp42100", MCP42100 }, 186*092cb71aSChris Coffey {} 187*092cb71aSChris Coffey }; 188*092cb71aSChris Coffey MODULE_DEVICE_TABLE(spi, mcp41010_id); 189*092cb71aSChris Coffey 190*092cb71aSChris Coffey static struct spi_driver mcp41010_driver = { 191*092cb71aSChris Coffey .driver = { 192*092cb71aSChris Coffey .name = "mcp41010", 193*092cb71aSChris Coffey .of_match_table = mcp41010_match, 194*092cb71aSChris Coffey }, 195*092cb71aSChris Coffey .probe = mcp41010_probe, 196*092cb71aSChris Coffey .id_table = mcp41010_id, 197*092cb71aSChris Coffey }; 198*092cb71aSChris Coffey 199*092cb71aSChris Coffey module_spi_driver(mcp41010_driver); 200*092cb71aSChris Coffey 201*092cb71aSChris Coffey MODULE_AUTHOR("Chris Coffey <cmc@babblebit.net>"); 202*092cb71aSChris Coffey MODULE_DESCRIPTION("MCP41010 digital potentiometer"); 203*092cb71aSChris Coffey MODULE_LICENSE("GPL v2"); 204