1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * pcmmio.c
4  * Driver for Winsystems PC-104 based multifunction IO board.
5  *
6  * COMEDI - Linux Control and Measurement Device Interface
7  * Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
8  */
9 
10 /*
11  * Driver: pcmmio
12  * Description: A driver for the PCM-MIO multifunction board
13  * Devices: [Winsystems] PCM-MIO (pcmmio)
14  * Author: Calin Culianu <calin@ajvar.org>
15  * Updated: Wed, May 16 2007 16:21:10 -0500
16  * Status: works
17  *
18  * A driver for the PCM-MIO multifunction board from Winsystems. This
19  * is a PC-104 based I/O board. It contains four subdevices:
20  *
21  *	subdevice 0 - 16 channels of 16-bit AI
22  *	subdevice 1 - 8 channels of 16-bit AO
23  *	subdevice 2 - first 24 channels of the 48 channel of DIO
24  *			(with edge-triggered interrupt support)
25  *	subdevice 3 - last 24 channels of the 48 channel DIO
26  *			(no interrupt support for this bank of channels)
27  *
28  * Some notes:
29  *
30  * Synchronous reads and writes are the only things implemented for analog
31  * input and output. The hardware itself can do streaming acquisition, etc.
32  *
33  * Asynchronous I/O for the DIO subdevices *is* implemented, however! They
34  * are basically edge-triggered interrupts for any configuration of the
35  * channels in subdevice 2.
36  *
37  * Also note that this interrupt support is untested.
38  *
39  * A few words about edge-detection IRQ support (commands on DIO):
40  *
41  * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
42  * of the board to the comedi_config command. The board IRQ is not jumpered
43  * but rather configured through software, so any IRQ from 1-15 is OK.
44  *
45  * Due to the genericity of the comedi API, you need to create a special
46  * comedi_command in order to use edge-triggered interrupts for DIO.
47  *
48  * Use comedi_commands with TRIG_NOW.  Your callback will be called each
49  * time an edge is detected on the specified DIO line(s), and the data
50  * values will be two sample_t's, which should be concatenated to form
51  * one 32-bit unsigned int. This value is the mask of channels that had
52  * edges detected from your channel list. Note that the bits positions
53  * in the mask correspond to positions in your chanlist when you
54  * specified the command and *not* channel id's!
55  *
56  * To set the polarity of the edge-detection interrupts pass a nonzero value
57  * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
58  * value for both CR_RANGE and CR_AREF if you want edge-down polarity.
59  *
60  * Configuration Options:
61  *   [0] - I/O port base address
62  *   [1] - IRQ (optional -- for edge-detect interrupt support only,
63  *		leave out if you don't need this feature)
64  */
65 
66 #include <linux/module.h>
67 #include <linux/interrupt.h>
68 #include <linux/slab.h>
69 
70 #include "../comedidev.h"
71 
72 /*
73  * Register I/O map
74  */
75 #define PCMMIO_AI_LSB_REG			0x00
76 #define PCMMIO_AI_MSB_REG			0x01
77 #define PCMMIO_AI_CMD_REG			0x02
78 #define PCMMIO_AI_CMD_SE			BIT(7)
79 #define PCMMIO_AI_CMD_ODD_CHAN			BIT(6)
80 #define PCMMIO_AI_CMD_CHAN_SEL(x)		(((x) & 0x3) << 4)
81 #define PCMMIO_AI_CMD_RANGE(x)			(((x) & 0x3) << 2)
82 #define PCMMIO_RESOURCE_REG			0x02
83 #define PCMMIO_RESOURCE_IRQ(x)			(((x) & 0xf) << 0)
84 #define PCMMIO_AI_STATUS_REG			0x03
85 #define PCMMIO_AI_STATUS_DATA_READY		BIT(7)
86 #define PCMMIO_AI_STATUS_DATA_DMA_PEND		BIT(6)
87 #define PCMMIO_AI_STATUS_CMD_DMA_PEND		BIT(5)
88 #define PCMMIO_AI_STATUS_IRQ_PEND		BIT(4)
89 #define PCMMIO_AI_STATUS_DATA_DRQ_ENA		BIT(2)
90 #define PCMMIO_AI_STATUS_REG_SEL		BIT(3)
91 #define PCMMIO_AI_STATUS_CMD_DRQ_ENA		BIT(1)
92 #define PCMMIO_AI_STATUS_IRQ_ENA		BIT(0)
93 #define PCMMIO_AI_RES_ENA_REG			0x03
94 #define PCMMIO_AI_RES_ENA_CMD_REG_ACCESS	(0 << 3)
95 #define PCMMIO_AI_RES_ENA_AI_RES_ACCESS		BIT(3)
96 #define PCMMIO_AI_RES_ENA_DIO_RES_ACCESS	BIT(4)
97 #define PCMMIO_AI_2ND_ADC_OFFSET		0x04
98 
99 #define PCMMIO_AO_LSB_REG			0x08
100 #define PCMMIO_AO_LSB_SPAN(x)			(((x) & 0xf) << 0)
101 #define PCMMIO_AO_MSB_REG			0x09
102 #define PCMMIO_AO_CMD_REG			0x0a
103 #define PCMMIO_AO_CMD_WR_SPAN			(0x2 << 4)
104 #define PCMMIO_AO_CMD_WR_CODE			(0x3 << 4)
105 #define PCMMIO_AO_CMD_UPDATE			(0x4 << 4)
106 #define PCMMIO_AO_CMD_UPDATE_ALL		(0x5 << 4)
107 #define PCMMIO_AO_CMD_WR_SPAN_UPDATE		(0x6 << 4)
108 #define PCMMIO_AO_CMD_WR_CODE_UPDATE		(0x7 << 4)
109 #define PCMMIO_AO_CMD_WR_SPAN_UPDATE_ALL	(0x8 << 4)
110 #define PCMMIO_AO_CMD_WR_CODE_UPDATE_ALL	(0x9 << 4)
111 #define PCMMIO_AO_CMD_RD_B1_SPAN		(0xa << 4)
112 #define PCMMIO_AO_CMD_RD_B1_CODE		(0xb << 4)
113 #define PCMMIO_AO_CMD_RD_B2_SPAN		(0xc << 4)
114 #define PCMMIO_AO_CMD_RD_B2_CODE		(0xd << 4)
115 #define PCMMIO_AO_CMD_NOP			(0xf << 4)
116 #define PCMMIO_AO_CMD_CHAN_SEL(x)		(((x) & 0x03) << 1)
117 #define PCMMIO_AO_CMD_CHAN_SEL_ALL		(0x0f << 0)
118 #define PCMMIO_AO_STATUS_REG			0x0b
119 #define PCMMIO_AO_STATUS_DATA_READY		BIT(7)
120 #define PCMMIO_AO_STATUS_DATA_DMA_PEND		BIT(6)
121 #define PCMMIO_AO_STATUS_CMD_DMA_PEND		BIT(5)
122 #define PCMMIO_AO_STATUS_IRQ_PEND		BIT(4)
123 #define PCMMIO_AO_STATUS_DATA_DRQ_ENA		BIT(2)
124 #define PCMMIO_AO_STATUS_REG_SEL		BIT(3)
125 #define PCMMIO_AO_STATUS_CMD_DRQ_ENA		BIT(1)
126 #define PCMMIO_AO_STATUS_IRQ_ENA		BIT(0)
127 #define PCMMIO_AO_RESOURCE_ENA_REG		0x0b
128 #define PCMMIO_AO_2ND_DAC_OFFSET		0x04
129 
130 /*
131  * WinSystems WS16C48
132  *
133  * Offset    Page 0       Page 1       Page 2       Page 3
134  * ------  -----------  -----------  -----------  -----------
135  *  0x10   Port 0 I/O   Port 0 I/O   Port 0 I/O   Port 0 I/O
136  *  0x11   Port 1 I/O   Port 1 I/O   Port 1 I/O   Port 1 I/O
137  *  0x12   Port 2 I/O   Port 2 I/O   Port 2 I/O   Port 2 I/O
138  *  0x13   Port 3 I/O   Port 3 I/O   Port 3 I/O   Port 3 I/O
139  *  0x14   Port 4 I/O   Port 4 I/O   Port 4 I/O   Port 4 I/O
140  *  0x15   Port 5 I/O   Port 5 I/O   Port 5 I/O   Port 5 I/O
141  *  0x16   INT_PENDING  INT_PENDING  INT_PENDING  INT_PENDING
142  *  0x17    Page/Lock    Page/Lock    Page/Lock    Page/Lock
143  *  0x18       N/A         POL_0       ENAB_0       INT_ID0
144  *  0x19       N/A         POL_1       ENAB_1       INT_ID1
145  *  0x1a       N/A         POL_2       ENAB_2       INT_ID2
146  */
147 #define PCMMIO_PORT_REG(x)			(0x10 + (x))
148 #define PCMMIO_INT_PENDING_REG			0x16
149 #define PCMMIO_PAGE_LOCK_REG			0x17
150 #define PCMMIO_LOCK_PORT(x)			((1 << (x)) & 0x3f)
151 #define PCMMIO_PAGE(x)				(((x) & 0x3) << 6)
152 #define PCMMIO_PAGE_MASK			PCMUIO_PAGE(3)
153 #define PCMMIO_PAGE_POL				1
154 #define PCMMIO_PAGE_ENAB			2
155 #define PCMMIO_PAGE_INT_ID			3
156 #define PCMMIO_PAGE_REG(x)			(0x18 + (x))
157 
158 static const struct comedi_lrange pcmmio_ai_ranges = {
159 	4, {
160 		BIP_RANGE(5),
161 		BIP_RANGE(10),
162 		UNI_RANGE(5),
163 		UNI_RANGE(10)
164 	}
165 };
166 
167 static const struct comedi_lrange pcmmio_ao_ranges = {
168 	6, {
169 		UNI_RANGE(5),
170 		UNI_RANGE(10),
171 		BIP_RANGE(5),
172 		BIP_RANGE(10),
173 		BIP_RANGE(2.5),
174 		RANGE(-2.5, 7.5)
175 	}
176 };
177 
178 struct pcmmio_private {
179 	spinlock_t pagelock;	/* protects the page registers */
180 	spinlock_t spinlock;	/* protects the member variables */
181 	unsigned int enabled_mask;
182 	unsigned int active:1;
183 };
184 
pcmmio_dio_write(struct comedi_device * dev,unsigned int val,int page,int port)185 static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
186 			     int page, int port)
187 {
188 	struct pcmmio_private *devpriv = dev->private;
189 	unsigned long iobase = dev->iobase;
190 	unsigned long flags;
191 
192 	spin_lock_irqsave(&devpriv->pagelock, flags);
193 	if (page == 0) {
194 		/* Port registers are valid for any page */
195 		outb(val & 0xff, iobase + PCMMIO_PORT_REG(port + 0));
196 		outb((val >> 8) & 0xff, iobase + PCMMIO_PORT_REG(port + 1));
197 		outb((val >> 16) & 0xff, iobase + PCMMIO_PORT_REG(port + 2));
198 	} else {
199 		outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
200 		outb(val & 0xff, iobase + PCMMIO_PAGE_REG(0));
201 		outb((val >> 8) & 0xff, iobase + PCMMIO_PAGE_REG(1));
202 		outb((val >> 16) & 0xff, iobase + PCMMIO_PAGE_REG(2));
203 	}
204 	spin_unlock_irqrestore(&devpriv->pagelock, flags);
205 }
206 
pcmmio_dio_read(struct comedi_device * dev,int page,int port)207 static unsigned int pcmmio_dio_read(struct comedi_device *dev,
208 				    int page, int port)
209 {
210 	struct pcmmio_private *devpriv = dev->private;
211 	unsigned long iobase = dev->iobase;
212 	unsigned long flags;
213 	unsigned int val;
214 
215 	spin_lock_irqsave(&devpriv->pagelock, flags);
216 	if (page == 0) {
217 		/* Port registers are valid for any page */
218 		val = inb(iobase + PCMMIO_PORT_REG(port + 0));
219 		val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8);
220 		val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16);
221 	} else {
222 		outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
223 		val = inb(iobase + PCMMIO_PAGE_REG(0));
224 		val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8);
225 		val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16);
226 	}
227 	spin_unlock_irqrestore(&devpriv->pagelock, flags);
228 
229 	return val;
230 }
231 
232 /*
233  * Each channel can be individually programmed for input or output.
234  * Writing a '0' to a channel causes the corresponding output pin
235  * to go to a high-z state (pulled high by an external 10K resistor).
236  * This allows it to be used as an input. When used in the input mode,
237  * a read reflects the inverted state of the I/O pin, such that a
238  * high on the pin will read as a '0' in the register. Writing a '1'
239  * to a bit position causes the pin to sink current (up to 12mA),
240  * effectively pulling it low.
241  */
pcmmio_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)242 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
243 				struct comedi_subdevice *s,
244 				struct comedi_insn *insn,
245 				unsigned int *data)
246 {
247 	/* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
248 	int port = s->index == 2 ? 0 : 3;
249 	unsigned int chanmask = (1 << s->n_chan) - 1;
250 	unsigned int mask;
251 	unsigned int val;
252 
253 	mask = comedi_dio_update_state(s, data);
254 	if (mask) {
255 		/*
256 		 * Outputs are inverted, invert the state and
257 		 * update the channels.
258 		 *
259 		 * The s->io_bits mask makes sure the input channels
260 		 * are '0' so that the outputs pins stay in a high
261 		 * z-state.
262 		 */
263 		val = ~s->state & chanmask;
264 		val &= s->io_bits;
265 		pcmmio_dio_write(dev, val, 0, port);
266 	}
267 
268 	/* get inverted state of the channels from the port */
269 	val = pcmmio_dio_read(dev, 0, port);
270 
271 	/* return the true state of the channels */
272 	data[1] = ~val & chanmask;
273 
274 	return insn->n;
275 }
276 
pcmmio_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)277 static int pcmmio_dio_insn_config(struct comedi_device *dev,
278 				  struct comedi_subdevice *s,
279 				  struct comedi_insn *insn,
280 				  unsigned int *data)
281 {
282 	/* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
283 	int port = s->index == 2 ? 0 : 3;
284 	int ret;
285 
286 	ret = comedi_dio_insn_config(dev, s, insn, data, 0);
287 	if (ret)
288 		return ret;
289 
290 	if (data[0] == INSN_CONFIG_DIO_INPUT)
291 		pcmmio_dio_write(dev, s->io_bits, 0, port);
292 
293 	return insn->n;
294 }
295 
pcmmio_reset(struct comedi_device * dev)296 static void pcmmio_reset(struct comedi_device *dev)
297 {
298 	/* Clear all the DIO port bits */
299 	pcmmio_dio_write(dev, 0, 0, 0);
300 	pcmmio_dio_write(dev, 0, 0, 3);
301 
302 	/* Clear all the paged registers */
303 	pcmmio_dio_write(dev, 0, PCMMIO_PAGE_POL, 0);
304 	pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
305 	pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
306 }
307 
308 /* devpriv->spinlock is already locked */
pcmmio_stop_intr(struct comedi_device * dev,struct comedi_subdevice * s)309 static void pcmmio_stop_intr(struct comedi_device *dev,
310 			     struct comedi_subdevice *s)
311 {
312 	struct pcmmio_private *devpriv = dev->private;
313 
314 	devpriv->enabled_mask = 0;
315 	devpriv->active = 0;
316 	s->async->inttrig = NULL;
317 
318 	/* disable all dio interrupts */
319 	pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
320 }
321 
pcmmio_handle_dio_intr(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int triggered)322 static void pcmmio_handle_dio_intr(struct comedi_device *dev,
323 				   struct comedi_subdevice *s,
324 				   unsigned int triggered)
325 {
326 	struct pcmmio_private *devpriv = dev->private;
327 	struct comedi_cmd *cmd = &s->async->cmd;
328 	unsigned int val = 0;
329 	unsigned long flags;
330 	int i;
331 
332 	spin_lock_irqsave(&devpriv->spinlock, flags);
333 
334 	if (!devpriv->active)
335 		goto done;
336 
337 	if (!(triggered & devpriv->enabled_mask))
338 		goto done;
339 
340 	for (i = 0; i < cmd->chanlist_len; i++) {
341 		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
342 
343 		if (triggered & (1 << chan))
344 			val |= (1 << i);
345 	}
346 
347 	comedi_buf_write_samples(s, &val, 1);
348 
349 	if (cmd->stop_src == TRIG_COUNT &&
350 	    s->async->scans_done >= cmd->stop_arg)
351 		s->async->events |= COMEDI_CB_EOA;
352 
353 done:
354 	spin_unlock_irqrestore(&devpriv->spinlock, flags);
355 
356 	comedi_handle_events(dev, s);
357 }
358 
interrupt_pcmmio(int irq,void * d)359 static irqreturn_t interrupt_pcmmio(int irq, void *d)
360 {
361 	struct comedi_device *dev = d;
362 	struct comedi_subdevice *s = dev->read_subdev;
363 	unsigned int triggered;
364 	unsigned char int_pend;
365 
366 	/* are there any interrupts pending */
367 	int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
368 	if (!int_pend)
369 		return IRQ_NONE;
370 
371 	/* get, and clear, the pending interrupts */
372 	triggered = pcmmio_dio_read(dev, PCMMIO_PAGE_INT_ID, 0);
373 	pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
374 
375 	pcmmio_handle_dio_intr(dev, s, triggered);
376 
377 	return IRQ_HANDLED;
378 }
379 
380 /* devpriv->spinlock is already locked */
pcmmio_start_intr(struct comedi_device * dev,struct comedi_subdevice * s)381 static void pcmmio_start_intr(struct comedi_device *dev,
382 			      struct comedi_subdevice *s)
383 {
384 	struct pcmmio_private *devpriv = dev->private;
385 	struct comedi_cmd *cmd = &s->async->cmd;
386 	unsigned int bits = 0;
387 	unsigned int pol_bits = 0;
388 	int i;
389 
390 	devpriv->enabled_mask = 0;
391 	devpriv->active = 1;
392 	if (cmd->chanlist) {
393 		for (i = 0; i < cmd->chanlist_len; i++) {
394 			unsigned int chanspec = cmd->chanlist[i];
395 			unsigned int chan = CR_CHAN(chanspec);
396 			unsigned int range = CR_RANGE(chanspec);
397 			unsigned int aref = CR_AREF(chanspec);
398 
399 			bits |= (1 << chan);
400 			pol_bits |= (((aref || range) ? 1 : 0) << chan);
401 		}
402 	}
403 	bits &= ((1 << s->n_chan) - 1);
404 	devpriv->enabled_mask = bits;
405 
406 	/* set polarity and enable interrupts */
407 	pcmmio_dio_write(dev, pol_bits, PCMMIO_PAGE_POL, 0);
408 	pcmmio_dio_write(dev, bits, PCMMIO_PAGE_ENAB, 0);
409 }
410 
pcmmio_cancel(struct comedi_device * dev,struct comedi_subdevice * s)411 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
412 {
413 	struct pcmmio_private *devpriv = dev->private;
414 	unsigned long flags;
415 
416 	spin_lock_irqsave(&devpriv->spinlock, flags);
417 	if (devpriv->active)
418 		pcmmio_stop_intr(dev, s);
419 	spin_unlock_irqrestore(&devpriv->spinlock, flags);
420 
421 	return 0;
422 }
423 
pcmmio_inttrig_start_intr(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int trig_num)424 static int pcmmio_inttrig_start_intr(struct comedi_device *dev,
425 				     struct comedi_subdevice *s,
426 				     unsigned int trig_num)
427 {
428 	struct pcmmio_private *devpriv = dev->private;
429 	struct comedi_cmd *cmd = &s->async->cmd;
430 	unsigned long flags;
431 
432 	if (trig_num != cmd->start_arg)
433 		return -EINVAL;
434 
435 	spin_lock_irqsave(&devpriv->spinlock, flags);
436 	s->async->inttrig = NULL;
437 	if (devpriv->active)
438 		pcmmio_start_intr(dev, s);
439 	spin_unlock_irqrestore(&devpriv->spinlock, flags);
440 
441 	return 1;
442 }
443 
444 /*
445  * 'do_cmd' function for an 'INTERRUPT' subdevice.
446  */
pcmmio_cmd(struct comedi_device * dev,struct comedi_subdevice * s)447 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
448 {
449 	struct pcmmio_private *devpriv = dev->private;
450 	struct comedi_cmd *cmd = &s->async->cmd;
451 	unsigned long flags;
452 
453 	spin_lock_irqsave(&devpriv->spinlock, flags);
454 	devpriv->active = 1;
455 
456 	/* Set up start of acquisition. */
457 	if (cmd->start_src == TRIG_INT)
458 		s->async->inttrig = pcmmio_inttrig_start_intr;
459 	else	/* TRIG_NOW */
460 		pcmmio_start_intr(dev, s);
461 
462 	spin_unlock_irqrestore(&devpriv->spinlock, flags);
463 
464 	return 0;
465 }
466 
pcmmio_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)467 static int pcmmio_cmdtest(struct comedi_device *dev,
468 			  struct comedi_subdevice *s,
469 			  struct comedi_cmd *cmd)
470 {
471 	int err = 0;
472 
473 	/* Step 1 : check if triggers are trivially valid */
474 
475 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
476 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
477 	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
478 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
479 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
480 
481 	if (err)
482 		return 1;
483 
484 	/* Step 2a : make sure trigger sources are unique */
485 
486 	err |= comedi_check_trigger_is_unique(cmd->start_src);
487 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
488 
489 	/* Step 2b : and mutually compatible */
490 
491 	if (err)
492 		return 2;
493 
494 	/* Step 3: check if arguments are trivially valid */
495 
496 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
497 	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
498 	err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
499 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
500 					   cmd->chanlist_len);
501 
502 	if (cmd->stop_src == TRIG_COUNT)
503 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
504 	else	/* TRIG_NONE */
505 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
506 
507 	if (err)
508 		return 3;
509 
510 	/* step 4: fix up any arguments */
511 
512 	/* if (err) return 4; */
513 
514 	return 0;
515 }
516 
pcmmio_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)517 static int pcmmio_ai_eoc(struct comedi_device *dev,
518 			 struct comedi_subdevice *s,
519 			 struct comedi_insn *insn,
520 			 unsigned long context)
521 {
522 	unsigned char status;
523 
524 	status = inb(dev->iobase + PCMMIO_AI_STATUS_REG);
525 	if (status & PCMMIO_AI_STATUS_DATA_READY)
526 		return 0;
527 	return -EBUSY;
528 }
529 
pcmmio_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)530 static int pcmmio_ai_insn_read(struct comedi_device *dev,
531 			       struct comedi_subdevice *s,
532 			       struct comedi_insn *insn,
533 			       unsigned int *data)
534 {
535 	unsigned long iobase = dev->iobase;
536 	unsigned int chan = CR_CHAN(insn->chanspec);
537 	unsigned int range = CR_RANGE(insn->chanspec);
538 	unsigned int aref = CR_AREF(insn->chanspec);
539 	unsigned char cmd = 0;
540 	unsigned int val;
541 	int ret;
542 	int i;
543 
544 	/*
545 	 * The PCM-MIO uses two Linear Tech LTC1859CG 8-channel A/D converters.
546 	 * The devices use a full duplex serial interface which transmits and
547 	 * receives data simultaneously. An 8-bit command is shifted into the
548 	 * ADC interface to configure it for the next conversion. At the same
549 	 * time, the data from the previous conversion is shifted out of the
550 	 * device. Consequently, the conversion result is delayed by one
551 	 * conversion from the command word.
552 	 *
553 	 * Setup the cmd for the conversions then do a dummy conversion to
554 	 * flush the junk data. Then do each conversion requested by the
555 	 * comedi_insn. Note that the last conversion will leave junk data
556 	 * in ADC which will get flushed on the next comedi_insn.
557 	 */
558 
559 	if (chan > 7) {
560 		chan -= 8;
561 		iobase += PCMMIO_AI_2ND_ADC_OFFSET;
562 	}
563 
564 	if (aref == AREF_GROUND)
565 		cmd |= PCMMIO_AI_CMD_SE;
566 	if (chan % 2)
567 		cmd |= PCMMIO_AI_CMD_ODD_CHAN;
568 	cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2);
569 	cmd |= PCMMIO_AI_CMD_RANGE(range);
570 
571 	outb(cmd, iobase + PCMMIO_AI_CMD_REG);
572 
573 	ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
574 	if (ret)
575 		return ret;
576 
577 	val = inb(iobase + PCMMIO_AI_LSB_REG);
578 	val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
579 
580 	for (i = 0; i < insn->n; i++) {
581 		outb(cmd, iobase + PCMMIO_AI_CMD_REG);
582 
583 		ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
584 		if (ret)
585 			return ret;
586 
587 		val = inb(iobase + PCMMIO_AI_LSB_REG);
588 		val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
589 
590 		/* bipolar data is two's complement */
591 		if (comedi_range_is_bipolar(s, range))
592 			val = comedi_offset_munge(s, val);
593 
594 		data[i] = val;
595 	}
596 
597 	return insn->n;
598 }
599 
pcmmio_ao_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)600 static int pcmmio_ao_eoc(struct comedi_device *dev,
601 			 struct comedi_subdevice *s,
602 			 struct comedi_insn *insn,
603 			 unsigned long context)
604 {
605 	unsigned char status;
606 
607 	status = inb(dev->iobase + PCMMIO_AO_STATUS_REG);
608 	if (status & PCMMIO_AO_STATUS_DATA_READY)
609 		return 0;
610 	return -EBUSY;
611 }
612 
pcmmio_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)613 static int pcmmio_ao_insn_write(struct comedi_device *dev,
614 				struct comedi_subdevice *s,
615 				struct comedi_insn *insn,
616 				unsigned int *data)
617 {
618 	unsigned long iobase = dev->iobase;
619 	unsigned int chan = CR_CHAN(insn->chanspec);
620 	unsigned int range = CR_RANGE(insn->chanspec);
621 	unsigned char cmd = 0;
622 	int ret;
623 	int i;
624 
625 	/*
626 	 * The PCM-MIO has two Linear Tech LTC2704 DAC devices. Each device
627 	 * is a 4-channel converter with software-selectable output range.
628 	 */
629 
630 	if (chan > 3) {
631 		cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4);
632 		iobase += PCMMIO_AO_2ND_DAC_OFFSET;
633 	} else {
634 		cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan);
635 	}
636 
637 	/* set the range for the channel */
638 	outb(PCMMIO_AO_LSB_SPAN(range), iobase + PCMMIO_AO_LSB_REG);
639 	outb(0, iobase + PCMMIO_AO_MSB_REG);
640 	outb(cmd | PCMMIO_AO_CMD_WR_SPAN_UPDATE, iobase + PCMMIO_AO_CMD_REG);
641 
642 	ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
643 	if (ret)
644 		return ret;
645 
646 	for (i = 0; i < insn->n; i++) {
647 		unsigned int val = data[i];
648 
649 		/* write the data to the channel */
650 		outb(val & 0xff, iobase + PCMMIO_AO_LSB_REG);
651 		outb((val >> 8) & 0xff, iobase + PCMMIO_AO_MSB_REG);
652 		outb(cmd | PCMMIO_AO_CMD_WR_CODE_UPDATE,
653 		     iobase + PCMMIO_AO_CMD_REG);
654 
655 		ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
656 		if (ret)
657 			return ret;
658 
659 		s->readback[chan] = val;
660 	}
661 
662 	return insn->n;
663 }
664 
pcmmio_attach(struct comedi_device * dev,struct comedi_devconfig * it)665 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
666 {
667 	struct pcmmio_private *devpriv;
668 	struct comedi_subdevice *s;
669 	int ret;
670 
671 	ret = comedi_request_region(dev, it->options[0], 32);
672 	if (ret)
673 		return ret;
674 
675 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
676 	if (!devpriv)
677 		return -ENOMEM;
678 
679 	spin_lock_init(&devpriv->pagelock);
680 	spin_lock_init(&devpriv->spinlock);
681 
682 	pcmmio_reset(dev);
683 
684 	if (it->options[1]) {
685 		ret = request_irq(it->options[1], interrupt_pcmmio, 0,
686 				  dev->board_name, dev);
687 		if (ret == 0) {
688 			dev->irq = it->options[1];
689 
690 			/* configure the interrupt routing on the board */
691 			outb(PCMMIO_AI_RES_ENA_DIO_RES_ACCESS,
692 			     dev->iobase + PCMMIO_AI_RES_ENA_REG);
693 			outb(PCMMIO_RESOURCE_IRQ(dev->irq),
694 			     dev->iobase + PCMMIO_RESOURCE_REG);
695 		}
696 	}
697 
698 	ret = comedi_alloc_subdevices(dev, 4);
699 	if (ret)
700 		return ret;
701 
702 	/* Analog Input subdevice */
703 	s = &dev->subdevices[0];
704 	s->type		= COMEDI_SUBD_AI;
705 	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_DIFF;
706 	s->n_chan	= 16;
707 	s->maxdata	= 0xffff;
708 	s->range_table	= &pcmmio_ai_ranges;
709 	s->insn_read	= pcmmio_ai_insn_read;
710 
711 	/* initialize the resource enable register by clearing it */
712 	outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
713 	     dev->iobase + PCMMIO_AI_RES_ENA_REG);
714 	outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
715 	     dev->iobase + PCMMIO_AI_RES_ENA_REG + PCMMIO_AI_2ND_ADC_OFFSET);
716 
717 	/* Analog Output subdevice */
718 	s = &dev->subdevices[1];
719 	s->type		= COMEDI_SUBD_AO;
720 	s->subdev_flags	= SDF_READABLE;
721 	s->n_chan	= 8;
722 	s->maxdata	= 0xffff;
723 	s->range_table	= &pcmmio_ao_ranges;
724 	s->insn_write	= pcmmio_ao_insn_write;
725 
726 	ret = comedi_alloc_subdev_readback(s);
727 	if (ret)
728 		return ret;
729 
730 	/* initialize the resource enable register by clearing it */
731 	outb(0, dev->iobase + PCMMIO_AO_RESOURCE_ENA_REG);
732 	outb(0, dev->iobase + PCMMIO_AO_2ND_DAC_OFFSET +
733 		PCMMIO_AO_RESOURCE_ENA_REG);
734 
735 	/* Digital I/O subdevice with interrupt support */
736 	s = &dev->subdevices[2];
737 	s->type		= COMEDI_SUBD_DIO;
738 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
739 	s->n_chan	= 24;
740 	s->maxdata	= 1;
741 	s->len_chanlist	= 1;
742 	s->range_table	= &range_digital;
743 	s->insn_bits	= pcmmio_dio_insn_bits;
744 	s->insn_config	= pcmmio_dio_insn_config;
745 	if (dev->irq) {
746 		dev->read_subdev = s;
747 		s->subdev_flags	|= SDF_CMD_READ | SDF_LSAMPL | SDF_PACKED;
748 		s->len_chanlist	= s->n_chan;
749 		s->cancel	= pcmmio_cancel;
750 		s->do_cmd	= pcmmio_cmd;
751 		s->do_cmdtest	= pcmmio_cmdtest;
752 	}
753 
754 	/* Digital I/O subdevice */
755 	s = &dev->subdevices[3];
756 	s->type		= COMEDI_SUBD_DIO;
757 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
758 	s->n_chan	= 24;
759 	s->maxdata	= 1;
760 	s->range_table	= &range_digital;
761 	s->insn_bits	= pcmmio_dio_insn_bits;
762 	s->insn_config	= pcmmio_dio_insn_config;
763 
764 	return 0;
765 }
766 
767 static struct comedi_driver pcmmio_driver = {
768 	.driver_name	= "pcmmio",
769 	.module		= THIS_MODULE,
770 	.attach		= pcmmio_attach,
771 	.detach		= comedi_legacy_detach,
772 };
773 module_comedi_driver(pcmmio_driver);
774 
775 MODULE_AUTHOR("Comedi https://www.comedi.org");
776 MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
777 MODULE_LICENSE("GPL");
778