162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IIO DAC emulation driver using a digital potentiometer
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016 Axentia Technologies AB
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Peter Rosin <peda@axentia.se>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * It is assumed that the dpot is used as a voltage divider between the
1262306a36Sopenharmony_ci * current dpot wiper setting and the maximum resistance of the dpot. The
1362306a36Sopenharmony_ci * divided voltage is provided by a vref regulator.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *                   .------.
1662306a36Sopenharmony_ci *    .-----------.  |      |
1762306a36Sopenharmony_ci *    | vref      |--'    .---.
1862306a36Sopenharmony_ci *    | regulator |--.    |   |
1962306a36Sopenharmony_ci *    '-----------'  |    | d |
2062306a36Sopenharmony_ci *                   |    | p |
2162306a36Sopenharmony_ci *                   |    | o |  wiper
2262306a36Sopenharmony_ci *                   |    | t |<---------+
2362306a36Sopenharmony_ci *                   |    |   |
2462306a36Sopenharmony_ci *                   |    '---'       dac output voltage
2562306a36Sopenharmony_ci *                   |      |
2662306a36Sopenharmony_ci *                   '------+------------+
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <linux/err.h>
3062306a36Sopenharmony_ci#include <linux/iio/consumer.h>
3162306a36Sopenharmony_ci#include <linux/iio/iio.h>
3262306a36Sopenharmony_ci#include <linux/module.h>
3362306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
3462306a36Sopenharmony_ci#include <linux/platform_device.h>
3562306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct dpot_dac {
3862306a36Sopenharmony_ci	struct regulator *vref;
3962306a36Sopenharmony_ci	struct iio_channel *dpot;
4062306a36Sopenharmony_ci	u32 max_ohms;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic const struct iio_chan_spec dpot_dac_iio_channel = {
4462306a36Sopenharmony_ci	.type = IIO_VOLTAGE,
4562306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
4662306a36Sopenharmony_ci			    | BIT(IIO_CHAN_INFO_SCALE),
4762306a36Sopenharmony_ci	.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
4862306a36Sopenharmony_ci	.output = 1,
4962306a36Sopenharmony_ci	.indexed = 1,
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int dpot_dac_read_raw(struct iio_dev *indio_dev,
5362306a36Sopenharmony_ci			     struct iio_chan_spec const *chan,
5462306a36Sopenharmony_ci			     int *val, int *val2, long mask)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct dpot_dac *dac = iio_priv(indio_dev);
5762306a36Sopenharmony_ci	int ret;
5862306a36Sopenharmony_ci	unsigned long long tmp;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	switch (mask) {
6162306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
6262306a36Sopenharmony_ci		return iio_read_channel_raw(dac->dpot, val);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
6562306a36Sopenharmony_ci		ret = iio_read_channel_scale(dac->dpot, val, val2);
6662306a36Sopenharmony_ci		switch (ret) {
6762306a36Sopenharmony_ci		case IIO_VAL_FRACTIONAL_LOG2:
6862306a36Sopenharmony_ci			tmp = *val * 1000000000LL;
6962306a36Sopenharmony_ci			do_div(tmp, dac->max_ohms);
7062306a36Sopenharmony_ci			tmp *= regulator_get_voltage(dac->vref) / 1000;
7162306a36Sopenharmony_ci			do_div(tmp, 1000000000LL);
7262306a36Sopenharmony_ci			*val = tmp;
7362306a36Sopenharmony_ci			return ret;
7462306a36Sopenharmony_ci		case IIO_VAL_INT:
7562306a36Sopenharmony_ci			/*
7662306a36Sopenharmony_ci			 * Convert integer scale to fractional scale by
7762306a36Sopenharmony_ci			 * setting the denominator (val2) to one...
7862306a36Sopenharmony_ci			 */
7962306a36Sopenharmony_ci			*val2 = 1;
8062306a36Sopenharmony_ci			ret = IIO_VAL_FRACTIONAL;
8162306a36Sopenharmony_ci			/* ...and fall through. Say it again for GCC. */
8262306a36Sopenharmony_ci			fallthrough;
8362306a36Sopenharmony_ci		case IIO_VAL_FRACTIONAL:
8462306a36Sopenharmony_ci			*val *= regulator_get_voltage(dac->vref) / 1000;
8562306a36Sopenharmony_ci			*val2 *= dac->max_ohms;
8662306a36Sopenharmony_ci			break;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		return ret;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return -EINVAL;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int dpot_dac_read_avail(struct iio_dev *indio_dev,
9662306a36Sopenharmony_ci			       struct iio_chan_spec const *chan,
9762306a36Sopenharmony_ci			       const int **vals, int *type, int *length,
9862306a36Sopenharmony_ci			       long mask)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct dpot_dac *dac = iio_priv(indio_dev);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	switch (mask) {
10362306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
10462306a36Sopenharmony_ci		*type = IIO_VAL_INT;
10562306a36Sopenharmony_ci		return iio_read_avail_channel_raw(dac->dpot, vals, length);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return -EINVAL;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int dpot_dac_write_raw(struct iio_dev *indio_dev,
11262306a36Sopenharmony_ci			      struct iio_chan_spec const *chan,
11362306a36Sopenharmony_ci			      int val, int val2, long mask)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct dpot_dac *dac = iio_priv(indio_dev);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	switch (mask) {
11862306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
11962306a36Sopenharmony_ci		return iio_write_channel_raw(dac->dpot, val);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return -EINVAL;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct iio_info dpot_dac_info = {
12662306a36Sopenharmony_ci	.read_raw = dpot_dac_read_raw,
12762306a36Sopenharmony_ci	.read_avail = dpot_dac_read_avail,
12862306a36Sopenharmony_ci	.write_raw = dpot_dac_write_raw,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct device *dev = &indio_dev->dev;
13462306a36Sopenharmony_ci	struct dpot_dac *dac = iio_priv(indio_dev);
13562306a36Sopenharmony_ci	unsigned long long tmp;
13662306a36Sopenharmony_ci	int ret;
13762306a36Sopenharmony_ci	int val;
13862306a36Sopenharmony_ci	int val2;
13962306a36Sopenharmony_ci	int max;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	ret = iio_read_max_channel_raw(dac->dpot, &max);
14262306a36Sopenharmony_ci	if (ret < 0) {
14362306a36Sopenharmony_ci		dev_err(dev, "dpot does not indicate its raw maximum value\n");
14462306a36Sopenharmony_ci		return ret;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	switch (iio_read_channel_scale(dac->dpot, &val, &val2)) {
14862306a36Sopenharmony_ci	case IIO_VAL_INT:
14962306a36Sopenharmony_ci		return max * val;
15062306a36Sopenharmony_ci	case IIO_VAL_FRACTIONAL:
15162306a36Sopenharmony_ci		tmp = (unsigned long long)max * val;
15262306a36Sopenharmony_ci		do_div(tmp, val2);
15362306a36Sopenharmony_ci		return tmp;
15462306a36Sopenharmony_ci	case IIO_VAL_FRACTIONAL_LOG2:
15562306a36Sopenharmony_ci		tmp = val * 1000000000LL * max >> val2;
15662306a36Sopenharmony_ci		do_div(tmp, 1000000000LL);
15762306a36Sopenharmony_ci		return tmp;
15862306a36Sopenharmony_ci	default:
15962306a36Sopenharmony_ci		dev_err(dev, "dpot has a scale that is too weird\n");
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return -EINVAL;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int dpot_dac_probe(struct platform_device *pdev)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
16862306a36Sopenharmony_ci	struct iio_dev *indio_dev;
16962306a36Sopenharmony_ci	struct dpot_dac *dac;
17062306a36Sopenharmony_ci	enum iio_chan_type type;
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(dev, sizeof(*dac));
17462306a36Sopenharmony_ci	if (!indio_dev)
17562306a36Sopenharmony_ci		return -ENOMEM;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	platform_set_drvdata(pdev, indio_dev);
17862306a36Sopenharmony_ci	dac = iio_priv(indio_dev);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	indio_dev->name = dev_name(dev);
18162306a36Sopenharmony_ci	indio_dev->info = &dpot_dac_info;
18262306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
18362306a36Sopenharmony_ci	indio_dev->channels = &dpot_dac_iio_channel;
18462306a36Sopenharmony_ci	indio_dev->num_channels = 1;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	dac->vref = devm_regulator_get(dev, "vref");
18762306a36Sopenharmony_ci	if (IS_ERR(dac->vref))
18862306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(dac->vref),
18962306a36Sopenharmony_ci				     "failed to get vref regulator\n");
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	dac->dpot = devm_iio_channel_get(dev, "dpot");
19262306a36Sopenharmony_ci	if (IS_ERR(dac->dpot))
19362306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(dac->dpot),
19462306a36Sopenharmony_ci				     "failed to get dpot input channel\n");
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	ret = iio_get_channel_type(dac->dpot, &type);
19762306a36Sopenharmony_ci	if (ret < 0)
19862306a36Sopenharmony_ci		return ret;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (type != IIO_RESISTANCE) {
20162306a36Sopenharmony_ci		dev_err(dev, "dpot is of the wrong type\n");
20262306a36Sopenharmony_ci		return -EINVAL;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ret = dpot_dac_channel_max_ohms(indio_dev);
20662306a36Sopenharmony_ci	if (ret < 0)
20762306a36Sopenharmony_ci		return ret;
20862306a36Sopenharmony_ci	dac->max_ohms = ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ret = regulator_enable(dac->vref);
21162306a36Sopenharmony_ci	if (ret) {
21262306a36Sopenharmony_ci		dev_err(dev, "failed to enable the vref regulator\n");
21362306a36Sopenharmony_ci		return ret;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	ret = iio_device_register(indio_dev);
21762306a36Sopenharmony_ci	if (ret) {
21862306a36Sopenharmony_ci		dev_err(dev, "failed to register iio device\n");
21962306a36Sopenharmony_ci		goto disable_reg;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cidisable_reg:
22562306a36Sopenharmony_ci	regulator_disable(dac->vref);
22662306a36Sopenharmony_ci	return ret;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int dpot_dac_remove(struct platform_device *pdev)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
23262306a36Sopenharmony_ci	struct dpot_dac *dac = iio_priv(indio_dev);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
23562306a36Sopenharmony_ci	regulator_disable(dac->vref);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return 0;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic const struct of_device_id dpot_dac_match[] = {
24162306a36Sopenharmony_ci	{ .compatible = "dpot-dac" },
24262306a36Sopenharmony_ci	{ /* sentinel */ }
24362306a36Sopenharmony_ci};
24462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dpot_dac_match);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic struct platform_driver dpot_dac_driver = {
24762306a36Sopenharmony_ci	.probe = dpot_dac_probe,
24862306a36Sopenharmony_ci	.remove = dpot_dac_remove,
24962306a36Sopenharmony_ci	.driver = {
25062306a36Sopenharmony_ci		.name = "iio-dpot-dac",
25162306a36Sopenharmony_ci		.of_match_table = dpot_dac_match,
25262306a36Sopenharmony_ci	},
25362306a36Sopenharmony_ci};
25462306a36Sopenharmony_cimodule_platform_driver(dpot_dac_driver);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ciMODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
25762306a36Sopenharmony_ciMODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
25862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
259