1 // SPDX-License-Identifier: GPL-2.0+
4 * Driver for Winsystems PC-104 based multifunction IO board.
6 * COMEDI - Linux Control and Measurement Device Interface
7 * Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
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
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:
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)
30 * Synchronous reads and writes are the only things implemented for analog
31 * input and output. The hardware itself can do streaming acquisition, etc.
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.
37 * Also note that this interrupt support is untested.
39 * A few words about edge-detection IRQ support (commands on DIO):
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.
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.
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!
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.
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)
66 #include <linux/module.h>
67 #include <linux/interrupt.h>
68 #include <linux/slab.h>
70 #include "../comedidev.h"
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
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
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
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))
158 static const struct comedi_lrange pcmmio_ai_ranges = {
167 static const struct comedi_lrange pcmmio_ao_ranges = {
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;
185 static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
188 struct pcmmio_private *devpriv = dev->private;
189 unsigned long iobase = dev->iobase;
192 spin_lock_irqsave(&devpriv->pagelock, flags);
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));
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));
204 spin_unlock_irqrestore(&devpriv->pagelock, flags);
207 static unsigned int pcmmio_dio_read(struct comedi_device *dev,
210 struct pcmmio_private *devpriv = dev->private;
211 unsigned long iobase = dev->iobase;
215 spin_lock_irqsave(&devpriv->pagelock, flags);
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);
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);
227 spin_unlock_irqrestore(&devpriv->pagelock, flags);
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.
242 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
243 struct comedi_subdevice *s,
244 struct comedi_insn *insn,
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;
253 mask = comedi_dio_update_state(s, data);
256 * Outputs are inverted, invert the state and
257 * update the channels.
259 * The s->io_bits mask makes sure the input channels
260 * are '0' so that the outputs pins stay in a high
263 val = ~s->state & chanmask;
265 pcmmio_dio_write(dev, val, 0, port);
268 /* get inverted state of the channels from the port */
269 val = pcmmio_dio_read(dev, 0, port);
271 /* return the true state of the channels */
272 data[1] = ~val & chanmask;
277 static int pcmmio_dio_insn_config(struct comedi_device *dev,
278 struct comedi_subdevice *s,
279 struct comedi_insn *insn,
282 /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
283 int port = s->index == 2 ? 0 : 3;
286 ret = comedi_dio_insn_config(dev, s, insn, data, 0);
290 if (data[0] == INSN_CONFIG_DIO_INPUT)
291 pcmmio_dio_write(dev, s->io_bits, 0, port);
296 static void pcmmio_reset(struct comedi_device *dev)
298 /* Clear all the DIO port bits */
299 pcmmio_dio_write(dev, 0, 0, 0);
300 pcmmio_dio_write(dev, 0, 0, 3);
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);
308 /* devpriv->spinlock is already locked */
309 static void pcmmio_stop_intr(struct comedi_device *dev,
310 struct comedi_subdevice *s)
312 struct pcmmio_private *devpriv = dev->private;
314 devpriv->enabled_mask = 0;
316 s->async->inttrig = NULL;
318 /* disable all dio interrupts */
319 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
322 static void pcmmio_handle_dio_intr(struct comedi_device *dev,
323 struct comedi_subdevice *s,
324 unsigned int triggered)
326 struct pcmmio_private *devpriv = dev->private;
327 struct comedi_cmd *cmd = &s->async->cmd;
328 unsigned int val = 0;
332 spin_lock_irqsave(&devpriv->spinlock, flags);
334 if (!devpriv->active)
337 if (!(triggered & devpriv->enabled_mask))
340 for (i = 0; i < cmd->chanlist_len; i++) {
341 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
343 if (triggered & (1 << chan))
347 comedi_buf_write_samples(s, &val, 1);
349 if (cmd->stop_src == TRIG_COUNT &&
350 s->async->scans_done >= cmd->stop_arg)
351 s->async->events |= COMEDI_CB_EOA;
354 spin_unlock_irqrestore(&devpriv->spinlock, flags);
356 comedi_handle_events(dev, s);
359 static irqreturn_t interrupt_pcmmio(int irq, void *d)
361 struct comedi_device *dev = d;
362 struct comedi_subdevice *s = dev->read_subdev;
363 unsigned int triggered;
364 unsigned char int_pend;
366 /* are there any interrupts pending */
367 int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
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);
375 pcmmio_handle_dio_intr(dev, s, triggered);
380 /* devpriv->spinlock is already locked */
381 static void pcmmio_start_intr(struct comedi_device *dev,
382 struct comedi_subdevice *s)
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;
390 devpriv->enabled_mask = 0;
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);
400 pol_bits |= (((aref || range) ? 1 : 0) << chan);
403 bits &= ((1 << s->n_chan) - 1);
404 devpriv->enabled_mask = bits;
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);
411 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
413 struct pcmmio_private *devpriv = dev->private;
416 spin_lock_irqsave(&devpriv->spinlock, flags);
418 pcmmio_stop_intr(dev, s);
419 spin_unlock_irqrestore(&devpriv->spinlock, flags);
424 static int pcmmio_inttrig_start_intr(struct comedi_device *dev,
425 struct comedi_subdevice *s,
426 unsigned int trig_num)
428 struct pcmmio_private *devpriv = dev->private;
429 struct comedi_cmd *cmd = &s->async->cmd;
432 if (trig_num != cmd->start_arg)
435 spin_lock_irqsave(&devpriv->spinlock, flags);
436 s->async->inttrig = NULL;
438 pcmmio_start_intr(dev, s);
439 spin_unlock_irqrestore(&devpriv->spinlock, flags);
445 * 'do_cmd' function for an 'INTERRUPT' subdevice.
447 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
449 struct pcmmio_private *devpriv = dev->private;
450 struct comedi_cmd *cmd = &s->async->cmd;
453 spin_lock_irqsave(&devpriv->spinlock, flags);
456 /* Set up start of acquisition. */
457 if (cmd->start_src == TRIG_INT)
458 s->async->inttrig = pcmmio_inttrig_start_intr;
460 pcmmio_start_intr(dev, s);
462 spin_unlock_irqrestore(&devpriv->spinlock, flags);
467 static int pcmmio_cmdtest(struct comedi_device *dev,
468 struct comedi_subdevice *s,
469 struct comedi_cmd *cmd)
473 /* Step 1 : check if triggers are trivially valid */
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);
484 /* Step 2a : make sure trigger sources are unique */
486 err |= comedi_check_trigger_is_unique(cmd->start_src);
487 err |= comedi_check_trigger_is_unique(cmd->stop_src);
489 /* Step 2b : and mutually compatible */
494 /* Step 3: check if arguments are trivially valid */
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,
502 if (cmd->stop_src == TRIG_COUNT)
503 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
505 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
510 /* step 4: fix up any arguments */
512 /* if (err) return 4; */
517 static int pcmmio_ai_eoc(struct comedi_device *dev,
518 struct comedi_subdevice *s,
519 struct comedi_insn *insn,
520 unsigned long context)
522 unsigned char status;
524 status = inb(dev->iobase + PCMMIO_AI_STATUS_REG);
525 if (status & PCMMIO_AI_STATUS_DATA_READY)
530 static int pcmmio_ai_insn_read(struct comedi_device *dev,
531 struct comedi_subdevice *s,
532 struct comedi_insn *insn,
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;
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.
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.
561 iobase += PCMMIO_AI_2ND_ADC_OFFSET;
564 if (aref == AREF_GROUND)
565 cmd |= PCMMIO_AI_CMD_SE;
567 cmd |= PCMMIO_AI_CMD_ODD_CHAN;
568 cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2);
569 cmd |= PCMMIO_AI_CMD_RANGE(range);
571 outb(cmd, iobase + PCMMIO_AI_CMD_REG);
573 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
577 val = inb(iobase + PCMMIO_AI_LSB_REG);
578 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
580 for (i = 0; i < insn->n; i++) {
581 outb(cmd, iobase + PCMMIO_AI_CMD_REG);
583 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
587 val = inb(iobase + PCMMIO_AI_LSB_REG);
588 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
590 /* bipolar data is two's complement */
591 if (comedi_range_is_bipolar(s, range))
592 val = comedi_offset_munge(s, val);
600 static int pcmmio_ao_eoc(struct comedi_device *dev,
601 struct comedi_subdevice *s,
602 struct comedi_insn *insn,
603 unsigned long context)
605 unsigned char status;
607 status = inb(dev->iobase + PCMMIO_AO_STATUS_REG);
608 if (status & PCMMIO_AO_STATUS_DATA_READY)
613 static int pcmmio_ao_insn_write(struct comedi_device *dev,
614 struct comedi_subdevice *s,
615 struct comedi_insn *insn,
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;
626 * The PCM-MIO has two Linear Tech LTC2704 DAC devices. Each device
627 * is a 4-channel converter with software-selectable output range.
631 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4);
632 iobase += PCMMIO_AO_2ND_DAC_OFFSET;
634 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan);
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);
642 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
646 for (i = 0; i < insn->n; i++) {
647 unsigned int val = data[i];
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);
655 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
659 s->readback[chan] = val;
665 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
667 struct pcmmio_private *devpriv;
668 struct comedi_subdevice *s;
671 ret = comedi_request_region(dev, it->options[0], 32);
675 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
679 spin_lock_init(&devpriv->pagelock);
680 spin_lock_init(&devpriv->spinlock);
684 if (it->options[1]) {
685 ret = request_irq(it->options[1], interrupt_pcmmio, 0,
686 dev->board_name, dev);
688 dev->irq = it->options[1];
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);
698 ret = comedi_alloc_subdevices(dev, 4);
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;
708 s->range_table = &pcmmio_ai_ranges;
709 s->insn_read = pcmmio_ai_insn_read;
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);
717 /* Analog Output subdevice */
718 s = &dev->subdevices[1];
719 s->type = COMEDI_SUBD_AO;
720 s->subdev_flags = SDF_READABLE;
723 s->range_table = &pcmmio_ao_ranges;
724 s->insn_write = pcmmio_ao_insn_write;
726 ret = comedi_alloc_subdev_readback(s);
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);
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;
742 s->range_table = &range_digital;
743 s->insn_bits = pcmmio_dio_insn_bits;
744 s->insn_config = pcmmio_dio_insn_config;
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;
754 /* Digital I/O subdevice */
755 s = &dev->subdevices[3];
756 s->type = COMEDI_SUBD_DIO;
757 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
760 s->range_table = &range_digital;
761 s->insn_bits = pcmmio_dio_insn_bits;
762 s->insn_config = pcmmio_dio_insn_config;
767 static struct comedi_driver pcmmio_driver = {
768 .driver_name = "pcmmio",
769 .module = THIS_MODULE,
770 .attach = pcmmio_attach,
771 .detach = comedi_legacy_detach,
773 module_comedi_driver(pcmmio_driver);
775 MODULE_AUTHOR("Comedi https://www.comedi.org");
776 MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
777 MODULE_LICENSE("GPL");