2 comedi/drivers/pcmuio.c
3 Driver for Winsystems PC-104 based 48-channel and 96-channel DIO boards.
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2006 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.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
25 Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
26 Author: Calin Culianu <calin@ajvar.org>
27 Updated: Fri, 13 Jan 2006 12:01:01 -0500
30 A driver for the relatively straightforward-to-program PCM-UIO48A and
31 PCM-UIO96A boards from Winsystems. These boards use either one or two
32 (in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
33 This chip is interesting in that each I/O line is individually
34 programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
35 on a per-channel basis). Also, each chip supports edge-triggered
36 interrupts for the first 24 I/O lines. Of course, since the
37 96-channel version of the board has two ASICs, it can detect polarity
38 changes on up to 48 I/O lines. Since this is essentially an (non-PnP)
39 ISA board, I/O Address and IRQ selection are done through jumpers on
40 the board. You need to pass that information to this driver as the
41 first and second comedi_config option, respectively. Note that the
42 48-channel version uses 16 bytes of IO memory and the 96-channel
43 version uses 32-bytes (in case you are worried about conflicts). The
44 48-channel board is split into two 24-channel comedi subdevices.
45 The 96-channel board is split into 4 24-channel DIO subdevices.
47 Note that IRQ support has been added, but it is untested.
49 To use edge-detection IRQ support, pass the IRQs of both ASICS
50 (for the 96 channel version) or just 1 ASIC (for 48-channel version).
51 Then, use use comedi_commands with TRIG_NOW.
52 Your callback will be called each time an edge is triggered, and the data
53 values will be two sample_t's, which should be concatenated to form one
54 32-bit unsigned int. This value is the mask of channels that had
55 edges detected from your channel list. Note that the bits positions
56 in the mask correspond to positions in your chanlist when you specified
57 the command and *not* channel id's!
59 To set the polarity of the edge-detection interrupts pass a nonzero value for
60 either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
61 CR_RANGE and CR_AREF if you want edge-down polarity.
63 In the 48-channel version:
65 On subdev 0, the first 24 channels channels are edge-detect channels.
67 In the 96-channel board you have the collowing channels that can do edge detection:
69 subdev 0, channels 0-24 (first 24 channels of 1st ASIC)
70 subdev 2, channels 0-24 (first 24 channels of 2nd ASIC)
72 Configuration Options:
73 [0] - I/O port base address
74 [1] - IRQ (for first ASIC, or first 24 channels)
75 [2] - IRQ for second ASIC (pcmuio96 only - IRQ for chans 48-72 .. can be the same as first irq!)
78 #include <linux/interrupt.h>
79 #include <linux/slab.h>
81 #include "../comedidev.h"
83 #include "comedi_fc.h"
85 #define CHANS_PER_PORT 8
86 #define PORTS_PER_ASIC 6
87 #define INTR_PORTS_PER_ASIC 3
88 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
89 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
90 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
91 #define INTR_CHANS_PER_ASIC 24
92 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
93 #define MAX_DIO_CHANS (PORTS_PER_ASIC*2*CHANS_PER_PORT)
94 #define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
95 #define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
97 #define ASIC_IOSIZE (0x10)
98 #define PCMUIO48_IOSIZE ASIC_IOSIZE
99 #define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
101 /* Some offsets - these are all in the 16byte IO memory offset from
102 the base address. Note that there is a paging scheme to swap out
103 offsets 0x8-0xA using the PAGELOCK register. See the table below.
105 Register(s) Pages R/W? Description
106 --------------------------------------------------------------
107 REG_PORTx All R/W Read/Write/Configure IO
108 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
109 REG_PAGELOCK All WriteOnly Select a page
110 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
111 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
112 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
114 #define REG_PORT0 0x0
115 #define REG_PORT1 0x1
116 #define REG_PORT2 0x2
117 #define REG_PORT3 0x3
118 #define REG_PORT4 0x4
119 #define REG_PORT5 0x5
120 #define REG_INT_PENDING 0x6
121 #define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page
122 and bits 0-5 are used to 'lock down' a particular
123 port above to make it readonly. */
127 #define REG_ENAB0 0x8
128 #define REG_ENAB1 0x9
129 #define REG_ENAB2 0xA
130 #define REG_INT_ID0 0x8
131 #define REG_INT_ID1 0x9
132 #define REG_INT_ID2 0xA
134 #define NUM_PAGED_REGS 3
136 #define FIRST_PAGED_REG 0x8
137 #define REG_PAGE_BITOFFSET 6
138 #define REG_LOCK_BITOFFSET 0
139 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
140 #define REG_LOCK_MASK ~(REG_PAGE_MASK)
143 #define PAGE_INT_ID 3
146 * Board descriptions for two imaginary boards. Describing the
147 * boards in this way is optional, and completely driver-dependent.
148 * Some drivers use arrays such as this, other do not.
150 struct pcmuio_board {
153 const int num_channels_per_port;
157 /* this structure is for data unique to this subdevice. */
158 struct pcmuio_subdev_private {
159 /* mapping of halfwords (bytes) in port/chanarray to iobase */
160 unsigned long iobases[PORTS_PER_SUBDEV];
162 /* The below is only used for intr subdevices */
164 int asic; /* if non-negative, this subdev has an interrupt asic */
165 int first_chan; /* if nonnegative, the first channel id for
167 int num_asic_chans; /* the number of asic channels in this subdev
168 that have interrutps */
169 int asic_chan; /* if nonnegative, the first channel id with
170 respect to the asic that has interrupts */
171 int enabled_mask; /* subdev-relative channel mask for channels
172 we are interested in */
180 /* this structure is for data unique to this hardware driver. If
181 several hardware drivers keep similar information in this structure,
182 feel free to suggest moving the variable to the struct comedi_device struct. */
183 struct pcmuio_private {
185 unsigned char pagelock; /* current page and lock */
186 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
187 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
189 unsigned long iobase;
193 struct pcmuio_subdev_private *sprivs;
196 #define subpriv ((struct pcmuio_subdev_private *)s->private)
198 /* DIO devices are slightly special. Although it is possible to
199 * implement the insn_read/insn_write interface, it is much more
200 * useful to applications if you implement the insn_bits interface.
201 * This allows packed reading/writing of the DIO channels. The
202 * comedi core can convert between insn_bits and insn_read/write */
203 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
204 struct comedi_subdevice *s,
205 struct comedi_insn *insn, unsigned int *data)
210 reading a 0 means this channel was high
211 writine a 0 sets the channel high
212 reading a 1 means this channel was low
213 writing a 1 means set this channel low
215 Therefore everything is always inverted. */
217 /* The insn data is a mask in data[0] and the new data
218 * in data[1], each channel cooresponding to a bit. */
220 #ifdef DAMMIT_ITS_BROKEN
222 dev_dbg(dev->class_dev, "write mask: %08x data: %08x\n", data[0],
228 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
229 /* address of 8-bit port */
230 unsigned long ioaddr = subpriv->iobases[byte_no],
231 /* bit offset of port in 32-bit doubleword */
232 offset = byte_no * 8;
233 /* this 8-bit port's data */
234 unsigned char byte = 0,
235 /* The write mask for this port (if any) */
236 write_mask_byte = (data[0] >> offset) & 0xff,
237 /* The data byte for this port */
238 data_byte = (data[1] >> offset) & 0xff;
240 byte = inb(ioaddr); /* read all 8-bits for this port */
242 #ifdef DAMMIT_ITS_BROKEN
245 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
246 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
247 offset, ioaddr, (unsigned)byte);
250 if (write_mask_byte) {
251 /* this byte has some write_bits -- so set the output lines */
252 byte &= ~write_mask_byte; /* clear bits for write mask */
253 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
254 /* Write out the new digital output state */
257 #ifdef DAMMIT_ITS_BROKEN
259 dev_dbg(dev->class_dev, "data_out_byte %02x\n", (unsigned)byte);
261 /* save the digital input lines for this byte.. */
262 s->state |= ((unsigned int)byte) << offset;
265 /* now return the DIO lines to data[1] - note they came inverted! */
268 #ifdef DAMMIT_ITS_BROKEN
270 dev_dbg(dev->class_dev, "s->state %08x data_out %08x\n", s->state,
277 /* The input or output configuration of each digital line is
278 * configured by a special insn_config instruction. chanspec
279 * contains the channel to be changed, and data[0] contains the
280 * value COMEDI_INPUT or COMEDI_OUTPUT. */
281 static int pcmuio_dio_insn_config(struct comedi_device *dev,
282 struct comedi_subdevice *s,
283 struct comedi_insn *insn, unsigned int *data)
285 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
287 unsigned long ioaddr;
290 /* Compute ioaddr for this channel */
291 ioaddr = subpriv->iobases[byte_no];
294 writing a 0 an IO channel's bit sets the channel to INPUT
295 and pulls the line high as well
297 writing a 1 to an IO channel's bit pulls the line low
299 All channels are implicitly always in OUTPUT mode -- but when
300 they are high they can be considered to be in INPUT mode..
302 Thus, we only force channels low if the config request was INPUT,
303 otherwise we do nothing to the hardware. */
306 case INSN_CONFIG_DIO_OUTPUT:
307 /* save to io_bits -- don't actually do anything since
308 all input channels are also output channels... */
309 s->io_bits |= 1 << chan;
311 case INSN_CONFIG_DIO_INPUT:
312 /* write a 0 to the actual register representing the channel
313 to set it to 'input'. 0 means "float high". */
315 byte &= ~(1 << bit_no);
316 /**< set input channel to '0' */
318 /* write out byte -- this is the only time we actually affect the
319 hardware as all channels are implicitly output -- but input
320 channels are set to float-high */
323 /* save to io_bits */
324 s->io_bits &= ~(1 << chan);
327 case INSN_CONFIG_DIO_QUERY:
328 /* retrieve from shadow register */
330 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
342 static void switch_page(struct comedi_device *dev, int asic, int page)
344 const struct pcmuio_board *board = comedi_board(dev);
345 struct pcmuio_private *devpriv = dev->private;
347 if (asic < 0 || asic >= board->num_asics)
348 return; /* paranoia */
349 if (page < 0 || page >= NUM_PAGES)
350 return; /* more paranoia */
352 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
353 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
355 /* now write out the shadow register */
356 outb(devpriv->asics[asic].pagelock,
357 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
360 static void init_asics(struct comedi_device *dev)
362 ASIC chip to defaults */
363 const struct pcmuio_board *board = comedi_board(dev);
366 for (asic = 0; asic < board->num_asics; ++asic) {
368 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
370 switch_page(dev, asic, 0); /* switch back to page 0 */
372 /* first, clear all the DIO port bits */
373 for (port = 0; port < PORTS_PER_ASIC; ++port)
374 outb(0, baseaddr + REG_PORT0 + port);
376 /* Next, clear all the paged registers for each page */
377 for (page = 1; page < NUM_PAGES; ++page) {
379 /* now clear all the paged registers */
380 switch_page(dev, asic, page);
381 for (reg = FIRST_PAGED_REG;
382 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
383 outb(0, baseaddr + reg);
386 /* DEBUG set rising edge interrupts on port0 of both asics */
387 /*switch_page(dev, asic, PAGE_POL);
388 outb(0xff, baseaddr + REG_POL0);
389 switch_page(dev, asic, PAGE_ENAB);
390 outb(0xff, baseaddr + REG_ENAB0); */
393 switch_page(dev, asic, 0); /* switch back to default page 0 */
399 static void lock_port(struct comedi_device *dev, int asic, int port)
401 const struct pcmuio_board *board = comedi_board(dev);
402 struct pcmuio_private *devpriv = dev->private;
404 if (asic < 0 || asic >= board->num_asics)
405 return; /* paranoia */
406 if (port < 0 || port >= PORTS_PER_ASIC)
407 return; /* more paranoia */
409 devpriv->asics[asic].pagelock |= 0x1 << port;
410 /* now write out the shadow register */
411 outb(devpriv->asics[asic].pagelock,
412 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
415 static void unlock_port(struct comedi_device *dev, int asic, int port)
417 const struct pcmuio_board *board = comedi_board(dev);
418 struct pcmuio_private *devpriv = dev->private;
420 if (asic < 0 || asic >= board->num_asics)
421 return; /* paranoia */
422 if (port < 0 || port >= PORTS_PER_ASIC)
423 return; /* more paranoia */
424 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
425 /* now write out the shadow register */
426 outb(devpriv->asics[asic].pagelock,
427 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
431 static void pcmuio_stop_intr(struct comedi_device *dev,
432 struct comedi_subdevice *s)
434 int nports, firstport, asic, port;
435 struct pcmuio_private *devpriv = dev->private;
437 asic = subpriv->intr.asic;
439 return; /* not an interrupt subdev */
441 subpriv->intr.enabled_mask = 0;
442 subpriv->intr.active = 0;
443 s->async->inttrig = NULL;
444 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
445 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
446 switch_page(dev, asic, PAGE_ENAB);
447 for (port = firstport; port < firstport + nports; ++port) {
448 /* disable all intrs for this subdev.. */
449 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
453 static irqreturn_t interrupt_pcmuio(int irq, void *d)
456 struct comedi_device *dev = (struct comedi_device *)d;
457 struct pcmuio_private *devpriv = dev->private;
460 for (asic = 0; asic < MAX_ASICS; ++asic) {
461 if (irq == devpriv->asics[asic].irq) {
463 unsigned triggered = 0;
464 unsigned long iobase = devpriv->asics[asic].iobase;
465 /* it is an interrupt for ASIC #asic */
466 unsigned char int_pend;
468 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
471 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
475 for (port = 0; port < INTR_PORTS_PER_ASIC;
477 if (int_pend & (0x1 << port)) {
479 io_lines_with_edges = 0;
480 switch_page(dev, asic,
482 io_lines_with_edges =
486 if (io_lines_with_edges)
487 /* clear pending interrupt */
493 io_lines_with_edges <<
501 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
505 struct comedi_subdevice *s;
506 /* TODO here: dispatch io lines to subdevs with commands.. */
508 ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
509 irq, asic, triggered);
510 for (i = 0; i < dev->n_subdevices; i++) {
511 s = &dev->subdevices[i];
512 if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
516 spin_lock_irqsave(&subpriv->
520 oldevents = s->async->events;
522 if (subpriv->intr.active) {
525 subpriv->intr.asic_chan)
533 subpriv->intr.enabled_mask)
542 async->cmd.chanlist_len;
546 ch = CR_CHAN(s->async->cmd.chanlist[n]);
547 if (mytrig & (1U << ch)) {
551 /* Write the scan to the buffer. */
552 if (comedi_buf_put(s->async, ((short *)&val)[0])
559 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
561 /* Overflow! Stop acquisition!! */
562 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
568 /* Check for end of acquisition. */
569 if (!subpriv->intr.continuous) {
570 /* stop_src == TRIG_COUNT */
571 if (subpriv->intr.stop_count > 0) {
572 subpriv->intr.stop_count--;
573 if (subpriv->intr.stop_count == 0) {
574 s->async->events |= COMEDI_CB_EOA;
575 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
585 spin_unlock_irqrestore
586 (&subpriv->intr.spinlock,
591 comedi_event(dev, s);
602 return IRQ_NONE; /* interrupt from other source */
606 static int pcmuio_start_intr(struct comedi_device *dev,
607 struct comedi_subdevice *s)
609 struct pcmuio_private *devpriv = dev->private;
611 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
612 /* An empty acquisition! */
613 s->async->events |= COMEDI_CB_EOA;
614 subpriv->intr.active = 0;
617 unsigned bits = 0, pol_bits = 0, n;
618 int nports, firstport, asic, port;
619 struct comedi_cmd *cmd = &s->async->cmd;
621 asic = subpriv->intr.asic;
623 return 1; /* not an interrupt
625 subpriv->intr.enabled_mask = 0;
626 subpriv->intr.active = 1;
627 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
628 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
630 for (n = 0; n < cmd->chanlist_len; n++) {
631 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
632 pol_bits |= (CR_AREF(cmd->chanlist[n])
634 chanlist[n]) ? 1U : 0U)
635 << CR_CHAN(cmd->chanlist[n]);
638 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
639 1) << subpriv->intr.first_chan;
640 subpriv->intr.enabled_mask = bits;
642 switch_page(dev, asic, PAGE_ENAB);
643 for (port = firstport; port < firstport + nports; ++port) {
645 bits >> (subpriv->intr.first_chan + (port -
648 pol_bits >> (subpriv->intr.first_chan +
649 (port - firstport) * 8) & 0xff;
650 /* set enab intrs for this subdev.. */
652 devpriv->asics[asic].iobase + REG_ENAB0 + port);
653 switch_page(dev, asic, PAGE_POL);
655 devpriv->asics[asic].iobase + REG_ENAB0 + port);
661 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
665 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
666 if (subpriv->intr.active)
667 pcmuio_stop_intr(dev, s);
668 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
674 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
677 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
678 unsigned int trignum)
686 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
687 s->async->inttrig = NULL;
688 if (subpriv->intr.active)
689 event = pcmuio_start_intr(dev, s);
691 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
694 comedi_event(dev, s);
700 * 'do_cmd' function for an 'INTERRUPT' subdevice.
702 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
704 struct comedi_cmd *cmd = &s->async->cmd;
708 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
709 subpriv->intr.active = 1;
711 /* Set up end of acquisition. */
712 switch (cmd->stop_src) {
714 subpriv->intr.continuous = 0;
715 subpriv->intr.stop_count = cmd->stop_arg;
719 subpriv->intr.continuous = 1;
720 subpriv->intr.stop_count = 0;
724 /* Set up start of acquisition. */
725 switch (cmd->start_src) {
727 s->async->inttrig = pcmuio_inttrig_start_intr;
731 event = pcmuio_start_intr(dev, s);
734 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
737 comedi_event(dev, s);
742 static int pcmuio_cmdtest(struct comedi_device *dev,
743 struct comedi_subdevice *s,
744 struct comedi_cmd *cmd)
748 /* Step 1 : check if triggers are trivially valid */
750 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
751 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
752 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
753 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
754 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
759 /* Step 2a : make sure trigger sources are unique */
761 err |= cfc_check_trigger_is_unique(cmd->start_src);
762 err |= cfc_check_trigger_is_unique(cmd->stop_src);
764 /* Step 2b : and mutually compatible */
769 /* Step 3: check if arguments are trivially valid */
771 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
772 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
773 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
774 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
776 switch (cmd->stop_src) {
778 /* any count allowed */
781 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
790 /* step 4: fix up any arguments */
792 /* if (err) return 4; */
797 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
799 const struct pcmuio_board *board = comedi_board(dev);
800 struct pcmuio_private *devpriv;
801 struct comedi_subdevice *s;
802 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
803 unsigned int irq[MAX_ASICS];
806 irq[0] = it->options[1];
807 irq[1] = it->options[2];
809 ret = comedi_request_region(dev, it->options[0],
810 board->num_asics * ASIC_IOSIZE);
814 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
817 dev->private = devpriv;
819 for (asic = 0; asic < MAX_ASICS; ++asic) {
820 devpriv->asics[asic].num = asic;
821 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
822 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
823 this function when we
825 spin_lock_init(&devpriv->asics[asic].spinlock);
828 chans_left = CHANS_PER_ASIC * board->num_asics;
829 n_subdevs = CALC_N_SUBDEVS(chans_left);
830 devpriv->sprivs = kcalloc(n_subdevs,
831 sizeof(struct pcmuio_subdev_private),
833 if (!devpriv->sprivs)
836 ret = comedi_alloc_subdevices(dev, n_subdevs);
842 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
845 s = &dev->subdevices[sdev_no];
846 s->private = &devpriv->sprivs[sdev_no];
848 s->range_table = &range_digital;
849 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
850 s->type = COMEDI_SUBD_DIO;
851 s->insn_bits = pcmuio_dio_insn_bits;
852 s->insn_config = pcmuio_dio_insn_config;
853 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
854 subpriv->intr.asic = -1;
855 subpriv->intr.first_chan = -1;
856 subpriv->intr.asic_chan = -1;
857 subpriv->intr.num_asic_chans = -1;
858 subpriv->intr.active = 0;
861 /* save the ioport address for each 'port' of 8 channels in the
863 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
864 if (port >= PORTS_PER_ASIC) {
869 subpriv->iobases[byte_no] =
870 devpriv->asics[asic].iobase + port;
872 if (thisasic_chanct <
873 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
874 && subpriv->intr.asic < 0) {
875 /* this is an interrupt subdevice, so setup the struct */
876 subpriv->intr.asic = asic;
877 subpriv->intr.active = 0;
878 subpriv->intr.stop_count = 0;
879 subpriv->intr.first_chan = byte_no * 8;
880 subpriv->intr.asic_chan = thisasic_chanct;
881 subpriv->intr.num_asic_chans =
882 s->n_chan - subpriv->intr.first_chan;
883 dev->read_subdev = s;
884 s->subdev_flags |= SDF_CMD_READ;
885 s->cancel = pcmuio_cancel;
886 s->do_cmd = pcmuio_cmd;
887 s->do_cmdtest = pcmuio_cmdtest;
888 s->len_chanlist = subpriv->intr.num_asic_chans;
890 thisasic_chanct += CHANS_PER_PORT;
892 spin_lock_init(&subpriv->intr.spinlock);
894 chans_left -= s->n_chan;
897 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
903 init_asics(dev); /* clear out all the registers, basically */
905 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
907 && request_irq(irq[asic], interrupt_pcmuio,
908 IRQF_SHARED, board->name, dev)) {
910 /* unroll the allocated irqs.. */
911 for (i = asic - 1; i >= 0; --i) {
912 free_irq(irq[i], dev);
913 devpriv->asics[i].irq = irq[i] = 0;
917 devpriv->asics[asic].irq = irq[asic];
921 dev_dbg(dev->class_dev, "irq: %u\n", irq[0]);
922 if (irq[1] && board->num_asics == 2)
923 dev_dbg(dev->class_dev, "second ASIC irq: %u\n",
926 dev_dbg(dev->class_dev, "(IRQ mode disabled)\n");
933 static void pcmuio_detach(struct comedi_device *dev)
935 struct pcmuio_private *devpriv = dev->private;
938 for (i = 0; i < MAX_ASICS; ++i) {
939 if (devpriv->asics[i].irq)
940 free_irq(devpriv->asics[i].irq, dev);
942 if (devpriv && devpriv->sprivs)
943 kfree(devpriv->sprivs);
944 comedi_legacy_detach(dev);
947 static const struct pcmuio_board pcmuio_boards[] = {
959 static struct comedi_driver pcmuio_driver = {
960 .driver_name = "pcmuio",
961 .module = THIS_MODULE,
962 .attach = pcmuio_attach,
963 .detach = pcmuio_detach,
964 .board_name = &pcmuio_boards[0].name,
965 .offset = sizeof(struct pcmuio_board),
966 .num_names = ARRAY_SIZE(pcmuio_boards),
968 module_comedi_driver(pcmuio_driver);
970 MODULE_AUTHOR("Comedi http://www.comedi.org");
971 MODULE_DESCRIPTION("Comedi low-level driver");
972 MODULE_LICENSE("GPL");