Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: dac: driver for MCP4821

Adds driver for the MCP48xx series of DACs.

Device uses a simplex SPI channel. To set the value of an output channel,
a 16-bit data of following format must be written:

Bit field | Description
15 [MSB] | Channel selection bit
0 -> Channel A
1 -> Channel B
13 | Output Gain Selection bit
0 -> 2x Gain (Vref = 4.096V)
1 -> 1x Gain (Vref = 2.048V)
12 | Output Shutdown Control bit
0 -> Shutdown the selected channel
1 -> Active mode operation
11-0 [LSB]| DAC Input Data bits
Value's big endian representation is taken as input for the
selected DAC channel. For devices with a resolution of less
than 12-bits, only the x most significant bits are considered
where x is the resolution of the device.
Reference: Page#22 [MCP48x2 Datasheet]

Supported devices:
+---------+--------------+-------------+
| Device | Resolution | Channels |
|---------|--------------|-------------|
| MCP4801 | 8-bit | 1 |
| MCP4802 | 8-bit | 2 |
| MCP4811 | 10-bit | 1 |
| MCP4812 | 10-bit | 2 |
| MCP4821 | 12-bit | 1 |
| MCP4822 | 12-bit | 2 |
+---------+--------------+-------------+

Devices tested:
MCP4821 [12-bit single channel]
MCP4802 [8-bit dual channel]

Tested on Raspberry Pi Zero 2W

Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22244B.pdf #MCP48x1
Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf #MCP48x2
Signed-off-by: Anshul Dalal <anshulusr@gmail.com>
Link: https://lore.kernel.org/r/20231220151954.154595-2-anshulusr@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Anshul Dalal and committed by
Jonathan Cameron
cdf3ecb0 6b626eee

