18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * DAC7612 Dual, 12-Bit Serial input Digital-to-Analog Converter
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2019 Qtechnology A/S
68c2ecf20Sopenharmony_ci * 2019 Ricardo Ribalda <ribalda@kernel.org>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Licensed under the GPL-2.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
138c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
148c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define DAC7612_RESOLUTION 12
178c2ecf20Sopenharmony_ci#define DAC7612_ADDRESS 4
188c2ecf20Sopenharmony_ci#define DAC7612_START 5
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct dac7612 {
218c2ecf20Sopenharmony_ci	struct spi_device *spi;
228c2ecf20Sopenharmony_ci	struct gpio_desc *loaddacs;
238c2ecf20Sopenharmony_ci	uint16_t cache[2];
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	/*
268c2ecf20Sopenharmony_ci	 * Lock to protect the state of the device from potential concurrent
278c2ecf20Sopenharmony_ci	 * write accesses from userspace. The write operation requires an
288c2ecf20Sopenharmony_ci	 * SPI write, then toggling of a GPIO, so the lock aims to protect
298c2ecf20Sopenharmony_ci	 * the sanity of the entire sequence of operation.
308c2ecf20Sopenharmony_ci	 */
318c2ecf20Sopenharmony_ci	struct mutex lock;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/*
348c2ecf20Sopenharmony_ci	 * DMA (thus cache coherency maintenance) requires the
358c2ecf20Sopenharmony_ci	 * transfer buffers to live in their own cache lines.
368c2ecf20Sopenharmony_ci	 */
378c2ecf20Sopenharmony_ci	uint8_t data[2] ____cacheline_aligned;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int dac7612_cmd_single(struct dac7612 *priv, int channel, u16 val)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	int ret;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	priv->data[0] = BIT(DAC7612_START) | (channel << DAC7612_ADDRESS);
458c2ecf20Sopenharmony_ci	priv->data[0] |= val >> 8;
468c2ecf20Sopenharmony_ci	priv->data[1] = val & 0xff;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	priv->cache[channel] = val;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	ret = spi_write(priv->spi, priv->data, sizeof(priv->data));
518c2ecf20Sopenharmony_ci	if (ret)
528c2ecf20Sopenharmony_ci		return ret;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	gpiod_set_value(priv->loaddacs, 1);
558c2ecf20Sopenharmony_ci	gpiod_set_value(priv->loaddacs, 0);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define dac7612_CHANNEL(chan, name) {				\
618c2ecf20Sopenharmony_ci	.type = IIO_VOLTAGE,					\
628c2ecf20Sopenharmony_ci	.channel = (chan),					\
638c2ecf20Sopenharmony_ci	.indexed = 1,						\
648c2ecf20Sopenharmony_ci	.output = 1,						\
658c2ecf20Sopenharmony_ci	.datasheet_name = name,					\
668c2ecf20Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
678c2ecf20Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic const struct iio_chan_spec dac7612_channels[] = {
718c2ecf20Sopenharmony_ci	dac7612_CHANNEL(0, "OUTA"),
728c2ecf20Sopenharmony_ci	dac7612_CHANNEL(1, "OUTB"),
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int dac7612_read_raw(struct iio_dev *iio_dev,
768c2ecf20Sopenharmony_ci			    const struct iio_chan_spec *chan,
778c2ecf20Sopenharmony_ci			    int *val, int *val2, long mask)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct dac7612 *priv;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	switch (mask) {
828c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
838c2ecf20Sopenharmony_ci		priv = iio_priv(iio_dev);
848c2ecf20Sopenharmony_ci		*val = priv->cache[chan->channel];
858c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
888c2ecf20Sopenharmony_ci		*val = 1;
898c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	default:
928c2ecf20Sopenharmony_ci		return -EINVAL;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int dac7612_write_raw(struct iio_dev *iio_dev,
978c2ecf20Sopenharmony_ci			     const struct iio_chan_spec *chan,
988c2ecf20Sopenharmony_ci			     int val, int val2, long mask)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct dac7612 *priv = iio_priv(iio_dev);
1018c2ecf20Sopenharmony_ci	int ret;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (mask != IIO_CHAN_INFO_RAW)
1048c2ecf20Sopenharmony_ci		return -EINVAL;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if ((val >= BIT(DAC7612_RESOLUTION)) || val < 0 || val2)
1078c2ecf20Sopenharmony_ci		return -EINVAL;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (val == priv->cache[chan->channel])
1108c2ecf20Sopenharmony_ci		return 0;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
1138c2ecf20Sopenharmony_ci	ret = dac7612_cmd_single(priv, chan->channel, val);
1148c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return ret;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic const struct iio_info dac7612_info = {
1208c2ecf20Sopenharmony_ci	.read_raw = dac7612_read_raw,
1218c2ecf20Sopenharmony_ci	.write_raw = dac7612_write_raw,
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int dac7612_probe(struct spi_device *spi)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct iio_dev *iio_dev;
1278c2ecf20Sopenharmony_ci	struct dac7612 *priv;
1288c2ecf20Sopenharmony_ci	int i;
1298c2ecf20Sopenharmony_ci	int ret;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
1328c2ecf20Sopenharmony_ci	if (!iio_dev)
1338c2ecf20Sopenharmony_ci		return -ENOMEM;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	priv = iio_priv(iio_dev);
1368c2ecf20Sopenharmony_ci	/*
1378c2ecf20Sopenharmony_ci	 * LOADDACS pin can be controlled by the driver or externally.
1388c2ecf20Sopenharmony_ci	 * When controlled by the driver, the DAC value is updated after
1398c2ecf20Sopenharmony_ci	 * every write.
1408c2ecf20Sopenharmony_ci	 * When the driver does not control the PIN, the user or an external
1418c2ecf20Sopenharmony_ci	 * event can change the value of all DACs by pulsing down the LOADDACs
1428c2ecf20Sopenharmony_ci	 * pin.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci	priv->loaddacs = devm_gpiod_get_optional(&spi->dev, "ti,loaddacs",
1458c2ecf20Sopenharmony_ci						 GPIOD_OUT_LOW);
1468c2ecf20Sopenharmony_ci	if (IS_ERR(priv->loaddacs))
1478c2ecf20Sopenharmony_ci		return PTR_ERR(priv->loaddacs);
1488c2ecf20Sopenharmony_ci	priv->spi = spi;
1498c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, iio_dev);
1508c2ecf20Sopenharmony_ci	iio_dev->info = &dac7612_info;
1518c2ecf20Sopenharmony_ci	iio_dev->modes = INDIO_DIRECT_MODE;
1528c2ecf20Sopenharmony_ci	iio_dev->channels = dac7612_channels;
1538c2ecf20Sopenharmony_ci	iio_dev->num_channels = ARRAY_SIZE(priv->cache);
1548c2ecf20Sopenharmony_ci	iio_dev->name = spi_get_device_id(spi)->name;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	mutex_init(&priv->lock);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(priv->cache); i++) {
1598c2ecf20Sopenharmony_ci		ret = dac7612_cmd_single(priv, i, 0);
1608c2ecf20Sopenharmony_ci		if (ret)
1618c2ecf20Sopenharmony_ci			return ret;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return devm_iio_device_register(&spi->dev, iio_dev);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic const struct spi_device_id dac7612_id[] = {
1688c2ecf20Sopenharmony_ci	{"ti-dac7612"},
1698c2ecf20Sopenharmony_ci	{}
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, dac7612_id);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic const struct of_device_id dac7612_of_match[] = {
1748c2ecf20Sopenharmony_ci	{ .compatible = "ti,dac7612" },
1758c2ecf20Sopenharmony_ci	{ .compatible = "ti,dac7612u" },
1768c2ecf20Sopenharmony_ci	{ .compatible = "ti,dac7612ub" },
1778c2ecf20Sopenharmony_ci	{ },
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, dac7612_of_match);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic struct spi_driver dac7612_driver = {
1828c2ecf20Sopenharmony_ci	.driver = {
1838c2ecf20Sopenharmony_ci		   .name = "ti-dac7612",
1848c2ecf20Sopenharmony_ci		   .of_match_table = dac7612_of_match,
1858c2ecf20Sopenharmony_ci		   },
1868c2ecf20Sopenharmony_ci	.probe = dac7612_probe,
1878c2ecf20Sopenharmony_ci	.id_table = dac7612_id,
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_cimodule_spi_driver(dac7612_driver);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ricardo Ribalda <ribalda@kernel.org>");
1928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments DAC7612 DAC driver");
1938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
194