3 * Driver for Winsystems PC-104 based multifunction IO board.
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
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.
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.
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
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:
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)
39 * Synchronous reads and writes are the only things implemented for analog
40 * input and output. The hardware itself can do streaming acquisition, etc.
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.
46 * Also note that this interrupt support is untested.
48 * A few words about edge-detection IRQ support (commands on DIO):
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.
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.
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!
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.
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)
75 #include <linux/module.h>
76 #include <linux/interrupt.h>
77 #include <linux/slab.h>
79 #include "../comedidev.h"
81 #include "comedi_fc.h"
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
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
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
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))
169 static const struct comedi_lrange pcmmio_ai_ranges = {
178 static const struct comedi_lrange pcmmio_ao_ranges = {
189 struct 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;
197 static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
200 struct pcmmio_private *devpriv = dev->private;
201 unsigned long iobase = dev->iobase;
204 spin_lock_irqsave(&devpriv->pagelock, flags);
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));
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));
216 spin_unlock_irqrestore(&devpriv->pagelock, flags);
219 static unsigned int pcmmio_dio_read(struct comedi_device *dev,
222 struct pcmmio_private *devpriv = dev->private;
223 unsigned long iobase = dev->iobase;
227 spin_lock_irqsave(&devpriv->pagelock, flags);
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);
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);
239 spin_unlock_irqrestore(&devpriv->pagelock, flags);
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.
254 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
255 struct comedi_subdevice *s,
256 struct comedi_insn *insn,
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;
265 mask = comedi_dio_update_state(s, data);
268 * Outputs are inverted, invert the state and
269 * update the channels.
271 * The s->io_bits mask makes sure the input channels
272 * are '0' so that the outputs pins stay in a high
275 val = ~s->state & chanmask;
277 pcmmio_dio_write(dev, val, 0, port);
280 /* get inverted state of the channels from the port */
281 val = pcmmio_dio_read(dev, 0, port);
283 /* return the true state of the channels */
284 data[1] = ~val & chanmask;
289 static int pcmmio_dio_insn_config(struct comedi_device *dev,
290 struct comedi_subdevice *s,
291 struct comedi_insn *insn,
294 /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
295 int port = s->index == 2 ? 0 : 3;
298 ret = comedi_dio_insn_config(dev, s, insn, data, 0);
302 if (data[0] == INSN_CONFIG_DIO_INPUT)
303 pcmmio_dio_write(dev, s->io_bits, 0, port);
308 static void pcmmio_reset(struct comedi_device *dev)
310 /* Clear all the DIO port bits */
311 pcmmio_dio_write(dev, 0, 0, 0);
312 pcmmio_dio_write(dev, 0, 0, 3);
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);
320 /* devpriv->spinlock is already locked */
321 static void pcmmio_stop_intr(struct comedi_device *dev,
322 struct comedi_subdevice *s)
324 struct pcmmio_private *devpriv = dev->private;
326 devpriv->enabled_mask = 0;
328 s->async->inttrig = NULL;
330 /* disable all dio interrupts */
331 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
334 static void pcmmio_handle_dio_intr(struct comedi_device *dev,
335 struct comedi_subdevice *s,
336 unsigned int triggered)
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;
345 spin_lock_irqsave(&devpriv->spinlock, flags);
347 if (!devpriv->active)
350 if (!(triggered & devpriv->enabled_mask))
353 for (i = 0; i < cmd->chanlist_len; i++) {
354 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
356 if (triggered & (1 << chan))
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);
365 /* Overflow! Stop acquisition!! */
366 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
367 pcmmio_stop_intr(dev, s);
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);
381 spin_unlock_irqrestore(&devpriv->spinlock, flags);
383 if (oldevents != s->async->events)
384 comedi_event(dev, s);
387 static irqreturn_t interrupt_pcmmio(int irq, void *d)
389 struct comedi_device *dev = d;
390 struct comedi_subdevice *s = dev->read_subdev;
391 unsigned int triggered;
392 unsigned char int_pend;
394 /* are there any interrupts pending */
395 int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
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);
403 pcmmio_handle_dio_intr(dev, s, triggered);
408 /* devpriv->spinlock is already locked */
409 static void pcmmio_start_intr(struct comedi_device *dev,
410 struct comedi_subdevice *s)
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;
418 devpriv->enabled_mask = 0;
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);
428 pol_bits |= (((aref || range) ? 1 : 0) << chan);
431 bits &= ((1 << s->n_chan) - 1);
432 devpriv->enabled_mask = bits;
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);
439 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
441 struct pcmmio_private *devpriv = dev->private;
444 spin_lock_irqsave(&devpriv->spinlock, flags);
446 pcmmio_stop_intr(dev, s);
447 spin_unlock_irqrestore(&devpriv->spinlock, flags);
452 static int pcmmio_inttrig_start_intr(struct comedi_device *dev,
453 struct comedi_subdevice *s,
454 unsigned int trig_num)
456 struct pcmmio_private *devpriv = dev->private;
457 struct comedi_cmd *cmd = &s->async->cmd;
460 if (trig_num != cmd->start_arg)
463 spin_lock_irqsave(&devpriv->spinlock, flags);
464 s->async->inttrig = NULL;
466 pcmmio_start_intr(dev, s);
467 spin_unlock_irqrestore(&devpriv->spinlock, flags);
473 * 'do_cmd' function for an 'INTERRUPT' subdevice.
475 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
477 struct pcmmio_private *devpriv = dev->private;
478 struct comedi_cmd *cmd = &s->async->cmd;
481 spin_lock_irqsave(&devpriv->spinlock, flags);
484 devpriv->stop_count = cmd->stop_arg;
486 /* Set up start of acquisition. */
487 if (cmd->start_src == TRIG_INT)
488 s->async->inttrig = pcmmio_inttrig_start_intr;
490 pcmmio_start_intr(dev, s);
492 spin_unlock_irqrestore(&devpriv->spinlock, flags);
497 static int pcmmio_cmdtest(struct comedi_device *dev,
498 struct comedi_subdevice *s,
499 struct comedi_cmd *cmd)
503 /* Step 1 : check if triggers are trivially valid */
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);
514 /* Step 2a : make sure trigger sources are unique */
516 err |= cfc_check_trigger_is_unique(cmd->start_src);
517 err |= cfc_check_trigger_is_unique(cmd->stop_src);
519 /* Step 2b : and mutually compatible */
524 /* Step 3: check if arguments are trivially valid */
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);
531 if (cmd->stop_src == TRIG_COUNT)
532 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
534 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
539 /* step 4: fix up any arguments */
541 /* if (err) return 4; */
546 static int pcmmio_ai_eoc(struct comedi_device *dev,
547 struct comedi_subdevice *s,
548 struct comedi_insn *insn,
549 unsigned long context)
551 unsigned char status;
553 status = inb(dev->iobase + PCMMIO_AI_STATUS_REG);
554 if (status & PCMMIO_AI_STATUS_DATA_READY)
559 static int pcmmio_ai_insn_read(struct comedi_device *dev,
560 struct comedi_subdevice *s,
561 struct comedi_insn *insn,
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;
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.
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.
590 iobase += PCMMIO_AI_2ND_ADC_OFFSET;
593 if (aref == AREF_GROUND)
594 cmd |= PCMMIO_AI_CMD_SE;
596 cmd |= PCMMIO_AI_CMD_ODD_CHAN;
597 cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2);
598 cmd |= PCMMIO_AI_CMD_RANGE(range);
600 outb(cmd, iobase + PCMMIO_AI_CMD_REG);
602 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
606 val = inb(iobase + PCMMIO_AI_LSB_REG);
607 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
609 for (i = 0; i < insn->n; i++) {
610 outb(cmd, iobase + PCMMIO_AI_CMD_REG);
612 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
616 val = inb(iobase + PCMMIO_AI_LSB_REG);
617 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
619 /* bipolar data is two's complement */
620 if (comedi_range_is_bipolar(s, range))
621 val = comedi_offset_munge(s, val);
629 static int pcmmio_ao_eoc(struct comedi_device *dev,
630 struct comedi_subdevice *s,
631 struct comedi_insn *insn,
632 unsigned long context)
634 unsigned char status;
636 status = inb(dev->iobase + PCMMIO_AO_STATUS_REG);
637 if (status & PCMMIO_AO_STATUS_DATA_READY)
642 static int pcmmio_ao_insn_write(struct comedi_device *dev,
643 struct comedi_subdevice *s,
644 struct comedi_insn *insn,
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;
655 * The PCM-MIO has two Linear Tech LTC2704 DAC devices. Each device
656 * is a 4-channel converter with software-selectable output range.
660 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4);
661 iobase += PCMMIO_AO_2ND_DAC_OFFSET;
663 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan);
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);
671 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
675 for (i = 0; i < insn->n; i++) {
676 unsigned int val = data[i];
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);
684 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
688 s->readback[chan] = val;
694 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
696 struct pcmmio_private *devpriv;
697 struct comedi_subdevice *s;
700 ret = comedi_request_region(dev, it->options[0], 32);
704 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
708 spin_lock_init(&devpriv->pagelock);
709 spin_lock_init(&devpriv->spinlock);
713 if (it->options[1]) {
714 ret = request_irq(it->options[1], interrupt_pcmmio, 0,
715 dev->board_name, dev);
717 dev->irq = it->options[1];
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);
727 ret = comedi_alloc_subdevices(dev, 4);
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;
737 s->range_table = &pcmmio_ai_ranges;
738 s->insn_read = pcmmio_ai_insn_read;
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);
746 /* Analog Output subdevice */
747 s = &dev->subdevices[1];
748 s->type = COMEDI_SUBD_AO;
749 s->subdev_flags = SDF_READABLE;
752 s->range_table = &pcmmio_ao_ranges;
753 s->insn_write = pcmmio_ao_insn_write;
754 s->insn_read = comedi_readback_insn_read;
756 ret = comedi_alloc_subdev_readback(s);
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);
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;
772 s->range_table = &range_digital;
773 s->insn_bits = pcmmio_dio_insn_bits;
774 s->insn_config = pcmmio_dio_insn_config;
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;
784 /* Digital I/O subdevice */
785 s = &dev->subdevices[3];
786 s->type = COMEDI_SUBD_DIO;
787 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
790 s->range_table = &range_digital;
791 s->insn_bits = pcmmio_dio_insn_bits;
792 s->insn_config = pcmmio_dio_insn_config;
797 static struct comedi_driver pcmmio_driver = {
798 .driver_name = "pcmmio",
799 .module = THIS_MODULE,
800 .attach = pcmmio_attach,
801 .detach = comedi_legacy_detach,
803 module_comedi_driver(pcmmio_driver);
805 MODULE_AUTHOR("Comedi http://www.comedi.org");
806 MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
807 MODULE_LICENSE("GPL");