1 // SPDX-License-Identifier: GPL-2.0
3 * Industrial I/O driver for Microchip digital potentiometers
5 * Copyright (c) 2018 Chris Coffey <cmc@babblebit.net>
6 * Based on: Slawomir Stepien's code from mcp4131.c
8 * Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/11195c.pdf
10 * DEVID #Wipers #Positions Resistance (kOhm)
19 #include <linux/cache.h>
20 #include <linux/err.h>
21 #include <linux/iio/iio.h>
22 #include <linux/iio/types.h>
23 #include <linux/module.h>
24 #include <linux/mod_devicetable.h>
25 #include <linux/mutex.h>
26 #include <linux/property.h>
27 #include <linux/spi/spi.h>
29 #define MCP41010_MAX_WIPERS 2
30 #define MCP41010_WRITE BIT(4)
31 #define MCP41010_WIPER_MAX 255
32 #define MCP41010_WIPER_CHANNEL BIT(0)
49 static const struct mcp41010_cfg mcp41010_cfg
[] = {
50 [MCP41010
] = { .name
= "mcp41010", .wipers
= 1, .kohms
= 10, },
51 [MCP41050
] = { .name
= "mcp41050", .wipers
= 1, .kohms
= 50, },
52 [MCP41100
] = { .name
= "mcp41100", .wipers
= 1, .kohms
= 100, },
53 [MCP42010
] = { .name
= "mcp42010", .wipers
= 2, .kohms
= 10, },
54 [MCP42050
] = { .name
= "mcp42050", .wipers
= 2, .kohms
= 50, },
55 [MCP42100
] = { .name
= "mcp42100", .wipers
= 2, .kohms
= 100, },
58 struct mcp41010_data
{
59 struct spi_device
*spi
;
60 const struct mcp41010_cfg
*cfg
;
61 struct mutex lock
; /* Protect write sequences */
62 unsigned int value
[MCP41010_MAX_WIPERS
]; /* Cache wiper values */
63 u8 buf
[2] __aligned(IIO_DMA_MINALIGN
);
66 #define MCP41010_CHANNEL(ch) { \
67 .type = IIO_RESISTANCE, \
71 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
72 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
75 static const struct iio_chan_spec mcp41010_channels
[] = {
80 static int mcp41010_read_raw(struct iio_dev
*indio_dev
,
81 struct iio_chan_spec
const *chan
,
82 int *val
, int *val2
, long mask
)
84 struct mcp41010_data
*data
= iio_priv(indio_dev
);
85 int channel
= chan
->channel
;
88 case IIO_CHAN_INFO_RAW
:
89 *val
= data
->value
[channel
];
92 case IIO_CHAN_INFO_SCALE
:
93 *val
= 1000 * data
->cfg
->kohms
;
94 *val2
= MCP41010_WIPER_MAX
;
95 return IIO_VAL_FRACTIONAL
;
101 static int mcp41010_write_raw(struct iio_dev
*indio_dev
,
102 struct iio_chan_spec
const *chan
,
103 int val
, int val2
, long mask
)
106 struct mcp41010_data
*data
= iio_priv(indio_dev
);
107 int channel
= chan
->channel
;
109 if (mask
!= IIO_CHAN_INFO_RAW
)
112 if (val
> MCP41010_WIPER_MAX
|| val
< 0)
115 mutex_lock(&data
->lock
);
117 data
->buf
[0] = MCP41010_WIPER_CHANNEL
<< channel
;
118 data
->buf
[0] |= MCP41010_WRITE
;
119 data
->buf
[1] = val
& 0xff;
121 err
= spi_write(data
->spi
, data
->buf
, sizeof(data
->buf
));
123 data
->value
[channel
] = val
;
125 mutex_unlock(&data
->lock
);
130 static const struct iio_info mcp41010_info
= {
131 .read_raw
= mcp41010_read_raw
,
132 .write_raw
= mcp41010_write_raw
,
135 static int mcp41010_probe(struct spi_device
*spi
)
138 struct device
*dev
= &spi
->dev
;
139 struct mcp41010_data
*data
;
140 struct iio_dev
*indio_dev
;
142 indio_dev
= devm_iio_device_alloc(dev
, sizeof(*data
));
146 data
= iio_priv(indio_dev
);
147 spi_set_drvdata(spi
, indio_dev
);
149 data
->cfg
= device_get_match_data(&spi
->dev
);
151 data
->cfg
= &mcp41010_cfg
[spi_get_device_id(spi
)->driver_data
];
153 mutex_init(&data
->lock
);
155 indio_dev
->info
= &mcp41010_info
;
156 indio_dev
->channels
= mcp41010_channels
;
157 indio_dev
->num_channels
= data
->cfg
->wipers
;
158 indio_dev
->name
= data
->cfg
->name
;
160 err
= devm_iio_device_register(dev
, indio_dev
);
162 dev_info(&spi
->dev
, "Unable to register %s\n", indio_dev
->name
);
167 static const struct of_device_id mcp41010_match
[] = {
168 { .compatible
= "microchip,mcp41010", .data
= &mcp41010_cfg
[MCP41010
] },
169 { .compatible
= "microchip,mcp41050", .data
= &mcp41010_cfg
[MCP41050
] },
170 { .compatible
= "microchip,mcp41100", .data
= &mcp41010_cfg
[MCP41100
] },
171 { .compatible
= "microchip,mcp42010", .data
= &mcp41010_cfg
[MCP42010
] },
172 { .compatible
= "microchip,mcp42050", .data
= &mcp41010_cfg
[MCP42050
] },
173 { .compatible
= "microchip,mcp42100", .data
= &mcp41010_cfg
[MCP42100
] },
176 MODULE_DEVICE_TABLE(of
, mcp41010_match
);
178 static const struct spi_device_id mcp41010_id
[] = {
179 { "mcp41010", MCP41010
},
180 { "mcp41050", MCP41050
},
181 { "mcp41100", MCP41100
},
182 { "mcp42010", MCP42010
},
183 { "mcp42050", MCP42050
},
184 { "mcp42100", MCP42100
},
187 MODULE_DEVICE_TABLE(spi
, mcp41010_id
);
189 static struct spi_driver mcp41010_driver
= {
192 .of_match_table
= mcp41010_match
,
194 .probe
= mcp41010_probe
,
195 .id_table
= mcp41010_id
,
198 module_spi_driver(mcp41010_driver
);
200 MODULE_AUTHOR("Chris Coffey <cmc@babblebit.net>");
201 MODULE_DESCRIPTION("MCP41010 digital potentiometer");
202 MODULE_LICENSE("GPL v2");