18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * LTC2632 Digital to analog convertors spi driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2017 Maxime Roussin-Bélanger
68c2ecf20Sopenharmony_ci * expanded by Silvan Murer <silvan.murer@gmail.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
138c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define LTC2632_CMD_WRITE_INPUT_N               0x0
188c2ecf20Sopenharmony_ci#define LTC2632_CMD_UPDATE_DAC_N                0x1
198c2ecf20Sopenharmony_ci#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL    0x2
208c2ecf20Sopenharmony_ci#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_N      0x3
218c2ecf20Sopenharmony_ci#define LTC2632_CMD_POWERDOWN_DAC_N             0x4
228c2ecf20Sopenharmony_ci#define LTC2632_CMD_POWERDOWN_CHIP              0x5
238c2ecf20Sopenharmony_ci#define LTC2632_CMD_INTERNAL_REFER              0x6
248c2ecf20Sopenharmony_ci#define LTC2632_CMD_EXTERNAL_REFER              0x7
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * struct ltc2632_chip_info - chip specific information
288c2ecf20Sopenharmony_ci * @channels:		channel spec for the DAC
298c2ecf20Sopenharmony_ci * @num_channels:	DAC channel count of the chip
308c2ecf20Sopenharmony_ci * @vref_mv:		internal reference voltage
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_cistruct ltc2632_chip_info {
338c2ecf20Sopenharmony_ci	const struct iio_chan_spec *channels;
348c2ecf20Sopenharmony_ci	const size_t num_channels;
358c2ecf20Sopenharmony_ci	const int vref_mv;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/**
398c2ecf20Sopenharmony_ci * struct ltc2632_state - driver instance specific data
408c2ecf20Sopenharmony_ci * @spi_dev:			pointer to the spi_device struct
418c2ecf20Sopenharmony_ci * @powerdown_cache_mask:	used to show current channel powerdown state
428c2ecf20Sopenharmony_ci * @vref_mv:			used reference voltage (internal or external)
438c2ecf20Sopenharmony_ci * @vref_reg:		regulator for the reference voltage
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistruct ltc2632_state {
468c2ecf20Sopenharmony_ci	struct spi_device *spi_dev;
478c2ecf20Sopenharmony_ci	unsigned int powerdown_cache_mask;
488c2ecf20Sopenharmony_ci	int vref_mv;
498c2ecf20Sopenharmony_ci	struct regulator *vref_reg;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cienum ltc2632_supported_device_ids {
538c2ecf20Sopenharmony_ci	ID_LTC2632L12,
548c2ecf20Sopenharmony_ci	ID_LTC2632L10,
558c2ecf20Sopenharmony_ci	ID_LTC2632L8,
568c2ecf20Sopenharmony_ci	ID_LTC2632H12,
578c2ecf20Sopenharmony_ci	ID_LTC2632H10,
588c2ecf20Sopenharmony_ci	ID_LTC2632H8,
598c2ecf20Sopenharmony_ci	ID_LTC2634L12,
608c2ecf20Sopenharmony_ci	ID_LTC2634L10,
618c2ecf20Sopenharmony_ci	ID_LTC2634L8,
628c2ecf20Sopenharmony_ci	ID_LTC2634H12,
638c2ecf20Sopenharmony_ci	ID_LTC2634H10,
648c2ecf20Sopenharmony_ci	ID_LTC2634H8,
658c2ecf20Sopenharmony_ci	ID_LTC2636L12,
668c2ecf20Sopenharmony_ci	ID_LTC2636L10,
678c2ecf20Sopenharmony_ci	ID_LTC2636L8,
688c2ecf20Sopenharmony_ci	ID_LTC2636H12,
698c2ecf20Sopenharmony_ci	ID_LTC2636H10,
708c2ecf20Sopenharmony_ci	ID_LTC2636H8,
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int ltc2632_spi_write(struct spi_device *spi,
748c2ecf20Sopenharmony_ci			     u8 cmd, u8 addr, u16 val, u8 shift)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	u32 data;
778c2ecf20Sopenharmony_ci	u8 msg[3];
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/*
808c2ecf20Sopenharmony_ci	 * The input shift register is 24 bits wide.
818c2ecf20Sopenharmony_ci	 * The next four are the command bits, C3 to C0,
828c2ecf20Sopenharmony_ci	 * followed by the 4-bit DAC address, A3 to A0, and then the
838c2ecf20Sopenharmony_ci	 * 12-, 10-, 8-bit data-word. The data-word comprises the 12-,
848c2ecf20Sopenharmony_ci	 * 10-, 8-bit input code followed by 4, 6, or 8 don't care bits.
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	data = (cmd << 20) | (addr << 16) | (val << shift);
878c2ecf20Sopenharmony_ci	put_unaligned_be24(data, &msg[0]);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return spi_write(spi, msg, sizeof(msg));
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int ltc2632_read_raw(struct iio_dev *indio_dev,
938c2ecf20Sopenharmony_ci			    struct iio_chan_spec const *chan,
948c2ecf20Sopenharmony_ci			    int *val,
958c2ecf20Sopenharmony_ci			    int *val2,
968c2ecf20Sopenharmony_ci			    long m)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	const struct ltc2632_state *st = iio_priv(indio_dev);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	switch (m) {
1018c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
1028c2ecf20Sopenharmony_ci		*val = st->vref_mv;
1038c2ecf20Sopenharmony_ci		*val2 = chan->scan_type.realbits;
1048c2ecf20Sopenharmony_ci		return IIO_VAL_FRACTIONAL_LOG2;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	return -EINVAL;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int ltc2632_write_raw(struct iio_dev *indio_dev,
1108c2ecf20Sopenharmony_ci			     struct iio_chan_spec const *chan,
1118c2ecf20Sopenharmony_ci			     int val,
1128c2ecf20Sopenharmony_ci			     int val2,
1138c2ecf20Sopenharmony_ci			     long mask)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct ltc2632_state *st = iio_priv(indio_dev);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	switch (mask) {
1188c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
1198c2ecf20Sopenharmony_ci		if (val >= (1 << chan->scan_type.realbits) || val < 0)
1208c2ecf20Sopenharmony_ci			return -EINVAL;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		return ltc2632_spi_write(st->spi_dev,
1238c2ecf20Sopenharmony_ci					 LTC2632_CMD_WRITE_INPUT_N_UPDATE_N,
1248c2ecf20Sopenharmony_ci					 chan->address, val,
1258c2ecf20Sopenharmony_ci					 chan->scan_type.shift);
1268c2ecf20Sopenharmony_ci	default:
1278c2ecf20Sopenharmony_ci		return -EINVAL;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic ssize_t ltc2632_read_dac_powerdown(struct iio_dev *indio_dev,
1328c2ecf20Sopenharmony_ci					  uintptr_t private,
1338c2ecf20Sopenharmony_ci					  const struct iio_chan_spec *chan,
1348c2ecf20Sopenharmony_ci					  char *buf)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct ltc2632_state *st = iio_priv(indio_dev);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n",
1398c2ecf20Sopenharmony_ci		       !!(st->powerdown_cache_mask & (1 << chan->channel)));
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev,
1438c2ecf20Sopenharmony_ci					   uintptr_t private,
1448c2ecf20Sopenharmony_ci					   const struct iio_chan_spec *chan,
1458c2ecf20Sopenharmony_ci					   const char *buf,
1468c2ecf20Sopenharmony_ci					   size_t len)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	bool pwr_down;
1498c2ecf20Sopenharmony_ci	int ret;
1508c2ecf20Sopenharmony_ci	struct ltc2632_state *st = iio_priv(indio_dev);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	ret = strtobool(buf, &pwr_down);
1538c2ecf20Sopenharmony_ci	if (ret)
1548c2ecf20Sopenharmony_ci		return ret;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (pwr_down)
1578c2ecf20Sopenharmony_ci		st->powerdown_cache_mask |= (1 << chan->channel);
1588c2ecf20Sopenharmony_ci	else
1598c2ecf20Sopenharmony_ci		st->powerdown_cache_mask &= ~(1 << chan->channel);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	ret = ltc2632_spi_write(st->spi_dev,
1628c2ecf20Sopenharmony_ci				LTC2632_CMD_POWERDOWN_DAC_N,
1638c2ecf20Sopenharmony_ci				chan->channel, 0, 0);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return ret ? ret : len;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic const struct iio_info ltc2632_info = {
1698c2ecf20Sopenharmony_ci	.write_raw	= ltc2632_write_raw,
1708c2ecf20Sopenharmony_ci	.read_raw	= ltc2632_read_raw,
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic const struct iio_chan_spec_ext_info ltc2632_ext_info[] = {
1748c2ecf20Sopenharmony_ci	{
1758c2ecf20Sopenharmony_ci		.name = "powerdown",
1768c2ecf20Sopenharmony_ci		.read = ltc2632_read_dac_powerdown,
1778c2ecf20Sopenharmony_ci		.write = ltc2632_write_dac_powerdown,
1788c2ecf20Sopenharmony_ci		.shared = IIO_SEPARATE,
1798c2ecf20Sopenharmony_ci	},
1808c2ecf20Sopenharmony_ci	{ },
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci#define LTC2632_CHANNEL(_chan, _bits) { \
1848c2ecf20Sopenharmony_ci		.type = IIO_VOLTAGE, \
1858c2ecf20Sopenharmony_ci		.indexed = 1, \
1868c2ecf20Sopenharmony_ci		.output = 1, \
1878c2ecf20Sopenharmony_ci		.channel = (_chan), \
1888c2ecf20Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
1898c2ecf20Sopenharmony_ci		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
1908c2ecf20Sopenharmony_ci		.address = (_chan), \
1918c2ecf20Sopenharmony_ci		.scan_type = { \
1928c2ecf20Sopenharmony_ci			.realbits	= (_bits), \
1938c2ecf20Sopenharmony_ci			.shift		= 16 - (_bits), \
1948c2ecf20Sopenharmony_ci		}, \
1958c2ecf20Sopenharmony_ci		.ext_info = ltc2632_ext_info, \
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci#define DECLARE_LTC2632_CHANNELS(_name, _bits) \
1998c2ecf20Sopenharmony_ci	const struct iio_chan_spec _name ## _channels[] = { \
2008c2ecf20Sopenharmony_ci		LTC2632_CHANNEL(0, _bits), \
2018c2ecf20Sopenharmony_ci		LTC2632_CHANNEL(1, _bits), \
2028c2ecf20Sopenharmony_ci		LTC2632_CHANNEL(2, _bits), \
2038c2ecf20Sopenharmony_ci		LTC2632_CHANNEL(3, _bits), \
2048c2ecf20Sopenharmony_ci		LTC2632_CHANNEL(4, _bits), \
2058c2ecf20Sopenharmony_ci		LTC2632_CHANNEL(5, _bits), \
2068c2ecf20Sopenharmony_ci		LTC2632_CHANNEL(6, _bits), \
2078c2ecf20Sopenharmony_ci		LTC2632_CHANNEL(7, _bits), \
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic DECLARE_LTC2632_CHANNELS(ltc2632x12, 12);
2118c2ecf20Sopenharmony_cistatic DECLARE_LTC2632_CHANNELS(ltc2632x10, 10);
2128c2ecf20Sopenharmony_cistatic DECLARE_LTC2632_CHANNELS(ltc2632x8, 8);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = {
2158c2ecf20Sopenharmony_ci	[ID_LTC2632L12] = {
2168c2ecf20Sopenharmony_ci		.channels	= ltc2632x12_channels,
2178c2ecf20Sopenharmony_ci		.num_channels	= 2,
2188c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2198c2ecf20Sopenharmony_ci	},
2208c2ecf20Sopenharmony_ci	[ID_LTC2632L10] = {
2218c2ecf20Sopenharmony_ci		.channels	= ltc2632x10_channels,
2228c2ecf20Sopenharmony_ci		.num_channels	= 2,
2238c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2248c2ecf20Sopenharmony_ci	},
2258c2ecf20Sopenharmony_ci	[ID_LTC2632L8] =  {
2268c2ecf20Sopenharmony_ci		.channels	= ltc2632x8_channels,
2278c2ecf20Sopenharmony_ci		.num_channels	= 2,
2288c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2298c2ecf20Sopenharmony_ci	},
2308c2ecf20Sopenharmony_ci	[ID_LTC2632H12] = {
2318c2ecf20Sopenharmony_ci		.channels	= ltc2632x12_channels,
2328c2ecf20Sopenharmony_ci		.num_channels	= 2,
2338c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
2348c2ecf20Sopenharmony_ci	},
2358c2ecf20Sopenharmony_ci	[ID_LTC2632H10] = {
2368c2ecf20Sopenharmony_ci		.channels	= ltc2632x10_channels,
2378c2ecf20Sopenharmony_ci		.num_channels	= 2,
2388c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
2398c2ecf20Sopenharmony_ci	},
2408c2ecf20Sopenharmony_ci	[ID_LTC2632H8] =  {
2418c2ecf20Sopenharmony_ci		.channels	= ltc2632x8_channels,
2428c2ecf20Sopenharmony_ci		.num_channels	= 2,
2438c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
2448c2ecf20Sopenharmony_ci	},
2458c2ecf20Sopenharmony_ci	[ID_LTC2634L12] = {
2468c2ecf20Sopenharmony_ci		.channels	= ltc2632x12_channels,
2478c2ecf20Sopenharmony_ci		.num_channels	= 4,
2488c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2498c2ecf20Sopenharmony_ci	},
2508c2ecf20Sopenharmony_ci	[ID_LTC2634L10] = {
2518c2ecf20Sopenharmony_ci		.channels	= ltc2632x10_channels,
2528c2ecf20Sopenharmony_ci		.num_channels	= 4,
2538c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2548c2ecf20Sopenharmony_ci	},
2558c2ecf20Sopenharmony_ci	[ID_LTC2634L8] =  {
2568c2ecf20Sopenharmony_ci		.channels	= ltc2632x8_channels,
2578c2ecf20Sopenharmony_ci		.num_channels	= 4,
2588c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2598c2ecf20Sopenharmony_ci	},
2608c2ecf20Sopenharmony_ci	[ID_LTC2634H12] = {
2618c2ecf20Sopenharmony_ci		.channels	= ltc2632x12_channels,
2628c2ecf20Sopenharmony_ci		.num_channels	= 4,
2638c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
2648c2ecf20Sopenharmony_ci	},
2658c2ecf20Sopenharmony_ci	[ID_LTC2634H10] = {
2668c2ecf20Sopenharmony_ci		.channels	= ltc2632x10_channels,
2678c2ecf20Sopenharmony_ci		.num_channels	= 4,
2688c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
2698c2ecf20Sopenharmony_ci	},
2708c2ecf20Sopenharmony_ci	[ID_LTC2634H8] =  {
2718c2ecf20Sopenharmony_ci		.channels	= ltc2632x8_channels,
2728c2ecf20Sopenharmony_ci		.num_channels	= 4,
2738c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
2748c2ecf20Sopenharmony_ci	},
2758c2ecf20Sopenharmony_ci	[ID_LTC2636L12] = {
2768c2ecf20Sopenharmony_ci		.channels	= ltc2632x12_channels,
2778c2ecf20Sopenharmony_ci		.num_channels	= 8,
2788c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2798c2ecf20Sopenharmony_ci	},
2808c2ecf20Sopenharmony_ci	[ID_LTC2636L10] = {
2818c2ecf20Sopenharmony_ci		.channels	= ltc2632x10_channels,
2828c2ecf20Sopenharmony_ci		.num_channels	= 8,
2838c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2848c2ecf20Sopenharmony_ci	},
2858c2ecf20Sopenharmony_ci	[ID_LTC2636L8] =  {
2868c2ecf20Sopenharmony_ci		.channels	= ltc2632x8_channels,
2878c2ecf20Sopenharmony_ci		.num_channels	= 8,
2888c2ecf20Sopenharmony_ci		.vref_mv	= 2500,
2898c2ecf20Sopenharmony_ci	},
2908c2ecf20Sopenharmony_ci	[ID_LTC2636H12] = {
2918c2ecf20Sopenharmony_ci		.channels	= ltc2632x12_channels,
2928c2ecf20Sopenharmony_ci		.num_channels	= 8,
2938c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
2948c2ecf20Sopenharmony_ci	},
2958c2ecf20Sopenharmony_ci	[ID_LTC2636H10] = {
2968c2ecf20Sopenharmony_ci		.channels	= ltc2632x10_channels,
2978c2ecf20Sopenharmony_ci		.num_channels	= 8,
2988c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
2998c2ecf20Sopenharmony_ci	},
3008c2ecf20Sopenharmony_ci	[ID_LTC2636H8] =  {
3018c2ecf20Sopenharmony_ci		.channels	= ltc2632x8_channels,
3028c2ecf20Sopenharmony_ci		.num_channels	= 8,
3038c2ecf20Sopenharmony_ci		.vref_mv	= 4096,
3048c2ecf20Sopenharmony_ci	},
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int ltc2632_probe(struct spi_device *spi)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct ltc2632_state *st;
3108c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
3118c2ecf20Sopenharmony_ci	struct ltc2632_chip_info *chip_info;
3128c2ecf20Sopenharmony_ci	int ret;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
3158c2ecf20Sopenharmony_ci	if (!indio_dev)
3168c2ecf20Sopenharmony_ci		return -ENOMEM;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	st = iio_priv(indio_dev);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, indio_dev);
3218c2ecf20Sopenharmony_ci	st->spi_dev = spi;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	chip_info = (struct ltc2632_chip_info *)
3248c2ecf20Sopenharmony_ci			spi_get_device_id(spi)->driver_data;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref");
3278c2ecf20Sopenharmony_ci	if (PTR_ERR(st->vref_reg) == -ENODEV) {
3288c2ecf20Sopenharmony_ci		/* use internal reference voltage */
3298c2ecf20Sopenharmony_ci		st->vref_reg = NULL;
3308c2ecf20Sopenharmony_ci		st->vref_mv = chip_info->vref_mv;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci		ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER,
3338c2ecf20Sopenharmony_ci				0, 0, 0);
3348c2ecf20Sopenharmony_ci		if (ret) {
3358c2ecf20Sopenharmony_ci			dev_err(&spi->dev,
3368c2ecf20Sopenharmony_ci				"Set internal reference command failed, %d\n",
3378c2ecf20Sopenharmony_ci				ret);
3388c2ecf20Sopenharmony_ci			return ret;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci	} else if (IS_ERR(st->vref_reg)) {
3418c2ecf20Sopenharmony_ci		dev_err(&spi->dev,
3428c2ecf20Sopenharmony_ci				"Error getting voltage reference regulator\n");
3438c2ecf20Sopenharmony_ci		return PTR_ERR(st->vref_reg);
3448c2ecf20Sopenharmony_ci	} else {
3458c2ecf20Sopenharmony_ci		/* use external reference voltage */
3468c2ecf20Sopenharmony_ci		ret = regulator_enable(st->vref_reg);
3478c2ecf20Sopenharmony_ci		if (ret) {
3488c2ecf20Sopenharmony_ci			dev_err(&spi->dev,
3498c2ecf20Sopenharmony_ci				"enable reference regulator failed, %d\n",
3508c2ecf20Sopenharmony_ci				ret);
3518c2ecf20Sopenharmony_ci			return ret;
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci		st->vref_mv = regulator_get_voltage(st->vref_reg) / 1000;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		ret = ltc2632_spi_write(spi, LTC2632_CMD_EXTERNAL_REFER,
3568c2ecf20Sopenharmony_ci				0, 0, 0);
3578c2ecf20Sopenharmony_ci		if (ret) {
3588c2ecf20Sopenharmony_ci			dev_err(&spi->dev,
3598c2ecf20Sopenharmony_ci				"Set external reference command failed, %d\n",
3608c2ecf20Sopenharmony_ci				ret);
3618c2ecf20Sopenharmony_ci			return ret;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name
3668c2ecf20Sopenharmony_ci						 : spi_get_device_id(spi)->name;
3678c2ecf20Sopenharmony_ci	indio_dev->info = &ltc2632_info;
3688c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
3698c2ecf20Sopenharmony_ci	indio_dev->channels = chip_info->channels;
3708c2ecf20Sopenharmony_ci	indio_dev->num_channels = chip_info->num_channels;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return iio_device_register(indio_dev);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int ltc2632_remove(struct spi_device *spi)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = spi_get_drvdata(spi);
3788c2ecf20Sopenharmony_ci	struct ltc2632_state *st = iio_priv(indio_dev);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	iio_device_unregister(indio_dev);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (st->vref_reg)
3838c2ecf20Sopenharmony_ci		regulator_disable(st->vref_reg);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic const struct spi_device_id ltc2632_id[] = {
3898c2ecf20Sopenharmony_ci	{ "ltc2632-l12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L12] },
3908c2ecf20Sopenharmony_ci	{ "ltc2632-l10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L10] },
3918c2ecf20Sopenharmony_ci	{ "ltc2632-l8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L8] },
3928c2ecf20Sopenharmony_ci	{ "ltc2632-h12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H12] },
3938c2ecf20Sopenharmony_ci	{ "ltc2632-h10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H10] },
3948c2ecf20Sopenharmony_ci	{ "ltc2632-h8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H8] },
3958c2ecf20Sopenharmony_ci	{ "ltc2634-l12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634L12] },
3968c2ecf20Sopenharmony_ci	{ "ltc2634-l10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634L10] },
3978c2ecf20Sopenharmony_ci	{ "ltc2634-l8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634L8] },
3988c2ecf20Sopenharmony_ci	{ "ltc2634-h12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634H12] },
3998c2ecf20Sopenharmony_ci	{ "ltc2634-h10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634H10] },
4008c2ecf20Sopenharmony_ci	{ "ltc2634-h8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634H8] },
4018c2ecf20Sopenharmony_ci	{ "ltc2636-l12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636L12] },
4028c2ecf20Sopenharmony_ci	{ "ltc2636-l10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636L10] },
4038c2ecf20Sopenharmony_ci	{ "ltc2636-l8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636L8] },
4048c2ecf20Sopenharmony_ci	{ "ltc2636-h12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636H12] },
4058c2ecf20Sopenharmony_ci	{ "ltc2636-h10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636H10] },
4068c2ecf20Sopenharmony_ci	{ "ltc2636-h8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636H8] },
4078c2ecf20Sopenharmony_ci	{}
4088c2ecf20Sopenharmony_ci};
4098c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ltc2632_id);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic const struct of_device_id ltc2632_of_match[] = {
4128c2ecf20Sopenharmony_ci	{
4138c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2632-l12",
4148c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2632L12]
4158c2ecf20Sopenharmony_ci	}, {
4168c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2632-l10",
4178c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2632L10]
4188c2ecf20Sopenharmony_ci	}, {
4198c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2632-l8",
4208c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2632L8]
4218c2ecf20Sopenharmony_ci	}, {
4228c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2632-h12",
4238c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2632H12]
4248c2ecf20Sopenharmony_ci	}, {
4258c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2632-h10",
4268c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2632H10]
4278c2ecf20Sopenharmony_ci	}, {
4288c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2632-h8",
4298c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2632H8]
4308c2ecf20Sopenharmony_ci	}, {
4318c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2634-l12",
4328c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2634L12]
4338c2ecf20Sopenharmony_ci	}, {
4348c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2634-l10",
4358c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2634L10]
4368c2ecf20Sopenharmony_ci	}, {
4378c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2634-l8",
4388c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2634L8]
4398c2ecf20Sopenharmony_ci	}, {
4408c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2634-h12",
4418c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2634H12]
4428c2ecf20Sopenharmony_ci	}, {
4438c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2634-h10",
4448c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2634H10]
4458c2ecf20Sopenharmony_ci	}, {
4468c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2634-h8",
4478c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2634H8]
4488c2ecf20Sopenharmony_ci	}, {
4498c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2636-l12",
4508c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2636L12]
4518c2ecf20Sopenharmony_ci	}, {
4528c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2636-l10",
4538c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2636L10]
4548c2ecf20Sopenharmony_ci	}, {
4558c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2636-l8",
4568c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2636L8]
4578c2ecf20Sopenharmony_ci	}, {
4588c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2636-h12",
4598c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2636H12]
4608c2ecf20Sopenharmony_ci	}, {
4618c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2636-h10",
4628c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2636H10]
4638c2ecf20Sopenharmony_ci	}, {
4648c2ecf20Sopenharmony_ci		.compatible = "lltc,ltc2636-h8",
4658c2ecf20Sopenharmony_ci		.data = &ltc2632_chip_info_tbl[ID_LTC2636H8]
4668c2ecf20Sopenharmony_ci	},
4678c2ecf20Sopenharmony_ci	{}
4688c2ecf20Sopenharmony_ci};
4698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ltc2632_of_match);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic struct spi_driver ltc2632_driver = {
4728c2ecf20Sopenharmony_ci	.driver		= {
4738c2ecf20Sopenharmony_ci		.name	= "ltc2632",
4748c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(ltc2632_of_match),
4758c2ecf20Sopenharmony_ci	},
4768c2ecf20Sopenharmony_ci	.probe		= ltc2632_probe,
4778c2ecf20Sopenharmony_ci	.remove		= ltc2632_remove,
4788c2ecf20Sopenharmony_ci	.id_table	= ltc2632_id,
4798c2ecf20Sopenharmony_ci};
4808c2ecf20Sopenharmony_cimodule_spi_driver(ltc2632_driver);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>");
4838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LTC2632 DAC SPI driver");
4848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
485