18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IIO driver for the Measurement Computing CIO-DAC
48c2ecf20Sopenharmony_ci * Copyright (C) 2016 William Breathitt Gray
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This driver supports the following Measurement Computing devices: CIO-DAC16,
78c2ecf20Sopenharmony_ci * CIO-DAC06, and PC104-DAC06.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/bitops.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
138c2ecf20Sopenharmony_ci#include <linux/iio/types.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/ioport.h>
168c2ecf20Sopenharmony_ci#include <linux/isa.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define CIO_DAC_NUM_CHAN 16
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define CIO_DAC_CHAN(chan) {				\
238c2ecf20Sopenharmony_ci	.type = IIO_VOLTAGE,				\
248c2ecf20Sopenharmony_ci	.channel = chan,				\
258c2ecf20Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
268c2ecf20Sopenharmony_ci	.indexed = 1,					\
278c2ecf20Sopenharmony_ci	.output = 1					\
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define CIO_DAC_EXTENT 32
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic unsigned int base[max_num_isa_dev(CIO_DAC_EXTENT)];
338c2ecf20Sopenharmony_cistatic unsigned int num_cio_dac;
348c2ecf20Sopenharmony_cimodule_param_hw_array(base, uint, ioport, &num_cio_dac, 0);
358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses");
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/**
388c2ecf20Sopenharmony_ci * struct cio_dac_iio - IIO device private data structure
398c2ecf20Sopenharmony_ci * @chan_out_states:	channels' output states
408c2ecf20Sopenharmony_ci * @base:		base port address of the IIO device
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_cistruct cio_dac_iio {
438c2ecf20Sopenharmony_ci	int chan_out_states[CIO_DAC_NUM_CHAN];
448c2ecf20Sopenharmony_ci	unsigned int base;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic int cio_dac_read_raw(struct iio_dev *indio_dev,
488c2ecf20Sopenharmony_ci	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct cio_dac_iio *const priv = iio_priv(indio_dev);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (mask != IIO_CHAN_INFO_RAW)
538c2ecf20Sopenharmony_ci		return -EINVAL;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	*val = priv->chan_out_states[chan->channel];
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return IIO_VAL_INT;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int cio_dac_write_raw(struct iio_dev *indio_dev,
618c2ecf20Sopenharmony_ci	struct iio_chan_spec const *chan, int val, int val2, long mask)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct cio_dac_iio *const priv = iio_priv(indio_dev);
648c2ecf20Sopenharmony_ci	const unsigned int chan_addr_offset = 2 * chan->channel;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (mask != IIO_CHAN_INFO_RAW)
678c2ecf20Sopenharmony_ci		return -EINVAL;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* DAC can only accept up to a 12-bit value */
708c2ecf20Sopenharmony_ci	if ((unsigned int)val > 4095)
718c2ecf20Sopenharmony_ci		return -EINVAL;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	priv->chan_out_states[chan->channel] = val;
748c2ecf20Sopenharmony_ci	outw(val, priv->base + chan_addr_offset);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic const struct iio_info cio_dac_info = {
808c2ecf20Sopenharmony_ci	.read_raw = cio_dac_read_raw,
818c2ecf20Sopenharmony_ci	.write_raw = cio_dac_write_raw
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic const struct iio_chan_spec cio_dac_channels[CIO_DAC_NUM_CHAN] = {
858c2ecf20Sopenharmony_ci	CIO_DAC_CHAN(0), CIO_DAC_CHAN(1), CIO_DAC_CHAN(2), CIO_DAC_CHAN(3),
868c2ecf20Sopenharmony_ci	CIO_DAC_CHAN(4), CIO_DAC_CHAN(5), CIO_DAC_CHAN(6), CIO_DAC_CHAN(7),
878c2ecf20Sopenharmony_ci	CIO_DAC_CHAN(8), CIO_DAC_CHAN(9), CIO_DAC_CHAN(10), CIO_DAC_CHAN(11),
888c2ecf20Sopenharmony_ci	CIO_DAC_CHAN(12), CIO_DAC_CHAN(13), CIO_DAC_CHAN(14), CIO_DAC_CHAN(15)
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int cio_dac_probe(struct device *dev, unsigned int id)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
948c2ecf20Sopenharmony_ci	struct cio_dac_iio *priv;
958c2ecf20Sopenharmony_ci	unsigned int i;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
988c2ecf20Sopenharmony_ci	if (!indio_dev)
998c2ecf20Sopenharmony_ci		return -ENOMEM;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (!devm_request_region(dev, base[id], CIO_DAC_EXTENT,
1028c2ecf20Sopenharmony_ci		dev_name(dev))) {
1038c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to request port addresses (0x%X-0x%X)\n",
1048c2ecf20Sopenharmony_ci			base[id], base[id] + CIO_DAC_EXTENT);
1058c2ecf20Sopenharmony_ci		return -EBUSY;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	indio_dev->info = &cio_dac_info;
1098c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
1108c2ecf20Sopenharmony_ci	indio_dev->channels = cio_dac_channels;
1118c2ecf20Sopenharmony_ci	indio_dev->num_channels = CIO_DAC_NUM_CHAN;
1128c2ecf20Sopenharmony_ci	indio_dev->name = dev_name(dev);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	priv = iio_priv(indio_dev);
1158c2ecf20Sopenharmony_ci	priv->base = base[id];
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* initialize DAC outputs to 0V */
1188c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i += 2)
1198c2ecf20Sopenharmony_ci		outw(0, base[id] + i);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return devm_iio_device_register(dev, indio_dev);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic struct isa_driver cio_dac_driver = {
1258c2ecf20Sopenharmony_ci	.probe = cio_dac_probe,
1268c2ecf20Sopenharmony_ci	.driver = {
1278c2ecf20Sopenharmony_ci		.name = "cio-dac"
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cimodule_isa_driver(cio_dac_driver, num_cio_dac);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ciMODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Measurement Computing CIO-DAC IIO driver");
1358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
136