xref: /openbmc/linux/drivers/iio/addac/stx104.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1955c2aa9SWilliam Breathitt Gray // SPDX-License-Identifier: GPL-2.0-only
2955c2aa9SWilliam Breathitt Gray /*
3955c2aa9SWilliam Breathitt Gray  * IIO driver for the Apex Embedded Systems STX104
4955c2aa9SWilliam Breathitt Gray  * Copyright (C) 2016 William Breathitt Gray
5955c2aa9SWilliam Breathitt Gray  */
6c7301b84SWilliam Breathitt Gray #include <linux/bitfield.h>
7aec463feSWilliam Breathitt Gray #include <linux/bits.h>
8955c2aa9SWilliam Breathitt Gray #include <linux/device.h>
9c7301b84SWilliam Breathitt Gray #include <linux/err.h>
10c7301b84SWilliam Breathitt Gray #include <linux/gpio/regmap.h>
11955c2aa9SWilliam Breathitt Gray #include <linux/iio/iio.h>
12955c2aa9SWilliam Breathitt Gray #include <linux/iio/types.h>
13955c2aa9SWilliam Breathitt Gray #include <linux/isa.h>
14955c2aa9SWilliam Breathitt Gray #include <linux/kernel.h>
1546a4cac7SWilliam Breathitt Gray #include <linux/limits.h>
16955c2aa9SWilliam Breathitt Gray #include <linux/module.h>
17955c2aa9SWilliam Breathitt Gray #include <linux/moduleparam.h>
1897408274SWilliam Breathitt Gray #include <linux/mutex.h>
19c7301b84SWilliam Breathitt Gray #include <linux/regmap.h>
20955c2aa9SWilliam Breathitt Gray #include <linux/types.h>
21955c2aa9SWilliam Breathitt Gray 
22955c2aa9SWilliam Breathitt Gray #define STX104_OUT_CHAN(chan) {				\
23955c2aa9SWilliam Breathitt Gray 	.type = IIO_VOLTAGE,				\
24955c2aa9SWilliam Breathitt Gray 	.channel = chan,				\
25955c2aa9SWilliam Breathitt Gray 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
26955c2aa9SWilliam Breathitt Gray 	.indexed = 1,					\
27955c2aa9SWilliam Breathitt Gray 	.output = 1					\
28955c2aa9SWilliam Breathitt Gray }
29955c2aa9SWilliam Breathitt Gray #define STX104_IN_CHAN(chan, diff) {					\
30955c2aa9SWilliam Breathitt Gray 	.type = IIO_VOLTAGE,						\
31955c2aa9SWilliam Breathitt Gray 	.channel = chan,						\
32955c2aa9SWilliam Breathitt Gray 	.channel2 = chan,						\
33955c2aa9SWilliam Breathitt Gray 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) |	\
34955c2aa9SWilliam Breathitt Gray 		BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),	\
35955c2aa9SWilliam Breathitt Gray 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
36955c2aa9SWilliam Breathitt Gray 	.indexed = 1,							\
37955c2aa9SWilliam Breathitt Gray 	.differential = diff						\
38955c2aa9SWilliam Breathitt Gray }
39955c2aa9SWilliam Breathitt Gray 
40955c2aa9SWilliam Breathitt Gray #define STX104_NUM_OUT_CHAN 2
41955c2aa9SWilliam Breathitt Gray 
42955c2aa9SWilliam Breathitt Gray #define STX104_EXTENT 16
43955c2aa9SWilliam Breathitt Gray 
44955c2aa9SWilliam Breathitt Gray static unsigned int base[max_num_isa_dev(STX104_EXTENT)];
45955c2aa9SWilliam Breathitt Gray static unsigned int num_stx104;
46955c2aa9SWilliam Breathitt Gray module_param_hw_array(base, uint, ioport, &num_stx104, 0);
47955c2aa9SWilliam Breathitt Gray MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
48955c2aa9SWilliam Breathitt Gray 
49c7301b84SWilliam Breathitt Gray #define STX104_AIO_BASE 0x0
50c7301b84SWilliam Breathitt Gray #define STX104_SOFTWARE_STROBE STX104_AIO_BASE
51c7301b84SWilliam Breathitt Gray #define STX104_ADC_DATA STX104_AIO_BASE
52c7301b84SWilliam Breathitt Gray #define STX104_ADC_CHANNEL (STX104_AIO_BASE + 0x2)
53c7301b84SWilliam Breathitt Gray #define STX104_DIO_REG (STX104_AIO_BASE + 0x3)
54c7301b84SWilliam Breathitt Gray #define STX104_DAC_BASE (STX104_AIO_BASE + 0x4)
55c7301b84SWilliam Breathitt Gray #define STX104_ADC_STATUS (STX104_AIO_BASE + 0x8)
56c7301b84SWilliam Breathitt Gray #define STX104_ADC_CONTROL (STX104_AIO_BASE + 0x9)
57c7301b84SWilliam Breathitt Gray #define STX104_ADC_CONFIGURATION (STX104_AIO_BASE + 0x11)
58c7301b84SWilliam Breathitt Gray 
59c7301b84SWilliam Breathitt Gray #define STX104_AIO_DATA_STRIDE 2
60c7301b84SWilliam Breathitt Gray #define STX104_DAC_OFFSET(_channel) (STX104_DAC_BASE + STX104_AIO_DATA_STRIDE * (_channel))
61c7301b84SWilliam Breathitt Gray 
62c7301b84SWilliam Breathitt Gray /* ADC Channel */
63c7301b84SWilliam Breathitt Gray #define STX104_FC GENMASK(3, 0)
64c7301b84SWilliam Breathitt Gray #define STX104_LC GENMASK(7, 4)
65c7301b84SWilliam Breathitt Gray #define STX104_SINGLE_CHANNEL(_channel) \
66c7301b84SWilliam Breathitt Gray 	(u8_encode_bits(_channel, STX104_FC) | u8_encode_bits(_channel, STX104_LC))
67c7301b84SWilliam Breathitt Gray 
68c7301b84SWilliam Breathitt Gray /* ADC Status */
69c7301b84SWilliam Breathitt Gray #define STX104_SD BIT(5)
70c7301b84SWilliam Breathitt Gray #define STX104_CNV BIT(7)
71c7301b84SWilliam Breathitt Gray #define STX104_DIFFERENTIAL 1
72c7301b84SWilliam Breathitt Gray 
73c7301b84SWilliam Breathitt Gray /* ADC Control */
74c7301b84SWilliam Breathitt Gray #define STX104_ALSS GENMASK(1, 0)
75c7301b84SWilliam Breathitt Gray #define STX104_SOFTWARE_TRIGGER u8_encode_bits(0x0, STX104_ALSS)
76c7301b84SWilliam Breathitt Gray 
77c7301b84SWilliam Breathitt Gray /* ADC Configuration */
78c7301b84SWilliam Breathitt Gray #define STX104_GAIN GENMASK(1, 0)
79c7301b84SWilliam Breathitt Gray #define STX104_ADBU BIT(2)
80c7301b84SWilliam Breathitt Gray #define STX104_BIPOLAR 0
81c7301b84SWilliam Breathitt Gray #define STX104_GAIN_X1 0
82c7301b84SWilliam Breathitt Gray #define STX104_GAIN_X2 1
83c7301b84SWilliam Breathitt Gray #define STX104_GAIN_X4 2
84c7301b84SWilliam Breathitt Gray #define STX104_GAIN_X8 3
85955c2aa9SWilliam Breathitt Gray 
86955c2aa9SWilliam Breathitt Gray /**
87955c2aa9SWilliam Breathitt Gray  * struct stx104_iio - IIO device private data structure
8897408274SWilliam Breathitt Gray  * @lock: synchronization lock to prevent I/O race conditions
89c7301b84SWilliam Breathitt Gray  * @aio_data_map: Regmap for analog I/O data
90c7301b84SWilliam Breathitt Gray  * @aio_ctl_map: Regmap for analog I/O control
91955c2aa9SWilliam Breathitt Gray  */
92955c2aa9SWilliam Breathitt Gray struct stx104_iio {
9397408274SWilliam Breathitt Gray 	struct mutex lock;
94c7301b84SWilliam Breathitt Gray 	struct regmap *aio_data_map;
95c7301b84SWilliam Breathitt Gray 	struct regmap *aio_ctl_map;
96955c2aa9SWilliam Breathitt Gray };
97955c2aa9SWilliam Breathitt Gray 
98c7301b84SWilliam Breathitt Gray static const struct regmap_range aio_ctl_wr_ranges[] = {
99c7301b84SWilliam Breathitt Gray 	regmap_reg_range(0x0, 0x0), regmap_reg_range(0x2, 0x2), regmap_reg_range(0x9, 0x9),
100c7301b84SWilliam Breathitt Gray 	regmap_reg_range(0x11, 0x11),
101c7301b84SWilliam Breathitt Gray };
102c7301b84SWilliam Breathitt Gray static const struct regmap_range aio_ctl_rd_ranges[] = {
103c7301b84SWilliam Breathitt Gray 	regmap_reg_range(0x2, 0x2), regmap_reg_range(0x8, 0x9), regmap_reg_range(0x11, 0x11),
104c7301b84SWilliam Breathitt Gray };
105c7301b84SWilliam Breathitt Gray static const struct regmap_range aio_ctl_volatile_ranges[] = {
106c7301b84SWilliam Breathitt Gray 	regmap_reg_range(0x8, 0x8),
107c7301b84SWilliam Breathitt Gray };
108c7301b84SWilliam Breathitt Gray static const struct regmap_access_table aio_ctl_wr_table = {
109c7301b84SWilliam Breathitt Gray 	.yes_ranges = aio_ctl_wr_ranges,
110c7301b84SWilliam Breathitt Gray 	.n_yes_ranges = ARRAY_SIZE(aio_ctl_wr_ranges),
111c7301b84SWilliam Breathitt Gray };
112c7301b84SWilliam Breathitt Gray static const struct regmap_access_table aio_ctl_rd_table = {
113c7301b84SWilliam Breathitt Gray 	.yes_ranges = aio_ctl_rd_ranges,
114c7301b84SWilliam Breathitt Gray 	.n_yes_ranges = ARRAY_SIZE(aio_ctl_rd_ranges),
115c7301b84SWilliam Breathitt Gray };
116c7301b84SWilliam Breathitt Gray static const struct regmap_access_table aio_ctl_volatile_table = {
117c7301b84SWilliam Breathitt Gray 	.yes_ranges = aio_ctl_volatile_ranges,
118c7301b84SWilliam Breathitt Gray 	.n_yes_ranges = ARRAY_SIZE(aio_ctl_volatile_ranges),
119c7301b84SWilliam Breathitt Gray };
120c7301b84SWilliam Breathitt Gray 
121c7301b84SWilliam Breathitt Gray static const struct regmap_config aio_ctl_regmap_config = {
122c7301b84SWilliam Breathitt Gray 	.name = "aio_ctl",
123c7301b84SWilliam Breathitt Gray 	.reg_bits = 8,
124c7301b84SWilliam Breathitt Gray 	.reg_stride = 1,
125c7301b84SWilliam Breathitt Gray 	.reg_base = STX104_AIO_BASE,
126c7301b84SWilliam Breathitt Gray 	.val_bits = 8,
127c7301b84SWilliam Breathitt Gray 	.io_port = true,
128c7301b84SWilliam Breathitt Gray 	.wr_table = &aio_ctl_wr_table,
129c7301b84SWilliam Breathitt Gray 	.rd_table = &aio_ctl_rd_table,
130c7301b84SWilliam Breathitt Gray 	.volatile_table = &aio_ctl_volatile_table,
131c7301b84SWilliam Breathitt Gray 	.cache_type = REGCACHE_FLAT,
132c7301b84SWilliam Breathitt Gray };
133c7301b84SWilliam Breathitt Gray 
134c7301b84SWilliam Breathitt Gray static const struct regmap_range aio_data_wr_ranges[] = {
135c7301b84SWilliam Breathitt Gray 	regmap_reg_range(0x4, 0x6),
136c7301b84SWilliam Breathitt Gray };
137c7301b84SWilliam Breathitt Gray static const struct regmap_range aio_data_rd_ranges[] = {
138c7301b84SWilliam Breathitt Gray 	regmap_reg_range(0x0, 0x0),
139c7301b84SWilliam Breathitt Gray };
140c7301b84SWilliam Breathitt Gray static const struct regmap_access_table aio_data_wr_table = {
141c7301b84SWilliam Breathitt Gray 	.yes_ranges = aio_data_wr_ranges,
142c7301b84SWilliam Breathitt Gray 	.n_yes_ranges = ARRAY_SIZE(aio_data_wr_ranges),
143c7301b84SWilliam Breathitt Gray };
144c7301b84SWilliam Breathitt Gray static const struct regmap_access_table aio_data_rd_table = {
145c7301b84SWilliam Breathitt Gray 	.yes_ranges = aio_data_rd_ranges,
146c7301b84SWilliam Breathitt Gray 	.n_yes_ranges = ARRAY_SIZE(aio_data_rd_ranges),
147c7301b84SWilliam Breathitt Gray };
148c7301b84SWilliam Breathitt Gray 
149c7301b84SWilliam Breathitt Gray static const struct regmap_config aio_data_regmap_config = {
150c7301b84SWilliam Breathitt Gray 	.name = "aio_data",
151c7301b84SWilliam Breathitt Gray 	.reg_bits = 16,
152c7301b84SWilliam Breathitt Gray 	.reg_stride = STX104_AIO_DATA_STRIDE,
153c7301b84SWilliam Breathitt Gray 	.reg_base = STX104_AIO_BASE,
154c7301b84SWilliam Breathitt Gray 	.val_bits = 16,
155c7301b84SWilliam Breathitt Gray 	.io_port = true,
156c7301b84SWilliam Breathitt Gray 	.wr_table = &aio_data_wr_table,
157c7301b84SWilliam Breathitt Gray 	.rd_table = &aio_data_rd_table,
158c7301b84SWilliam Breathitt Gray 	.volatile_table = &aio_data_rd_table,
159c7301b84SWilliam Breathitt Gray 	.cache_type = REGCACHE_FLAT,
160c7301b84SWilliam Breathitt Gray };
161c7301b84SWilliam Breathitt Gray 
162c7301b84SWilliam Breathitt Gray static const struct regmap_config dio_regmap_config = {
163c7301b84SWilliam Breathitt Gray 	.name = "dio",
164c7301b84SWilliam Breathitt Gray 	.reg_bits = 8,
165c7301b84SWilliam Breathitt Gray 	.reg_stride = 1,
166c7301b84SWilliam Breathitt Gray 	.reg_base = STX104_DIO_REG,
167c7301b84SWilliam Breathitt Gray 	.val_bits = 8,
168c7301b84SWilliam Breathitt Gray 	.io_port = true,
169955c2aa9SWilliam Breathitt Gray };
170955c2aa9SWilliam Breathitt Gray 
stx104_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)171955c2aa9SWilliam Breathitt Gray static int stx104_read_raw(struct iio_dev *indio_dev,
172955c2aa9SWilliam Breathitt Gray 	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
173955c2aa9SWilliam Breathitt Gray {
174955c2aa9SWilliam Breathitt Gray 	struct stx104_iio *const priv = iio_priv(indio_dev);
175c7301b84SWilliam Breathitt Gray 	int err;
176955c2aa9SWilliam Breathitt Gray 	unsigned int adc_config;
177c7301b84SWilliam Breathitt Gray 	unsigned int value;
178c7301b84SWilliam Breathitt Gray 	unsigned int adc_status;
179955c2aa9SWilliam Breathitt Gray 
180955c2aa9SWilliam Breathitt Gray 	switch (mask) {
181955c2aa9SWilliam Breathitt Gray 	case IIO_CHAN_INFO_HARDWAREGAIN:
182c7301b84SWilliam Breathitt Gray 		err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config);
183c7301b84SWilliam Breathitt Gray 		if (err)
184c7301b84SWilliam Breathitt Gray 			return err;
185955c2aa9SWilliam Breathitt Gray 
186c7301b84SWilliam Breathitt Gray 		*val = BIT(u8_get_bits(adc_config, STX104_GAIN));
187955c2aa9SWilliam Breathitt Gray 		return IIO_VAL_INT;
188955c2aa9SWilliam Breathitt Gray 	case IIO_CHAN_INFO_RAW:
189955c2aa9SWilliam Breathitt Gray 		if (chan->output) {
190c7301b84SWilliam Breathitt Gray 			err = regmap_read(priv->aio_data_map, STX104_DAC_OFFSET(chan->channel),
191c7301b84SWilliam Breathitt Gray 					  &value);
192c7301b84SWilliam Breathitt Gray 			if (err)
193c7301b84SWilliam Breathitt Gray 				return err;
194c7301b84SWilliam Breathitt Gray 			*val = value;
195955c2aa9SWilliam Breathitt Gray 			return IIO_VAL_INT;
196955c2aa9SWilliam Breathitt Gray 		}
197955c2aa9SWilliam Breathitt Gray 
1984f9b80aeSWilliam Breathitt Gray 		mutex_lock(&priv->lock);
1994f9b80aeSWilliam Breathitt Gray 
200955c2aa9SWilliam Breathitt Gray 		/* select ADC channel */
201c7301b84SWilliam Breathitt Gray 		err = regmap_write(priv->aio_ctl_map, STX104_ADC_CHANNEL,
202c7301b84SWilliam Breathitt Gray 				   STX104_SINGLE_CHANNEL(chan->channel));
203c7301b84SWilliam Breathitt Gray 		if (err) {
204c7301b84SWilliam Breathitt Gray 			mutex_unlock(&priv->lock);
205c7301b84SWilliam Breathitt Gray 			return err;
206c7301b84SWilliam Breathitt Gray 		}
207955c2aa9SWilliam Breathitt Gray 
208*7c95a3f5SWilliam Breathitt Gray 		/*
209*7c95a3f5SWilliam Breathitt Gray 		 * Trigger ADC sample capture by writing to the 8-bit Software Strobe Register and
210*7c95a3f5SWilliam Breathitt Gray 		 * wait for completion; the conversion time range is 5 microseconds to 53.68 seconds
211*7c95a3f5SWilliam Breathitt Gray 		 * in steps of 25 nanoseconds. The actual Analog Input Frame Timer time interval is
212*7c95a3f5SWilliam Breathitt Gray 		 * calculated as:
213*7c95a3f5SWilliam Breathitt Gray 		 * ai_time_frame_ns = ( AIFT + 1 ) * ( 25 nanoseconds ).
214*7c95a3f5SWilliam Breathitt Gray 		 * Where 0 <= AIFT <= 2147483648.
215955c2aa9SWilliam Breathitt Gray 		 */
216c7301b84SWilliam Breathitt Gray 		err = regmap_write(priv->aio_ctl_map, STX104_SOFTWARE_STROBE, 0);
217c7301b84SWilliam Breathitt Gray 		if (err) {
218c7301b84SWilliam Breathitt Gray 			mutex_unlock(&priv->lock);
219c7301b84SWilliam Breathitt Gray 			return err;
220c7301b84SWilliam Breathitt Gray 		}
221*7c95a3f5SWilliam Breathitt Gray 		err = regmap_read_poll_timeout(priv->aio_ctl_map, STX104_ADC_STATUS, adc_status,
222*7c95a3f5SWilliam Breathitt Gray 					       !u8_get_bits(adc_status, STX104_CNV), 0, 53687092);
223c7301b84SWilliam Breathitt Gray 		if (err) {
224c7301b84SWilliam Breathitt Gray 			mutex_unlock(&priv->lock);
225c7301b84SWilliam Breathitt Gray 			return err;
226c7301b84SWilliam Breathitt Gray 		}
227955c2aa9SWilliam Breathitt Gray 
228c7301b84SWilliam Breathitt Gray 		err = regmap_read(priv->aio_data_map, STX104_ADC_DATA, &value);
229c7301b84SWilliam Breathitt Gray 		if (err) {
230c7301b84SWilliam Breathitt Gray 			mutex_unlock(&priv->lock);
231c7301b84SWilliam Breathitt Gray 			return err;
232c7301b84SWilliam Breathitt Gray 		}
233c7301b84SWilliam Breathitt Gray 		*val = value;
2344f9b80aeSWilliam Breathitt Gray 
2354f9b80aeSWilliam Breathitt Gray 		mutex_unlock(&priv->lock);
236955c2aa9SWilliam Breathitt Gray 		return IIO_VAL_INT;
237955c2aa9SWilliam Breathitt Gray 	case IIO_CHAN_INFO_OFFSET:
238955c2aa9SWilliam Breathitt Gray 		/* get ADC bipolar/unipolar configuration */
239c7301b84SWilliam Breathitt Gray 		err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config);
240c7301b84SWilliam Breathitt Gray 		if (err)
241c7301b84SWilliam Breathitt Gray 			return err;
242955c2aa9SWilliam Breathitt Gray 
243c7301b84SWilliam Breathitt Gray 		*val = (u8_get_bits(adc_config, STX104_ADBU) == STX104_BIPOLAR) ? -32768 : 0;
244955c2aa9SWilliam Breathitt Gray 		return IIO_VAL_INT;
245955c2aa9SWilliam Breathitt Gray 	case IIO_CHAN_INFO_SCALE:
246955c2aa9SWilliam Breathitt Gray 		/* get ADC bipolar/unipolar and gain configuration */
247c7301b84SWilliam Breathitt Gray 		err = regmap_read(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, &adc_config);
248c7301b84SWilliam Breathitt Gray 		if (err)
249c7301b84SWilliam Breathitt Gray 			return err;
250955c2aa9SWilliam Breathitt Gray 
251955c2aa9SWilliam Breathitt Gray 		*val = 5;
252c7301b84SWilliam Breathitt Gray 		*val2 = (u8_get_bits(adc_config, STX104_ADBU) == STX104_BIPOLAR) ? 14 : 15;
253c7301b84SWilliam Breathitt Gray 		*val2 += u8_get_bits(adc_config, STX104_GAIN);
254955c2aa9SWilliam Breathitt Gray 		return IIO_VAL_FRACTIONAL_LOG2;
255955c2aa9SWilliam Breathitt Gray 	}
256955c2aa9SWilliam Breathitt Gray 
257955c2aa9SWilliam Breathitt Gray 	return -EINVAL;
258955c2aa9SWilliam Breathitt Gray }
259955c2aa9SWilliam Breathitt Gray 
stx104_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)260955c2aa9SWilliam Breathitt Gray static int stx104_write_raw(struct iio_dev *indio_dev,
261955c2aa9SWilliam Breathitt Gray 	struct iio_chan_spec const *chan, int val, int val2, long mask)
262955c2aa9SWilliam Breathitt Gray {
263955c2aa9SWilliam Breathitt Gray 	struct stx104_iio *const priv = iio_priv(indio_dev);
264c7301b84SWilliam Breathitt Gray 	u8 gain;
265955c2aa9SWilliam Breathitt Gray 
266955c2aa9SWilliam Breathitt Gray 	switch (mask) {
267955c2aa9SWilliam Breathitt Gray 	case IIO_CHAN_INFO_HARDWAREGAIN:
268955c2aa9SWilliam Breathitt Gray 		/* Only four gain states (x1, x2, x4, x8) */
269955c2aa9SWilliam Breathitt Gray 		switch (val) {
270955c2aa9SWilliam Breathitt Gray 		case 1:
271c7301b84SWilliam Breathitt Gray 			gain = STX104_GAIN_X1;
272955c2aa9SWilliam Breathitt Gray 			break;
273955c2aa9SWilliam Breathitt Gray 		case 2:
274c7301b84SWilliam Breathitt Gray 			gain = STX104_GAIN_X2;
275955c2aa9SWilliam Breathitt Gray 			break;
276955c2aa9SWilliam Breathitt Gray 		case 4:
277c7301b84SWilliam Breathitt Gray 			gain = STX104_GAIN_X4;
278955c2aa9SWilliam Breathitt Gray 			break;
279955c2aa9SWilliam Breathitt Gray 		case 8:
280c7301b84SWilliam Breathitt Gray 			gain = STX104_GAIN_X8;
281955c2aa9SWilliam Breathitt Gray 			break;
282955c2aa9SWilliam Breathitt Gray 		default:
283955c2aa9SWilliam Breathitt Gray 			return -EINVAL;
284955c2aa9SWilliam Breathitt Gray 		}
285955c2aa9SWilliam Breathitt Gray 
286c7301b84SWilliam Breathitt Gray 		return regmap_write(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, gain);
287955c2aa9SWilliam Breathitt Gray 	case IIO_CHAN_INFO_RAW:
288a94abc74SWilliam Breathitt Gray 		if (!chan->output)
289a94abc74SWilliam Breathitt Gray 			return -EINVAL;
290a94abc74SWilliam Breathitt Gray 
29146a4cac7SWilliam Breathitt Gray 		if (val < 0 || val > U16_MAX)
292955c2aa9SWilliam Breathitt Gray 			return -EINVAL;
293955c2aa9SWilliam Breathitt Gray 
294c7301b84SWilliam Breathitt Gray 		return regmap_write(priv->aio_data_map, STX104_DAC_OFFSET(chan->channel), val);
295955c2aa9SWilliam Breathitt Gray 	}
296955c2aa9SWilliam Breathitt Gray 
297955c2aa9SWilliam Breathitt Gray 	return -EINVAL;
298955c2aa9SWilliam Breathitt Gray }
299955c2aa9SWilliam Breathitt Gray 
300955c2aa9SWilliam Breathitt Gray static const struct iio_info stx104_info = {
301955c2aa9SWilliam Breathitt Gray 	.read_raw = stx104_read_raw,
302955c2aa9SWilliam Breathitt Gray 	.write_raw = stx104_write_raw
303955c2aa9SWilliam Breathitt Gray };
304955c2aa9SWilliam Breathitt Gray 
305955c2aa9SWilliam Breathitt Gray /* single-ended input channels configuration */
306955c2aa9SWilliam Breathitt Gray static const struct iio_chan_spec stx104_channels_sing[] = {
307955c2aa9SWilliam Breathitt Gray 	STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
308955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0),
309955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0),
310955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0),
311955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0),
312955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0),
313955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(15, 0)
314955c2aa9SWilliam Breathitt Gray };
315955c2aa9SWilliam Breathitt Gray /* differential input channels configuration */
316955c2aa9SWilliam Breathitt Gray static const struct iio_chan_spec stx104_channels_diff[] = {
317955c2aa9SWilliam Breathitt Gray 	STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
318955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1),
319955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1),
320955c2aa9SWilliam Breathitt Gray 	STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
321955c2aa9SWilliam Breathitt Gray };
322955c2aa9SWilliam Breathitt Gray 
stx104_reg_mask_xlate(struct gpio_regmap * const gpio,const unsigned int base,unsigned int offset,unsigned int * const reg,unsigned int * const mask)323c7301b84SWilliam Breathitt Gray static int stx104_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
324c7301b84SWilliam Breathitt Gray 				 unsigned int offset, unsigned int *const reg,
325c7301b84SWilliam Breathitt Gray 				 unsigned int *const mask)
326955c2aa9SWilliam Breathitt Gray {
327c7301b84SWilliam Breathitt Gray 	/* Output lines are located at same register bit offsets as input lines */
328955c2aa9SWilliam Breathitt Gray 	if (offset >= 4)
329c7301b84SWilliam Breathitt Gray 		offset -= 4;
330c7301b84SWilliam Breathitt Gray 
331c7301b84SWilliam Breathitt Gray 	*reg = base;
332c7301b84SWilliam Breathitt Gray 	*mask = BIT(offset);
333955c2aa9SWilliam Breathitt Gray 
334955c2aa9SWilliam Breathitt Gray 	return 0;
335955c2aa9SWilliam Breathitt Gray }
336955c2aa9SWilliam Breathitt Gray 
337955c2aa9SWilliam Breathitt Gray #define STX104_NGPIO 8
338955c2aa9SWilliam Breathitt Gray static const char *stx104_names[STX104_NGPIO] = {
339955c2aa9SWilliam Breathitt Gray 	"DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3"
340955c2aa9SWilliam Breathitt Gray };
341955c2aa9SWilliam Breathitt Gray 
stx104_init_hw(struct stx104_iio * const priv)342c7301b84SWilliam Breathitt Gray static int stx104_init_hw(struct stx104_iio *const priv)
343955c2aa9SWilliam Breathitt Gray {
344c7301b84SWilliam Breathitt Gray 	int err;
345955c2aa9SWilliam Breathitt Gray 
346c7301b84SWilliam Breathitt Gray 	/* configure device for software trigger operation */
347c7301b84SWilliam Breathitt Gray 	err = regmap_write(priv->aio_ctl_map, STX104_ADC_CONTROL, STX104_SOFTWARE_TRIGGER);
348c7301b84SWilliam Breathitt Gray 	if (err)
349c7301b84SWilliam Breathitt Gray 		return err;
350955c2aa9SWilliam Breathitt Gray 
351c7301b84SWilliam Breathitt Gray 	/* initialize gain setting to x1 */
352c7301b84SWilliam Breathitt Gray 	err = regmap_write(priv->aio_ctl_map, STX104_ADC_CONFIGURATION, STX104_GAIN_X1);
353c7301b84SWilliam Breathitt Gray 	if (err)
354c7301b84SWilliam Breathitt Gray 		return err;
355955c2aa9SWilliam Breathitt Gray 
356c7301b84SWilliam Breathitt Gray 	/* initialize DAC outputs to 0V */
357c7301b84SWilliam Breathitt Gray 	err = regmap_write(priv->aio_data_map, STX104_DAC_BASE, 0);
358c7301b84SWilliam Breathitt Gray 	if (err)
359c7301b84SWilliam Breathitt Gray 		return err;
360c7301b84SWilliam Breathitt Gray 	err = regmap_write(priv->aio_data_map, STX104_DAC_BASE + STX104_AIO_DATA_STRIDE, 0);
361c7301b84SWilliam Breathitt Gray 	if (err)
362c7301b84SWilliam Breathitt Gray 		return err;
363955c2aa9SWilliam Breathitt Gray 
364c7301b84SWilliam Breathitt Gray 	return 0;
365955c2aa9SWilliam Breathitt Gray }
366955c2aa9SWilliam Breathitt Gray 
stx104_probe(struct device * dev,unsigned int id)367955c2aa9SWilliam Breathitt Gray static int stx104_probe(struct device *dev, unsigned int id)
368955c2aa9SWilliam Breathitt Gray {
369955c2aa9SWilliam Breathitt Gray 	struct iio_dev *indio_dev;
370955c2aa9SWilliam Breathitt Gray 	struct stx104_iio *priv;
371c7301b84SWilliam Breathitt Gray 	struct gpio_regmap_config gpio_config;
372c7301b84SWilliam Breathitt Gray 	void __iomem *stx104_base;
373c7301b84SWilliam Breathitt Gray 	struct regmap *aio_ctl_map;
374c7301b84SWilliam Breathitt Gray 	struct regmap *aio_data_map;
375c7301b84SWilliam Breathitt Gray 	struct regmap *dio_map;
376955c2aa9SWilliam Breathitt Gray 	int err;
377c7301b84SWilliam Breathitt Gray 	unsigned int adc_status;
378955c2aa9SWilliam Breathitt Gray 
379955c2aa9SWilliam Breathitt Gray 	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
380955c2aa9SWilliam Breathitt Gray 	if (!indio_dev)
381955c2aa9SWilliam Breathitt Gray 		return -ENOMEM;
382955c2aa9SWilliam Breathitt Gray 
383955c2aa9SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], STX104_EXTENT,
384955c2aa9SWilliam Breathitt Gray 		dev_name(dev))) {
385955c2aa9SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
386955c2aa9SWilliam Breathitt Gray 			base[id], base[id] + STX104_EXTENT);
387955c2aa9SWilliam Breathitt Gray 		return -EBUSY;
388955c2aa9SWilliam Breathitt Gray 	}
389955c2aa9SWilliam Breathitt Gray 
390c7301b84SWilliam Breathitt Gray 	stx104_base = devm_ioport_map(dev, base[id], STX104_EXTENT);
391c7301b84SWilliam Breathitt Gray 	if (!stx104_base)
392955c2aa9SWilliam Breathitt Gray 		return -ENOMEM;
393955c2aa9SWilliam Breathitt Gray 
394c7301b84SWilliam Breathitt Gray 	aio_ctl_map = devm_regmap_init_mmio(dev, stx104_base, &aio_ctl_regmap_config);
395c7301b84SWilliam Breathitt Gray 	if (IS_ERR(aio_ctl_map))
396c7301b84SWilliam Breathitt Gray 		return dev_err_probe(dev, PTR_ERR(aio_ctl_map),
397c7301b84SWilliam Breathitt Gray 				     "Unable to initialize aio_ctl register map\n");
398c7301b84SWilliam Breathitt Gray 
399c7301b84SWilliam Breathitt Gray 	aio_data_map = devm_regmap_init_mmio(dev, stx104_base, &aio_data_regmap_config);
400c7301b84SWilliam Breathitt Gray 	if (IS_ERR(aio_data_map))
401c7301b84SWilliam Breathitt Gray 		return dev_err_probe(dev, PTR_ERR(aio_data_map),
402c7301b84SWilliam Breathitt Gray 				     "Unable to initialize aio_data register map\n");
403c7301b84SWilliam Breathitt Gray 
404c7301b84SWilliam Breathitt Gray 	dio_map = devm_regmap_init_mmio(dev, stx104_base, &dio_regmap_config);
405c7301b84SWilliam Breathitt Gray 	if (IS_ERR(dio_map))
406c7301b84SWilliam Breathitt Gray 		return dev_err_probe(dev, PTR_ERR(dio_map),
407c7301b84SWilliam Breathitt Gray 				     "Unable to initialize dio register map\n");
408c7301b84SWilliam Breathitt Gray 
409c7301b84SWilliam Breathitt Gray 	priv = iio_priv(indio_dev);
410c7301b84SWilliam Breathitt Gray 	priv->aio_ctl_map = aio_ctl_map;
411c7301b84SWilliam Breathitt Gray 	priv->aio_data_map = aio_data_map;
412c7301b84SWilliam Breathitt Gray 
413955c2aa9SWilliam Breathitt Gray 	indio_dev->info = &stx104_info;
414955c2aa9SWilliam Breathitt Gray 	indio_dev->modes = INDIO_DIRECT_MODE;
415955c2aa9SWilliam Breathitt Gray 
416c7301b84SWilliam Breathitt Gray 	err = regmap_read(aio_ctl_map, STX104_ADC_STATUS, &adc_status);
417c7301b84SWilliam Breathitt Gray 	if (err)
418c7301b84SWilliam Breathitt Gray 		return err;
419c7301b84SWilliam Breathitt Gray 
420c7301b84SWilliam Breathitt Gray 	if (u8_get_bits(adc_status, STX104_SD) == STX104_DIFFERENTIAL) {
421955c2aa9SWilliam Breathitt Gray 		indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff);
422955c2aa9SWilliam Breathitt Gray 		indio_dev->channels = stx104_channels_diff;
423955c2aa9SWilliam Breathitt Gray 	} else {
424955c2aa9SWilliam Breathitt Gray 		indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing);
425955c2aa9SWilliam Breathitt Gray 		indio_dev->channels = stx104_channels_sing;
426955c2aa9SWilliam Breathitt Gray 	}
427955c2aa9SWilliam Breathitt Gray 
428955c2aa9SWilliam Breathitt Gray 	indio_dev->name = dev_name(dev);
429955c2aa9SWilliam Breathitt Gray 
43097408274SWilliam Breathitt Gray 	mutex_init(&priv->lock);
43197408274SWilliam Breathitt Gray 
432c7301b84SWilliam Breathitt Gray 	err = stx104_init_hw(priv);
433c7301b84SWilliam Breathitt Gray 	if (err)
434955c2aa9SWilliam Breathitt Gray 		return err;
435955c2aa9SWilliam Breathitt Gray 
436c7301b84SWilliam Breathitt Gray 	err = devm_iio_device_register(dev, indio_dev);
437c7301b84SWilliam Breathitt Gray 	if (err)
438c7301b84SWilliam Breathitt Gray 		return err;
439c7301b84SWilliam Breathitt Gray 
440c7301b84SWilliam Breathitt Gray 	gpio_config = (struct gpio_regmap_config) {
441c7301b84SWilliam Breathitt Gray 		.parent = dev,
442c7301b84SWilliam Breathitt Gray 		.regmap = dio_map,
443c7301b84SWilliam Breathitt Gray 		.ngpio = STX104_NGPIO,
444c7301b84SWilliam Breathitt Gray 		.names = stx104_names,
445c7301b84SWilliam Breathitt Gray 		.reg_dat_base = GPIO_REGMAP_ADDR(STX104_DIO_REG),
446c7301b84SWilliam Breathitt Gray 		.reg_set_base = GPIO_REGMAP_ADDR(STX104_DIO_REG),
447c7301b84SWilliam Breathitt Gray 		.ngpio_per_reg = STX104_NGPIO,
448c7301b84SWilliam Breathitt Gray 		.reg_mask_xlate = stx104_reg_mask_xlate,
449c7301b84SWilliam Breathitt Gray 		.drvdata = dio_map,
450c7301b84SWilliam Breathitt Gray 	};
451c7301b84SWilliam Breathitt Gray 
452c7301b84SWilliam Breathitt Gray 	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
453955c2aa9SWilliam Breathitt Gray }
454955c2aa9SWilliam Breathitt Gray 
455955c2aa9SWilliam Breathitt Gray static struct isa_driver stx104_driver = {
456955c2aa9SWilliam Breathitt Gray 	.probe = stx104_probe,
457955c2aa9SWilliam Breathitt Gray 	.driver = {
458955c2aa9SWilliam Breathitt Gray 		.name = "stx104"
459955c2aa9SWilliam Breathitt Gray 	},
460955c2aa9SWilliam Breathitt Gray };
461955c2aa9SWilliam Breathitt Gray 
462955c2aa9SWilliam Breathitt Gray module_isa_driver(stx104_driver, num_stx104);
463955c2aa9SWilliam Breathitt Gray 
464955c2aa9SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
465955c2aa9SWilliam Breathitt Gray MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver");
466955c2aa9SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
467