xref: /linux/drivers/comedi/drivers/aio_aio12_8.c (revision 2da68a77)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * aio_aio12_8.c
4  * Driver for Access I/O Products PC-104 AIO12-8 Analog I/O Board
5  * Copyright (C) 2006 C&C Technologies, Inc.
6  */
7 
8 /*
9  * Driver: aio_aio12_8
10  * Description: Access I/O Products PC-104 AIO12-8 Analog I/O Board
11  * Author: Pablo Mejia <pablo.mejia@cctechnol.com>
12  * Devices: [Access I/O] PC-104 AIO12-8 (aio_aio12_8),
13  *   [Access I/O] PC-104 AI12-8 (aio_ai12_8),
14  *   [Access I/O] PC-104 AO12-4 (aio_ao12_4)
15  * Status: experimental
16  *
17  * Configuration Options:
18  *   [0] - I/O port base address
19  *
20  * Notes:
21  * Only synchronous operations are supported.
22  */
23 
24 #include <linux/module.h>
25 #include <linux/comedi/comedidev.h>
26 #include <linux/comedi/comedi_8255.h>
27 #include <linux/comedi/comedi_8254.h>
28 
29 /*
30  * Register map
31  */
32 #define AIO12_8_STATUS_REG		0x00
33 #define AIO12_8_STATUS_ADC_EOC		BIT(7)
34 #define AIO12_8_STATUS_PORT_C_COS	BIT(6)
35 #define AIO12_8_STATUS_IRQ_ENA		BIT(2)
36 #define AIO12_8_INTERRUPT_REG		0x01
37 #define AIO12_8_INTERRUPT_ADC		BIT(7)
38 #define AIO12_8_INTERRUPT_COS		BIT(6)
39 #define AIO12_8_INTERRUPT_COUNTER1	BIT(5)
40 #define AIO12_8_INTERRUPT_PORT_C3	BIT(4)
41 #define AIO12_8_INTERRUPT_PORT_C0	BIT(3)
42 #define AIO12_8_INTERRUPT_ENA		BIT(2)
43 #define AIO12_8_ADC_REG			0x02
44 #define AIO12_8_ADC_MODE(x)		(((x) & 0x3) << 6)
45 #define AIO12_8_ADC_MODE_NORMAL		AIO12_8_ADC_MODE(0)
46 #define AIO12_8_ADC_MODE_INT_CLK	AIO12_8_ADC_MODE(1)
47 #define AIO12_8_ADC_MODE_STANDBY	AIO12_8_ADC_MODE(2)
48 #define AIO12_8_ADC_MODE_POWERDOWN	AIO12_8_ADC_MODE(3)
49 #define AIO12_8_ADC_ACQ(x)		(((x) & 0x1) << 5)
50 #define AIO12_8_ADC_ACQ_3USEC		AIO12_8_ADC_ACQ(0)
51 #define AIO12_8_ADC_ACQ_PROGRAM		AIO12_8_ADC_ACQ(1)
52 #define AIO12_8_ADC_RANGE(x)		((x) << 3)
53 #define AIO12_8_ADC_CHAN(x)		((x) << 0)
54 #define AIO12_8_DAC_REG(x)		(0x04 + (x) * 2)
55 #define AIO12_8_8254_BASE_REG		0x0c
56 #define AIO12_8_8255_BASE_REG		0x10
57 #define AIO12_8_DIO_CONTROL_REG		0x14
58 #define AIO12_8_DIO_CONTROL_TST		BIT(0)
59 #define AIO12_8_ADC_TRIGGER_REG		0x15
60 #define AIO12_8_ADC_TRIGGER_RANGE(x)	((x) << 3)
61 #define AIO12_8_ADC_TRIGGER_CHAN(x)	((x) << 0)
62 #define AIO12_8_TRIGGER_REG		0x16
63 #define AIO12_8_TRIGGER_ADTRIG		BIT(1)
64 #define AIO12_8_TRIGGER_DACTRIG		BIT(0)
65 #define AIO12_8_COS_REG			0x17
66 #define AIO12_8_DAC_ENABLE_REG		0x18
67 #define AIO12_8_DAC_ENABLE_REF_ENA	BIT(0)
68 
69 static const struct comedi_lrange aio_aio12_8_range = {
70 	4, {
71 		UNI_RANGE(5),
72 		BIP_RANGE(5),
73 		UNI_RANGE(10),
74 		BIP_RANGE(10)
75 	}
76 };
77 
78 struct aio12_8_boardtype {
79 	const char *name;
80 	unsigned int has_ai:1;
81 	unsigned int has_ao:1;
82 };
83 
84 static const struct aio12_8_boardtype board_types[] = {
85 	{
86 		.name		= "aio_aio12_8",
87 		.has_ai		= 1,
88 		.has_ao		= 1,
89 	}, {
90 		.name		= "aio_ai12_8",
91 		.has_ai		= 1,
92 	}, {
93 		.name		= "aio_ao12_4",
94 		.has_ao		= 1,
95 	},
96 };
97 
98 static int aio_aio12_8_ai_eoc(struct comedi_device *dev,
99 			      struct comedi_subdevice *s,
100 			      struct comedi_insn *insn,
101 			      unsigned long context)
102 {
103 	unsigned int status;
104 
105 	status = inb(dev->iobase + AIO12_8_STATUS_REG);
106 	if (status & AIO12_8_STATUS_ADC_EOC)
107 		return 0;
108 	return -EBUSY;
109 }
110 
111 static int aio_aio12_8_ai_read(struct comedi_device *dev,
112 			       struct comedi_subdevice *s,
113 			       struct comedi_insn *insn,
114 			       unsigned int *data)
115 {
116 	unsigned int chan = CR_CHAN(insn->chanspec);
117 	unsigned int range = CR_RANGE(insn->chanspec);
118 	unsigned int val;
119 	unsigned char control;
120 	int ret;
121 	int i;
122 
123 	/*
124 	 * Setup the control byte for internal 2MHz clock, 3uS conversion,
125 	 * at the desired range of the requested channel.
126 	 */
127 	control = AIO12_8_ADC_MODE_NORMAL | AIO12_8_ADC_ACQ_3USEC |
128 		  AIO12_8_ADC_RANGE(range) | AIO12_8_ADC_CHAN(chan);
129 
130 	/* Read status to clear EOC latch */
131 	inb(dev->iobase + AIO12_8_STATUS_REG);
132 
133 	for (i = 0; i < insn->n; i++) {
134 		/*  Setup and start conversion */
135 		outb(control, dev->iobase + AIO12_8_ADC_REG);
136 
137 		/*  Wait for conversion to complete */
138 		ret = comedi_timeout(dev, s, insn, aio_aio12_8_ai_eoc, 0);
139 		if (ret)
140 			return ret;
141 
142 		val = inw(dev->iobase + AIO12_8_ADC_REG) & s->maxdata;
143 
144 		/* munge bipolar 2's complement data to offset binary */
145 		if (comedi_range_is_bipolar(s, range))
146 			val = comedi_offset_munge(s, val);
147 
148 		data[i] = val;
149 	}
150 
151 	return insn->n;
152 }
153 
154 static int aio_aio12_8_ao_insn_write(struct comedi_device *dev,
155 				     struct comedi_subdevice *s,
156 				     struct comedi_insn *insn,
157 				     unsigned int *data)
158 {
159 	unsigned int chan = CR_CHAN(insn->chanspec);
160 	unsigned int val = s->readback[chan];
161 	int i;
162 
163 	/* enable DACs */
164 	outb(AIO12_8_DAC_ENABLE_REF_ENA, dev->iobase + AIO12_8_DAC_ENABLE_REG);
165 
166 	for (i = 0; i < insn->n; i++) {
167 		val = data[i];
168 		outw(val, dev->iobase + AIO12_8_DAC_REG(chan));
169 	}
170 	s->readback[chan] = val;
171 
172 	return insn->n;
173 }
174 
175 static int aio_aio12_8_counter_insn_config(struct comedi_device *dev,
176 					   struct comedi_subdevice *s,
177 					   struct comedi_insn *insn,
178 					   unsigned int *data)
179 {
180 	unsigned int chan = CR_CHAN(insn->chanspec);
181 
182 	switch (data[0]) {
183 	case INSN_CONFIG_GET_CLOCK_SRC:
184 		/*
185 		 * Channels 0 and 2 have external clock sources.
186 		 * Channel 1 has a fixed 1 MHz clock source.
187 		 */
188 		data[0] = 0;
189 		data[1] = (chan == 1) ? I8254_OSC_BASE_1MHZ : 0;
190 		break;
191 	default:
192 		return -EINVAL;
193 	}
194 
195 	return insn->n;
196 }
197 
198 static int aio_aio12_8_attach(struct comedi_device *dev,
199 			      struct comedi_devconfig *it)
200 {
201 	const struct aio12_8_boardtype *board = dev->board_ptr;
202 	struct comedi_subdevice *s;
203 	int ret;
204 
205 	ret = comedi_request_region(dev, it->options[0], 32);
206 	if (ret)
207 		return ret;
208 
209 	dev->pacer = comedi_8254_init(dev->iobase + AIO12_8_8254_BASE_REG,
210 				      0, I8254_IO8, 0);
211 	if (!dev->pacer)
212 		return -ENOMEM;
213 
214 	ret = comedi_alloc_subdevices(dev, 4);
215 	if (ret)
216 		return ret;
217 
218 	/* Analog Input subdevice */
219 	s = &dev->subdevices[0];
220 	if (board->has_ai) {
221 		s->type		= COMEDI_SUBD_AI;
222 		s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_DIFF;
223 		s->n_chan	= 8;
224 		s->maxdata	= 0x0fff;
225 		s->range_table	= &aio_aio12_8_range;
226 		s->insn_read	= aio_aio12_8_ai_read;
227 	} else {
228 		s->type = COMEDI_SUBD_UNUSED;
229 	}
230 
231 	/* Analog Output subdevice */
232 	s = &dev->subdevices[1];
233 	if (board->has_ao) {
234 		s->type		= COMEDI_SUBD_AO;
235 		s->subdev_flags	= SDF_WRITABLE | SDF_GROUND;
236 		s->n_chan	= 4;
237 		s->maxdata	= 0x0fff;
238 		s->range_table	= &aio_aio12_8_range;
239 		s->insn_write	= aio_aio12_8_ao_insn_write;
240 
241 		ret = comedi_alloc_subdev_readback(s);
242 		if (ret)
243 			return ret;
244 	} else {
245 		s->type = COMEDI_SUBD_UNUSED;
246 	}
247 
248 	/* Digital I/O subdevice (8255) */
249 	s = &dev->subdevices[2];
250 	ret = subdev_8255_init(dev, s, NULL, AIO12_8_8255_BASE_REG);
251 	if (ret)
252 		return ret;
253 
254 	/* Counter subdevice (8254) */
255 	s = &dev->subdevices[3];
256 	comedi_8254_subdevice_init(s, dev->pacer);
257 
258 	dev->pacer->insn_config = aio_aio12_8_counter_insn_config;
259 
260 	return 0;
261 }
262 
263 static struct comedi_driver aio_aio12_8_driver = {
264 	.driver_name	= "aio_aio12_8",
265 	.module		= THIS_MODULE,
266 	.attach		= aio_aio12_8_attach,
267 	.detach		= comedi_legacy_detach,
268 	.board_name	= &board_types[0].name,
269 	.num_names	= ARRAY_SIZE(board_types),
270 	.offset		= sizeof(struct aio12_8_boardtype),
271 };
272 module_comedi_driver(aio_aio12_8_driver);
273 
274 MODULE_AUTHOR("Comedi https://www.comedi.org");
275 MODULE_DESCRIPTION("Comedi driver for Access I/O AIO12-8 Analog I/O Board");
276 MODULE_LICENSE("GPL");
277