xref: /openbmc/linux/drivers/iio/dac/ad8801.c (revision 1c20292c)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27f270bc9SGwenhael Goavec-Merou /*
37f270bc9SGwenhael Goavec-Merou  * IIO DAC driver for Analog Devices AD8801 DAC
47f270bc9SGwenhael Goavec-Merou  *
57f270bc9SGwenhael Goavec-Merou  * Copyright (C) 2016 Gwenhael Goavec-Merou
67f270bc9SGwenhael Goavec-Merou  */
77f270bc9SGwenhael Goavec-Merou 
87f270bc9SGwenhael Goavec-Merou #include <linux/iio/iio.h>
97f270bc9SGwenhael Goavec-Merou #include <linux/module.h>
107f270bc9SGwenhael Goavec-Merou #include <linux/regulator/consumer.h>
117f270bc9SGwenhael Goavec-Merou #include <linux/spi/spi.h>
127f270bc9SGwenhael Goavec-Merou #include <linux/sysfs.h>
137f270bc9SGwenhael Goavec-Merou 
147f270bc9SGwenhael Goavec-Merou #define AD8801_CFG_ADDR_OFFSET 8
157f270bc9SGwenhael Goavec-Merou 
167f270bc9SGwenhael Goavec-Merou enum ad8801_device_ids {
177f270bc9SGwenhael Goavec-Merou 	ID_AD8801,
187f270bc9SGwenhael Goavec-Merou 	ID_AD8803,
197f270bc9SGwenhael Goavec-Merou };
207f270bc9SGwenhael Goavec-Merou 
217f270bc9SGwenhael Goavec-Merou struct ad8801_state {
227f270bc9SGwenhael Goavec-Merou 	struct spi_device *spi;
237f270bc9SGwenhael Goavec-Merou 	unsigned char dac_cache[8]; /* Value write on each channel */
247f270bc9SGwenhael Goavec-Merou 	unsigned int vrefh_mv;
257f270bc9SGwenhael Goavec-Merou 	unsigned int vrefl_mv;
267f270bc9SGwenhael Goavec-Merou 	struct regulator *vrefh_reg;
277f270bc9SGwenhael Goavec-Merou 	struct regulator *vrefl_reg;
287f270bc9SGwenhael Goavec-Merou 
29*1c20292cSJonathan Cameron 	__be16 data __aligned(IIO_DMA_MINALIGN);
307f270bc9SGwenhael Goavec-Merou };
317f270bc9SGwenhael Goavec-Merou 
ad8801_spi_write(struct ad8801_state * state,u8 channel,unsigned char value)327f270bc9SGwenhael Goavec-Merou static int ad8801_spi_write(struct ad8801_state *state,
337f270bc9SGwenhael Goavec-Merou 	u8 channel, unsigned char value)
347f270bc9SGwenhael Goavec-Merou {
357f270bc9SGwenhael Goavec-Merou 	state->data = cpu_to_be16((channel << AD8801_CFG_ADDR_OFFSET) | value);
367f270bc9SGwenhael Goavec-Merou 	return spi_write(state->spi, &state->data, sizeof(state->data));
377f270bc9SGwenhael Goavec-Merou }
387f270bc9SGwenhael Goavec-Merou 
ad8801_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)397f270bc9SGwenhael Goavec-Merou static int ad8801_write_raw(struct iio_dev *indio_dev,
407f270bc9SGwenhael Goavec-Merou 	struct iio_chan_spec const *chan, int val, int val2, long mask)
417f270bc9SGwenhael Goavec-Merou {
427f270bc9SGwenhael Goavec-Merou 	struct ad8801_state *state = iio_priv(indio_dev);
437f270bc9SGwenhael Goavec-Merou 	int ret;
447f270bc9SGwenhael Goavec-Merou 
457f270bc9SGwenhael Goavec-Merou 	switch (mask) {
467f270bc9SGwenhael Goavec-Merou 	case IIO_CHAN_INFO_RAW:
477f270bc9SGwenhael Goavec-Merou 		if (val >= 256 || val < 0)
487f270bc9SGwenhael Goavec-Merou 			return -EINVAL;
497f270bc9SGwenhael Goavec-Merou 
507f270bc9SGwenhael Goavec-Merou 		ret = ad8801_spi_write(state, chan->channel, val);
517f270bc9SGwenhael Goavec-Merou 		if (ret == 0)
527f270bc9SGwenhael Goavec-Merou 			state->dac_cache[chan->channel] = val;
537f270bc9SGwenhael Goavec-Merou 		break;
547f270bc9SGwenhael Goavec-Merou 	default:
557f270bc9SGwenhael Goavec-Merou 		ret = -EINVAL;
567f270bc9SGwenhael Goavec-Merou 	}
577f270bc9SGwenhael Goavec-Merou 
587f270bc9SGwenhael Goavec-Merou 	return ret;
597f270bc9SGwenhael Goavec-Merou }
607f270bc9SGwenhael Goavec-Merou 
ad8801_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long info)617f270bc9SGwenhael Goavec-Merou static int ad8801_read_raw(struct iio_dev *indio_dev,
627f270bc9SGwenhael Goavec-Merou 	struct iio_chan_spec const *chan, int *val, int *val2, long info)
637f270bc9SGwenhael Goavec-Merou {
647f270bc9SGwenhael Goavec-Merou 	struct ad8801_state *state = iio_priv(indio_dev);
657f270bc9SGwenhael Goavec-Merou 
667f270bc9SGwenhael Goavec-Merou 	switch (info) {
677f270bc9SGwenhael Goavec-Merou 	case IIO_CHAN_INFO_RAW:
687f270bc9SGwenhael Goavec-Merou 		*val = state->dac_cache[chan->channel];
697f270bc9SGwenhael Goavec-Merou 		return IIO_VAL_INT;
707f270bc9SGwenhael Goavec-Merou 	case IIO_CHAN_INFO_SCALE:
717f270bc9SGwenhael Goavec-Merou 		*val = state->vrefh_mv - state->vrefl_mv;
727f270bc9SGwenhael Goavec-Merou 		*val2 = 8;
737f270bc9SGwenhael Goavec-Merou 		return IIO_VAL_FRACTIONAL_LOG2;
747f270bc9SGwenhael Goavec-Merou 	case IIO_CHAN_INFO_OFFSET:
757f270bc9SGwenhael Goavec-Merou 		*val = state->vrefl_mv;
767f270bc9SGwenhael Goavec-Merou 		return IIO_VAL_INT;
777f270bc9SGwenhael Goavec-Merou 	default:
787f270bc9SGwenhael Goavec-Merou 		return -EINVAL;
797f270bc9SGwenhael Goavec-Merou 	}
807f270bc9SGwenhael Goavec-Merou 
817f270bc9SGwenhael Goavec-Merou 	return -EINVAL;
827f270bc9SGwenhael Goavec-Merou }
837f270bc9SGwenhael Goavec-Merou 
847f270bc9SGwenhael Goavec-Merou static const struct iio_info ad8801_info = {
857f270bc9SGwenhael Goavec-Merou 	.read_raw = ad8801_read_raw,
867f270bc9SGwenhael Goavec-Merou 	.write_raw = ad8801_write_raw,
877f270bc9SGwenhael Goavec-Merou };
887f270bc9SGwenhael Goavec-Merou 
897f270bc9SGwenhael Goavec-Merou #define AD8801_CHANNEL(chan) {		\
907f270bc9SGwenhael Goavec-Merou 	.type = IIO_VOLTAGE,			\
917f270bc9SGwenhael Goavec-Merou 	.indexed = 1,				\
927f270bc9SGwenhael Goavec-Merou 	.output = 1,				\
937f270bc9SGwenhael Goavec-Merou 	.channel = chan,			\
947f270bc9SGwenhael Goavec-Merou 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
957f270bc9SGwenhael Goavec-Merou 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\
967f270bc9SGwenhael Goavec-Merou 		BIT(IIO_CHAN_INFO_OFFSET), \
977f270bc9SGwenhael Goavec-Merou }
987f270bc9SGwenhael Goavec-Merou 
997f270bc9SGwenhael Goavec-Merou static const struct iio_chan_spec ad8801_channels[] = {
1007f270bc9SGwenhael Goavec-Merou 	AD8801_CHANNEL(0),
1017f270bc9SGwenhael Goavec-Merou 	AD8801_CHANNEL(1),
1027f270bc9SGwenhael Goavec-Merou 	AD8801_CHANNEL(2),
1037f270bc9SGwenhael Goavec-Merou 	AD8801_CHANNEL(3),
1047f270bc9SGwenhael Goavec-Merou 	AD8801_CHANNEL(4),
1057f270bc9SGwenhael Goavec-Merou 	AD8801_CHANNEL(5),
1067f270bc9SGwenhael Goavec-Merou 	AD8801_CHANNEL(6),
1077f270bc9SGwenhael Goavec-Merou 	AD8801_CHANNEL(7),
1087f270bc9SGwenhael Goavec-Merou };
1097f270bc9SGwenhael Goavec-Merou 
ad8801_probe(struct spi_device * spi)1107f270bc9SGwenhael Goavec-Merou static int ad8801_probe(struct spi_device *spi)
1117f270bc9SGwenhael Goavec-Merou {
1127f270bc9SGwenhael Goavec-Merou 	struct iio_dev *indio_dev;
1137f270bc9SGwenhael Goavec-Merou 	struct ad8801_state *state;
1147f270bc9SGwenhael Goavec-Merou 	const struct spi_device_id *id;
1157f270bc9SGwenhael Goavec-Merou 	int ret;
1167f270bc9SGwenhael Goavec-Merou 
1177f270bc9SGwenhael Goavec-Merou 	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
1187f270bc9SGwenhael Goavec-Merou 	if (indio_dev == NULL)
1197f270bc9SGwenhael Goavec-Merou 		return -ENOMEM;
1207f270bc9SGwenhael Goavec-Merou 
1217f270bc9SGwenhael Goavec-Merou 	state = iio_priv(indio_dev);
1227f270bc9SGwenhael Goavec-Merou 	state->spi = spi;
1237f270bc9SGwenhael Goavec-Merou 	id = spi_get_device_id(spi);
1247f270bc9SGwenhael Goavec-Merou 
1257f270bc9SGwenhael Goavec-Merou 	state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh");
126f80d6061SCai Huoqing 	if (IS_ERR(state->vrefh_reg))
127f80d6061SCai Huoqing 		return dev_err_probe(&spi->dev, PTR_ERR(state->vrefh_reg),
128f80d6061SCai Huoqing 				     "Vrefh regulator not specified\n");
1297f270bc9SGwenhael Goavec-Merou 
1307f270bc9SGwenhael Goavec-Merou 	ret = regulator_enable(state->vrefh_reg);
1317f270bc9SGwenhael Goavec-Merou 	if (ret) {
1327f270bc9SGwenhael Goavec-Merou 		dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n",
1337f270bc9SGwenhael Goavec-Merou 				ret);
1347f270bc9SGwenhael Goavec-Merou 		return ret;
1357f270bc9SGwenhael Goavec-Merou 	}
1367f270bc9SGwenhael Goavec-Merou 
1377f270bc9SGwenhael Goavec-Merou 	ret = regulator_get_voltage(state->vrefh_reg);
1387f270bc9SGwenhael Goavec-Merou 	if (ret < 0) {
1397f270bc9SGwenhael Goavec-Merou 		dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n",
1407f270bc9SGwenhael Goavec-Merou 				ret);
1417f270bc9SGwenhael Goavec-Merou 		goto error_disable_vrefh_reg;
1427f270bc9SGwenhael Goavec-Merou 	}
1437f270bc9SGwenhael Goavec-Merou 	state->vrefh_mv = ret / 1000;
1447f270bc9SGwenhael Goavec-Merou 
1457f270bc9SGwenhael Goavec-Merou 	if (id->driver_data == ID_AD8803) {
1467f270bc9SGwenhael Goavec-Merou 		state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl");
1477f270bc9SGwenhael Goavec-Merou 		if (IS_ERR(state->vrefl_reg)) {
148f80d6061SCai Huoqing 			ret = dev_err_probe(&spi->dev, PTR_ERR(state->vrefl_reg),
149f80d6061SCai Huoqing 					    "Vrefl regulator not specified\n");
1507f270bc9SGwenhael Goavec-Merou 			goto error_disable_vrefh_reg;
1517f270bc9SGwenhael Goavec-Merou 		}
1527f270bc9SGwenhael Goavec-Merou 
1537f270bc9SGwenhael Goavec-Merou 		ret = regulator_enable(state->vrefl_reg);
1547f270bc9SGwenhael Goavec-Merou 		if (ret) {
1557f270bc9SGwenhael Goavec-Merou 			dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n",
1567f270bc9SGwenhael Goavec-Merou 					ret);
1577f270bc9SGwenhael Goavec-Merou 			goto error_disable_vrefh_reg;
1587f270bc9SGwenhael Goavec-Merou 		}
1597f270bc9SGwenhael Goavec-Merou 
1607f270bc9SGwenhael Goavec-Merou 		ret = regulator_get_voltage(state->vrefl_reg);
1617f270bc9SGwenhael Goavec-Merou 		if (ret < 0) {
1627f270bc9SGwenhael Goavec-Merou 			dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n",
1637f270bc9SGwenhael Goavec-Merou 					ret);
1647f270bc9SGwenhael Goavec-Merou 			goto error_disable_vrefl_reg;
1657f270bc9SGwenhael Goavec-Merou 		}
1667f270bc9SGwenhael Goavec-Merou 		state->vrefl_mv = ret / 1000;
1677f270bc9SGwenhael Goavec-Merou 	} else {
1687f270bc9SGwenhael Goavec-Merou 		state->vrefl_mv = 0;
1697f270bc9SGwenhael Goavec-Merou 		state->vrefl_reg = NULL;
1707f270bc9SGwenhael Goavec-Merou 	}
1717f270bc9SGwenhael Goavec-Merou 
1727f270bc9SGwenhael Goavec-Merou 	spi_set_drvdata(spi, indio_dev);
1737f270bc9SGwenhael Goavec-Merou 	indio_dev->info = &ad8801_info;
1747f270bc9SGwenhael Goavec-Merou 	indio_dev->modes = INDIO_DIRECT_MODE;
1757f270bc9SGwenhael Goavec-Merou 	indio_dev->channels = ad8801_channels;
1767f270bc9SGwenhael Goavec-Merou 	indio_dev->num_channels = ARRAY_SIZE(ad8801_channels);
1777f270bc9SGwenhael Goavec-Merou 	indio_dev->name = id->name;
1787f270bc9SGwenhael Goavec-Merou 
1797f270bc9SGwenhael Goavec-Merou 	ret = iio_device_register(indio_dev);
1807f270bc9SGwenhael Goavec-Merou 	if (ret) {
1817f270bc9SGwenhael Goavec-Merou 		dev_err(&spi->dev, "Failed to register iio device: %d\n",
1827f270bc9SGwenhael Goavec-Merou 				ret);
1837f270bc9SGwenhael Goavec-Merou 		goto error_disable_vrefl_reg;
1847f270bc9SGwenhael Goavec-Merou 	}
1857f270bc9SGwenhael Goavec-Merou 
1867f270bc9SGwenhael Goavec-Merou 	return 0;
1877f270bc9SGwenhael Goavec-Merou 
1887f270bc9SGwenhael Goavec-Merou error_disable_vrefl_reg:
1897f270bc9SGwenhael Goavec-Merou 	if (state->vrefl_reg)
1907f270bc9SGwenhael Goavec-Merou 		regulator_disable(state->vrefl_reg);
1917f270bc9SGwenhael Goavec-Merou error_disable_vrefh_reg:
1927f270bc9SGwenhael Goavec-Merou 	regulator_disable(state->vrefh_reg);
1937f270bc9SGwenhael Goavec-Merou 	return ret;
1947f270bc9SGwenhael Goavec-Merou }
1957f270bc9SGwenhael Goavec-Merou 
ad8801_remove(struct spi_device * spi)196a0386bbaSUwe Kleine-König static void ad8801_remove(struct spi_device *spi)
1977f270bc9SGwenhael Goavec-Merou {
1987f270bc9SGwenhael Goavec-Merou 	struct iio_dev *indio_dev = spi_get_drvdata(spi);
1997f270bc9SGwenhael Goavec-Merou 	struct ad8801_state *state = iio_priv(indio_dev);
2007f270bc9SGwenhael Goavec-Merou 
2017f270bc9SGwenhael Goavec-Merou 	iio_device_unregister(indio_dev);
2027f270bc9SGwenhael Goavec-Merou 	if (state->vrefl_reg)
2037f270bc9SGwenhael Goavec-Merou 		regulator_disable(state->vrefl_reg);
2047f270bc9SGwenhael Goavec-Merou 	regulator_disable(state->vrefh_reg);
2057f270bc9SGwenhael Goavec-Merou }
2067f270bc9SGwenhael Goavec-Merou 
2077f270bc9SGwenhael Goavec-Merou static const struct spi_device_id ad8801_ids[] = {
2087f270bc9SGwenhael Goavec-Merou 	{"ad8801", ID_AD8801},
2097f270bc9SGwenhael Goavec-Merou 	{"ad8803", ID_AD8803},
2107f270bc9SGwenhael Goavec-Merou 	{}
2117f270bc9SGwenhael Goavec-Merou };
2127f270bc9SGwenhael Goavec-Merou MODULE_DEVICE_TABLE(spi, ad8801_ids);
2137f270bc9SGwenhael Goavec-Merou 
2147f270bc9SGwenhael Goavec-Merou static struct spi_driver ad8801_driver = {
2157f270bc9SGwenhael Goavec-Merou 	.driver = {
2167f270bc9SGwenhael Goavec-Merou 		.name	= "ad8801",
2177f270bc9SGwenhael Goavec-Merou 	},
2187f270bc9SGwenhael Goavec-Merou 	.probe		= ad8801_probe,
2197f270bc9SGwenhael Goavec-Merou 	.remove		= ad8801_remove,
2207f270bc9SGwenhael Goavec-Merou 	.id_table	= ad8801_ids,
2217f270bc9SGwenhael Goavec-Merou };
2227f270bc9SGwenhael Goavec-Merou module_spi_driver(ad8801_driver);
2237f270bc9SGwenhael Goavec-Merou 
2247f270bc9SGwenhael Goavec-Merou MODULE_AUTHOR("Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>");
2257f270bc9SGwenhael Goavec-Merou MODULE_DESCRIPTION("Analog Devices AD8801/AD8803 DAC");
2267f270bc9SGwenhael Goavec-Merou MODULE_LICENSE("GPL v2");
227