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 297f270bc9SGwenhael Goavec-Merou __be16 data ____cacheline_aligned; 307f270bc9SGwenhael Goavec-Merou }; 317f270bc9SGwenhael Goavec-Merou 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 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 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 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"); 1267f270bc9SGwenhael Goavec-Merou if (IS_ERR(state->vrefh_reg)) { 1277f270bc9SGwenhael Goavec-Merou dev_err(&spi->dev, "Vrefh regulator not specified\n"); 1287f270bc9SGwenhael Goavec-Merou return PTR_ERR(state->vrefh_reg); 1297f270bc9SGwenhael Goavec-Merou } 1307f270bc9SGwenhael Goavec-Merou 1317f270bc9SGwenhael Goavec-Merou ret = regulator_enable(state->vrefh_reg); 1327f270bc9SGwenhael Goavec-Merou if (ret) { 1337f270bc9SGwenhael Goavec-Merou dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n", 1347f270bc9SGwenhael Goavec-Merou ret); 1357f270bc9SGwenhael Goavec-Merou return ret; 1367f270bc9SGwenhael Goavec-Merou } 1377f270bc9SGwenhael Goavec-Merou 1387f270bc9SGwenhael Goavec-Merou ret = regulator_get_voltage(state->vrefh_reg); 1397f270bc9SGwenhael Goavec-Merou if (ret < 0) { 1407f270bc9SGwenhael Goavec-Merou dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n", 1417f270bc9SGwenhael Goavec-Merou ret); 1427f270bc9SGwenhael Goavec-Merou goto error_disable_vrefh_reg; 1437f270bc9SGwenhael Goavec-Merou } 1447f270bc9SGwenhael Goavec-Merou state->vrefh_mv = ret / 1000; 1457f270bc9SGwenhael Goavec-Merou 1467f270bc9SGwenhael Goavec-Merou if (id->driver_data == ID_AD8803) { 1477f270bc9SGwenhael Goavec-Merou state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl"); 1487f270bc9SGwenhael Goavec-Merou if (IS_ERR(state->vrefl_reg)) { 1497f270bc9SGwenhael Goavec-Merou dev_err(&spi->dev, "Vrefl regulator not specified\n"); 1507f270bc9SGwenhael Goavec-Merou ret = PTR_ERR(state->vrefl_reg); 1517f270bc9SGwenhael Goavec-Merou goto error_disable_vrefh_reg; 1527f270bc9SGwenhael Goavec-Merou } 1537f270bc9SGwenhael Goavec-Merou 1547f270bc9SGwenhael Goavec-Merou ret = regulator_enable(state->vrefl_reg); 1557f270bc9SGwenhael Goavec-Merou if (ret) { 1567f270bc9SGwenhael Goavec-Merou dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n", 1577f270bc9SGwenhael Goavec-Merou ret); 1587f270bc9SGwenhael Goavec-Merou goto error_disable_vrefh_reg; 1597f270bc9SGwenhael Goavec-Merou } 1607f270bc9SGwenhael Goavec-Merou 1617f270bc9SGwenhael Goavec-Merou ret = regulator_get_voltage(state->vrefl_reg); 1627f270bc9SGwenhael Goavec-Merou if (ret < 0) { 1637f270bc9SGwenhael Goavec-Merou dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n", 1647f270bc9SGwenhael Goavec-Merou ret); 1657f270bc9SGwenhael Goavec-Merou goto error_disable_vrefl_reg; 1667f270bc9SGwenhael Goavec-Merou } 1677f270bc9SGwenhael Goavec-Merou state->vrefl_mv = ret / 1000; 1687f270bc9SGwenhael Goavec-Merou } else { 1697f270bc9SGwenhael Goavec-Merou state->vrefl_mv = 0; 1707f270bc9SGwenhael Goavec-Merou state->vrefl_reg = NULL; 1717f270bc9SGwenhael Goavec-Merou } 1727f270bc9SGwenhael Goavec-Merou 1737f270bc9SGwenhael Goavec-Merou spi_set_drvdata(spi, indio_dev); 1747f270bc9SGwenhael Goavec-Merou indio_dev->dev.parent = &spi->dev; 1757f270bc9SGwenhael Goavec-Merou indio_dev->info = &ad8801_info; 1767f270bc9SGwenhael Goavec-Merou indio_dev->modes = INDIO_DIRECT_MODE; 1777f270bc9SGwenhael Goavec-Merou indio_dev->channels = ad8801_channels; 1787f270bc9SGwenhael Goavec-Merou indio_dev->num_channels = ARRAY_SIZE(ad8801_channels); 1797f270bc9SGwenhael Goavec-Merou indio_dev->name = id->name; 1807f270bc9SGwenhael Goavec-Merou 1817f270bc9SGwenhael Goavec-Merou ret = iio_device_register(indio_dev); 1827f270bc9SGwenhael Goavec-Merou if (ret) { 1837f270bc9SGwenhael Goavec-Merou dev_err(&spi->dev, "Failed to register iio device: %d\n", 1847f270bc9SGwenhael Goavec-Merou ret); 1857f270bc9SGwenhael Goavec-Merou goto error_disable_vrefl_reg; 1867f270bc9SGwenhael Goavec-Merou } 1877f270bc9SGwenhael Goavec-Merou 1887f270bc9SGwenhael Goavec-Merou return 0; 1897f270bc9SGwenhael Goavec-Merou 1907f270bc9SGwenhael Goavec-Merou error_disable_vrefl_reg: 1917f270bc9SGwenhael Goavec-Merou if (state->vrefl_reg) 1927f270bc9SGwenhael Goavec-Merou regulator_disable(state->vrefl_reg); 1937f270bc9SGwenhael Goavec-Merou error_disable_vrefh_reg: 1947f270bc9SGwenhael Goavec-Merou regulator_disable(state->vrefh_reg); 1957f270bc9SGwenhael Goavec-Merou return ret; 1967f270bc9SGwenhael Goavec-Merou } 1977f270bc9SGwenhael Goavec-Merou 1987f270bc9SGwenhael Goavec-Merou static int ad8801_remove(struct spi_device *spi) 1997f270bc9SGwenhael Goavec-Merou { 2007f270bc9SGwenhael Goavec-Merou struct iio_dev *indio_dev = spi_get_drvdata(spi); 2017f270bc9SGwenhael Goavec-Merou struct ad8801_state *state = iio_priv(indio_dev); 2027f270bc9SGwenhael Goavec-Merou 2037f270bc9SGwenhael Goavec-Merou iio_device_unregister(indio_dev); 2047f270bc9SGwenhael Goavec-Merou if (state->vrefl_reg) 2057f270bc9SGwenhael Goavec-Merou regulator_disable(state->vrefl_reg); 2067f270bc9SGwenhael Goavec-Merou regulator_disable(state->vrefh_reg); 2077f270bc9SGwenhael Goavec-Merou 2087f270bc9SGwenhael Goavec-Merou return 0; 2097f270bc9SGwenhael Goavec-Merou } 2107f270bc9SGwenhael Goavec-Merou 2117f270bc9SGwenhael Goavec-Merou static const struct spi_device_id ad8801_ids[] = { 2127f270bc9SGwenhael Goavec-Merou {"ad8801", ID_AD8801}, 2137f270bc9SGwenhael Goavec-Merou {"ad8803", ID_AD8803}, 2147f270bc9SGwenhael Goavec-Merou {} 2157f270bc9SGwenhael Goavec-Merou }; 2167f270bc9SGwenhael Goavec-Merou MODULE_DEVICE_TABLE(spi, ad8801_ids); 2177f270bc9SGwenhael Goavec-Merou 2187f270bc9SGwenhael Goavec-Merou static struct spi_driver ad8801_driver = { 2197f270bc9SGwenhael Goavec-Merou .driver = { 2207f270bc9SGwenhael Goavec-Merou .name = "ad8801", 2217f270bc9SGwenhael Goavec-Merou }, 2227f270bc9SGwenhael Goavec-Merou .probe = ad8801_probe, 2237f270bc9SGwenhael Goavec-Merou .remove = ad8801_remove, 2247f270bc9SGwenhael Goavec-Merou .id_table = ad8801_ids, 2257f270bc9SGwenhael Goavec-Merou }; 2267f270bc9SGwenhael Goavec-Merou module_spi_driver(ad8801_driver); 2277f270bc9SGwenhael Goavec-Merou 2287f270bc9SGwenhael Goavec-Merou MODULE_AUTHOR("Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>"); 2297f270bc9SGwenhael Goavec-Merou MODULE_DESCRIPTION("Analog Devices AD8801/AD8803 DAC"); 2307f270bc9SGwenhael Goavec-Merou MODULE_LICENSE("GPL v2"); 231