1 // SPDX-License-Identifier: GPL-2.0-or-later
5 * Driver for Microchip Digital to Analog Converters.
6 * Supports MCP4902, MCP4912, and MCP4922.
8 * Copyright (c) 2014 EMAC Inc.
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/spi/spi.h>
14 #include <linux/iio/iio.h>
15 #include <linux/iio/sysfs.h>
16 #include <linux/regulator/consumer.h>
17 #include <linux/bitops.h>
19 #define MCP4922_NUM_CHANNELS 2
21 enum mcp4922_supported_device_ids
{
27 struct mcp4922_state
{
28 struct spi_device
*spi
;
29 unsigned int value
[MCP4922_NUM_CHANNELS
];
31 struct regulator
*vref_reg
;
32 u8 mosi
[2] ____cacheline_aligned
;
35 #define MCP4922_CHAN(chan, bits) { \
36 .type = IIO_VOLTAGE, \
40 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
41 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
46 .shift = 12 - (bits), \
50 static int mcp4922_spi_write(struct mcp4922_state
*state
, u8 addr
, u32 val
)
52 state
->mosi
[1] = val
& 0xff;
53 state
->mosi
[0] = (addr
== 0) ? 0x00 : 0x80;
54 state
->mosi
[0] |= 0x30 | ((val
>> 8) & 0x0f);
56 return spi_write(state
->spi
, state
->mosi
, 2);
59 static int mcp4922_read_raw(struct iio_dev
*indio_dev
,
60 struct iio_chan_spec
const *chan
,
65 struct mcp4922_state
*state
= iio_priv(indio_dev
);
68 case IIO_CHAN_INFO_RAW
:
69 *val
= state
->value
[chan
->channel
];
71 case IIO_CHAN_INFO_SCALE
:
72 *val
= state
->vref_mv
;
73 *val2
= chan
->scan_type
.realbits
;
74 return IIO_VAL_FRACTIONAL_LOG2
;
80 static int mcp4922_write_raw(struct iio_dev
*indio_dev
,
81 struct iio_chan_spec
const *chan
,
86 struct mcp4922_state
*state
= iio_priv(indio_dev
);
93 case IIO_CHAN_INFO_RAW
:
94 if (val
< 0 || val
> GENMASK(chan
->scan_type
.realbits
- 1, 0))
96 val
<<= chan
->scan_type
.shift
;
98 ret
= mcp4922_spi_write(state
, chan
->channel
, val
);
100 state
->value
[chan
->channel
] = val
;
108 static const struct iio_chan_spec mcp4922_channels
[3][MCP4922_NUM_CHANNELS
] = {
109 [ID_MCP4902
] = { MCP4922_CHAN(0, 8), MCP4922_CHAN(1, 8) },
110 [ID_MCP4912
] = { MCP4922_CHAN(0, 10), MCP4922_CHAN(1, 10) },
111 [ID_MCP4922
] = { MCP4922_CHAN(0, 12), MCP4922_CHAN(1, 12) },
114 static const struct iio_info mcp4922_info
= {
115 .read_raw
= &mcp4922_read_raw
,
116 .write_raw
= &mcp4922_write_raw
,
119 static int mcp4922_probe(struct spi_device
*spi
)
121 struct iio_dev
*indio_dev
;
122 struct mcp4922_state
*state
;
123 const struct spi_device_id
*id
;
126 indio_dev
= devm_iio_device_alloc(&spi
->dev
, sizeof(*state
));
127 if (indio_dev
== NULL
)
130 state
= iio_priv(indio_dev
);
132 state
->vref_reg
= devm_regulator_get(&spi
->dev
, "vref");
133 if (IS_ERR(state
->vref_reg
)) {
134 dev_err(&spi
->dev
, "Vref regulator not specified\n");
135 return PTR_ERR(state
->vref_reg
);
138 ret
= regulator_enable(state
->vref_reg
);
140 dev_err(&spi
->dev
, "Failed to enable vref regulator: %d\n",
145 ret
= regulator_get_voltage(state
->vref_reg
);
147 dev_err(&spi
->dev
, "Failed to read vref regulator: %d\n",
149 goto error_disable_reg
;
151 state
->vref_mv
= ret
/ 1000;
153 spi_set_drvdata(spi
, indio_dev
);
154 id
= spi_get_device_id(spi
);
155 indio_dev
->dev
.parent
= &spi
->dev
;
156 indio_dev
->info
= &mcp4922_info
;
157 indio_dev
->modes
= INDIO_DIRECT_MODE
;
158 indio_dev
->channels
= mcp4922_channels
[id
->driver_data
];
159 indio_dev
->num_channels
= MCP4922_NUM_CHANNELS
;
160 indio_dev
->name
= id
->name
;
162 ret
= iio_device_register(indio_dev
);
164 dev_err(&spi
->dev
, "Failed to register iio device: %d\n",
166 goto error_disable_reg
;
172 regulator_disable(state
->vref_reg
);
177 static int mcp4922_remove(struct spi_device
*spi
)
179 struct iio_dev
*indio_dev
= spi_get_drvdata(spi
);
180 struct mcp4922_state
*state
;
182 iio_device_unregister(indio_dev
);
183 state
= iio_priv(indio_dev
);
184 regulator_disable(state
->vref_reg
);
189 static const struct spi_device_id mcp4922_id
[] = {
190 {"mcp4902", ID_MCP4902
},
191 {"mcp4912", ID_MCP4912
},
192 {"mcp4922", ID_MCP4922
},
195 MODULE_DEVICE_TABLE(spi
, mcp4922_id
);
197 static struct spi_driver mcp4922_driver
= {
201 .probe
= mcp4922_probe
,
202 .remove
= mcp4922_remove
,
203 .id_table
= mcp4922_id
,
205 module_spi_driver(mcp4922_driver
);
207 MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>");
208 MODULE_DESCRIPTION("Microchip MCP4902, MCP4912, MCP4922 DAC");
209 MODULE_LICENSE("GPL v2");