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