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