+254
+7
MAINTAINERS
··· 13174 13174 F: drivers/iio/potentiometer/mcp4018.c 13175 13175 F: drivers/iio/potentiometer/mcp4531.c 13176 13176 13177 + MCP4821 DAC DRIVER 13178 + M: Anshul Dalal <anshulusr@gmail.com> 13179 + L: linux-iio@vger.kernel.org 13180 + S: Maintained 13181 + F: Documentation/devicetree/bindings/iio/dac/microchip,mcp4821.yaml 13182 + F: drivers/iio/dac/mcp4821.c 13183 + 13177 13184 MCR20A IEEE-802.15.4 RADIO DRIVER 13178 13185 M: Stefan Schmidt <stefan@datenfreihafen.org> 13179 13186 L: linux-wpan@vger.kernel.org
+10
drivers/iio/dac/Kconfig
··· 400 400 To compile this driver as a module, choose M here: the module 401 401 will be called mcp4728. 402 402 403 + config MCP4821 404 + tristate "MCP4801/02/11/12/21/22 DAC driver" 405 + depends on SPI 406 + help 407 + Say yes here to build the driver for the Microchip MCP4801 408 + MCP4802, MCP4811, MCP4812, MCP4821 and MCP4822 DAC devices. 409 + 410 + To compile this driver as a module, choose M here: the module 411 + will be called mcp4821. 412 + 403 413 config MCP4922 404 414 tristate "MCP4902, MCP4912, MCP4922 DAC driver" 405 415 depends on SPI
+1
drivers/iio/dac/Makefile
··· 42 42 obj-$(CONFIG_MAX5821) += max5821.o 43 43 obj-$(CONFIG_MCP4725) += mcp4725.o 44 44 obj-$(CONFIG_MCP4728) += mcp4728.o 45 + obj-$(CONFIG_MCP4821) += mcp4821.o 45 46 obj-$(CONFIG_MCP4922) += mcp4922.o 46 47 obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o 47 48 obj-$(CONFIG_STM32_DAC) += stm32-dac.o
+236
drivers/iio/dac/mcp4821.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com> 4 + * 5 + * Driver for Microchip MCP4801, MCP4802, MCP4811, MCP4812, MCP4821 and MCP4822 6 + * 7 + * Based on the work of: 8 + * Michael Welling (MCP4922 Driver) 9 + * 10 + * Datasheet: 11 + * MCP48x1: https://ww1.microchip.com/downloads/en/DeviceDoc/22244B.pdf 12 + * MCP48x2: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf 13 + * 14 + * TODO: 15 + * - Configurable gain 16 + * - Regulator control 17 + */ 18 + 19 + #include <linux/module.h> 20 + #include <linux/of.h> 21 + #include <linux/spi/spi.h> 22 + 23 + #include <linux/iio/iio.h> 24 + #include <linux/iio/types.h> 25 + 26 + #include <asm/unaligned.h> 27 + 28 + #define MCP4821_ACTIVE_MODE BIT(12) 29 + #define MCP4802_SECOND_CHAN BIT(15) 30 + 31 + /* DAC uses an internal Voltage reference of 4.096V at a gain of 2x */ 32 + #define MCP4821_2X_GAIN_VREF_MV 4096 33 + 34 + enum mcp4821_supported_drvice_ids { 35 + ID_MCP4801, 36 + ID_MCP4802, 37 + ID_MCP4811, 38 + ID_MCP4812, 39 + ID_MCP4821, 40 + ID_MCP4822, 41 + }; 42 + 43 + struct mcp4821_state { 44 + struct spi_device *spi; 45 + u16 dac_value[2]; 46 + }; 47 + 48 + struct mcp4821_chip_info { 49 + const char *name; 50 + int num_channels; 51 + const struct iio_chan_spec channels[2]; 52 + }; 53 + 54 + #define MCP4821_CHAN(channel_id, resolution) \ 55 + { \ 56 + .type = IIO_VOLTAGE, .output = 1, .indexed = 1, \ 57 + .channel = (channel_id), \ 58 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 59 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 60 + .scan_type = { \ 61 + .realbits = (resolution), \ 62 + .shift = 12 - (resolution), \ 63 + }, \ 64 + } 65 + 66 + static const struct mcp4821_chip_info mcp4821_chip_info_table[6] = { 67 + [ID_MCP4801] = { 68 + .name = "mcp4801", 69 + .num_channels = 1, 70 + .channels = { 71 + MCP4821_CHAN(0, 8), 72 + }, 73 + }, 74 + [ID_MCP4802] = { 75 + .name = "mcp4802", 76 + .num_channels = 2, 77 + .channels = { 78 + MCP4821_CHAN(0, 8), 79 + MCP4821_CHAN(1, 8), 80 + }, 81 + }, 82 + [ID_MCP4811] = { 83 + .name = "mcp4811", 84 + .num_channels = 1, 85 + .channels = { 86 + MCP4821_CHAN(0, 10), 87 + }, 88 + }, 89 + [ID_MCP4812] = { 90 + .name = "mcp4812", 91 + .num_channels = 2, 92 + .channels = { 93 + MCP4821_CHAN(0, 10), 94 + MCP4821_CHAN(1, 10), 95 + }, 96 + }, 97 + [ID_MCP4821] = { 98 + .name = "mcp4821", 99 + .num_channels = 1, 100 + .channels = { 101 + MCP4821_CHAN(0, 12), 102 + }, 103 + }, 104 + [ID_MCP4822] = { 105 + .name = "mcp4822", 106 + .num_channels = 2, 107 + .channels = { 108 + MCP4821_CHAN(0, 12), 109 + MCP4821_CHAN(1, 12), 110 + }, 111 + }, 112 + }; 113 + 114 + static int mcp4821_read_raw(struct iio_dev *indio_dev, 115 + struct iio_chan_spec const *chan, int *val, 116 + int *val2, long mask) 117 + { 118 + struct mcp4821_state *state; 119 + 120 + switch (mask) { 121 + case IIO_CHAN_INFO_RAW: 122 + state = iio_priv(indio_dev); 123 + *val = state->dac_value[chan->channel]; 124 + return IIO_VAL_INT; 125 + case IIO_CHAN_INFO_SCALE: 126 + *val = MCP4821_2X_GAIN_VREF_MV; 127 + *val2 = chan->scan_type.realbits; 128 + return IIO_VAL_FRACTIONAL_LOG2; 129 + default: 130 + return -EINVAL; 131 + } 132 + } 133 + 134 + static int mcp4821_write_raw(struct iio_dev *indio_dev, 135 + struct iio_chan_spec const *chan, int val, 136 + int val2, long mask) 137 + { 138 + struct mcp4821_state *state = iio_priv(indio_dev); 139 + u16 write_val; 140 + __be16 write_buffer; 141 + int ret; 142 + 143 + if (val2 != 0) 144 + return -EINVAL; 145 + 146 + if (val < 0 || val >= BIT(chan->scan_type.realbits)) 147 + return -EINVAL; 148 + 149 + if (mask != IIO_CHAN_INFO_RAW) 150 + return -EINVAL; 151 + 152 + write_val = MCP4821_ACTIVE_MODE | val << chan->scan_type.shift; 153 + if (chan->channel) 154 + write_val |= MCP4802_SECOND_CHAN; 155 + 156 + write_buffer = cpu_to_be16(write_val); 157 + ret = spi_write(state->spi, &write_buffer, sizeof(write_buffer)); 158 + if (ret) { 159 + dev_err(&state->spi->dev, "Failed to write to device: %d", ret); 160 + return ret; 161 + } 162 + 163 + state->dac_value[chan->channel] = val; 164 + 165 + return 0; 166 + } 167 + 168 + static const struct iio_info mcp4821_info = { 169 + .read_raw = &mcp4821_read_raw, 170 + .write_raw = &mcp4821_write_raw, 171 + }; 172 + 173 + static int mcp4821_probe(struct spi_device *spi) 174 + { 175 + struct iio_dev *indio_dev; 176 + struct mcp4821_state *state; 177 + const struct mcp4821_chip_info *info; 178 + 179 + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); 180 + if (indio_dev == NULL) 181 + return -ENOMEM; 182 + 183 + state = iio_priv(indio_dev); 184 + state->spi = spi; 185 + 186 + info = spi_get_device_match_data(spi); 187 + indio_dev->name = info->name; 188 + indio_dev->info = &mcp4821_info; 189 + indio_dev->modes = INDIO_DIRECT_MODE; 190 + indio_dev->channels = info->channels; 191 + indio_dev->num_channels = info->num_channels; 192 + 193 + return devm_iio_device_register(&spi->dev, indio_dev); 194 + } 195 + 196 + #define MCP4821_COMPATIBLE(of_compatible, id) \ 197 + { \ 198 + .compatible = of_compatible, \ 199 + .data = &mcp4821_chip_info_table[id] \ 200 + } 201 + 202 + static const struct of_device_id mcp4821_of_table[] = { 203 + MCP4821_COMPATIBLE("microchip,mcp4801", ID_MCP4801), 204 + MCP4821_COMPATIBLE("microchip,mcp4802", ID_MCP4802), 205 + MCP4821_COMPATIBLE("microchip,mcp4811", ID_MCP4811), 206 + MCP4821_COMPATIBLE("microchip,mcp4812", ID_MCP4812), 207 + MCP4821_COMPATIBLE("microchip,mcp4821", ID_MCP4821), 208 + MCP4821_COMPATIBLE("microchip,mcp4822", ID_MCP4822), 209 + { /* Sentinel */ } 210 + }; 211 + MODULE_DEVICE_TABLE(of, mcp4821_of_table); 212 + 213 + static const struct spi_device_id mcp4821_id_table[] = { 214 + { "mcp4801", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4801]}, 215 + { "mcp4802", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4802]}, 216 + { "mcp4811", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4811]}, 217 + { "mcp4812", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812]}, 218 + { "mcp4821", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821]}, 219 + { "mcp4822", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822]}, 220 + { /* Sentinel */ } 221 + }; 222 + MODULE_DEVICE_TABLE(spi, mcp4821_id_table); 223 + 224 + static struct spi_driver mcp4821_driver = { 225 + .driver = { 226 + .name = "mcp4821", 227 + .of_match_table = mcp4821_of_table, 228 + }, 229 + .probe = mcp4821_probe, 230 + .id_table = mcp4821_id_table, 231 + }; 232 + module_spi_driver(mcp4821_driver); 233 + 234 + MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>"); 235 + MODULE_DESCRIPTION("Microchip MCP4821 DAC Driver"); 236 + MODULE_LICENSE("GPL");