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>
80 #include "../comedidev.h"
81 #include "pcm_common.h"
83 #include <linux/pci.h> /* for PCI devices */
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 SDEV_NO ((int)(s - dev->subdevices))
96 #define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
98 #define ASIC_IOSIZE (0x10)
99 #define PCMUIO48_IOSIZE ASIC_IOSIZE
100 #define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
102 /* Some offsets - these are all in the 16byte IO memory offset from
103 the base address. Note that there is a paging scheme to swap out
104 offsets 0x8-0xA using the PAGELOCK register. See the table below.
106 Register(s) Pages R/W? Description
107 --------------------------------------------------------------
108 REG_PORTx All R/W Read/Write/Configure IO
109 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
110 REG_PAGELOCK All WriteOnly Select a page
111 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
112 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
113 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
115 #define REG_PORT0 0x0
116 #define REG_PORT1 0x1
117 #define REG_PORT2 0x2
118 #define REG_PORT3 0x3
119 #define REG_PORT4 0x4
120 #define REG_PORT5 0x5
121 #define REG_INT_PENDING 0x6
122 #define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page
123 and bits 0-5 are used to 'lock down' a particular
124 port above to make it readonly. */
128 #define REG_ENAB0 0x8
129 #define REG_ENAB1 0x9
130 #define REG_ENAB2 0xA
131 #define REG_INT_ID0 0x8
132 #define REG_INT_ID1 0x9
133 #define REG_INT_ID2 0xA
135 #define NUM_PAGED_REGS 3
137 #define FIRST_PAGED_REG 0x8
138 #define REG_PAGE_BITOFFSET 6
139 #define REG_LOCK_BITOFFSET 0
140 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
141 #define REG_LOCK_MASK ~(REG_PAGE_MASK)
144 #define PAGE_INT_ID 3
147 * Board descriptions for two imaginary boards. Describing the
148 * boards in this way is optional, and completely driver-dependent.
149 * Some drivers use arrays such as this, other do not.
151 struct pcmuio_board {
154 const int num_channels_per_port;
158 /* this structure is for data unique to this subdevice. */
159 struct pcmuio_subdev_private {
160 /* mapping of halfwords (bytes) in port/chanarray to iobase */
161 unsigned long iobases[PORTS_PER_SUBDEV];
163 /* The below is only used for intr subdevices */
165 int asic; /* if non-negative, this subdev has an interrupt asic */
166 int first_chan; /* if nonnegative, the first channel id for
168 int num_asic_chans; /* the number of asic channels in this subdev
169 that have interrutps */
170 int asic_chan; /* if nonnegative, the first channel id with
171 respect to the asic that has interrupts */
172 int enabled_mask; /* subdev-relative channel mask for channels
173 we are interested in */
181 /* this structure is for data unique to this hardware driver. If
182 several hardware drivers keep similar information in this structure,
183 feel free to suggest moving the variable to the struct comedi_device struct. */
184 struct pcmuio_private {
186 unsigned char pagelock; /* current page and lock */
187 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
188 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
190 unsigned long iobase;
194 struct pcmuio_subdev_private *sprivs;
198 * most drivers define the following macro to make it easy to
199 * access the private structure.
201 #define devpriv ((struct pcmuio_private *)dev->private)
202 #define subpriv ((struct pcmuio_subdev_private *)s->private)
204 /* DIO devices are slightly special. Although it is possible to
205 * implement the insn_read/insn_write interface, it is much more
206 * useful to applications if you implement the insn_bits interface.
207 * This allows packed reading/writing of the DIO channels. The
208 * comedi core can convert between insn_bits and insn_read/write */
209 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
210 struct comedi_subdevice *s,
211 struct comedi_insn *insn, unsigned int *data)
216 reading a 0 means this channel was high
217 writine a 0 sets the channel high
218 reading a 1 means this channel was low
219 writing a 1 means set this channel low
221 Therefore everything is always inverted. */
223 /* The insn data is a mask in data[0] and the new data
224 * in data[1], each channel cooresponding to a bit. */
226 #ifdef DAMMIT_ITS_BROKEN
228 dev_dbg(dev->class_dev, "write mask: %08x data: %08x\n", data[0],
234 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
235 /* address of 8-bit port */
236 unsigned long ioaddr = subpriv->iobases[byte_no],
237 /* bit offset of port in 32-bit doubleword */
238 offset = byte_no * 8;
239 /* this 8-bit port's data */
240 unsigned char byte = 0,
241 /* The write mask for this port (if any) */
242 write_mask_byte = (data[0] >> offset) & 0xff,
243 /* The data byte for this port */
244 data_byte = (data[1] >> offset) & 0xff;
246 byte = inb(ioaddr); /* read all 8-bits for this port */
248 #ifdef DAMMIT_ITS_BROKEN
251 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
252 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
253 offset, ioaddr, (unsigned)byte);
256 if (write_mask_byte) {
257 /* this byte has some write_bits -- so set the output lines */
258 byte &= ~write_mask_byte; /* clear bits for write mask */
259 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
260 /* Write out the new digital output state */
263 #ifdef DAMMIT_ITS_BROKEN
265 dev_dbg(dev->class_dev, "data_out_byte %02x\n", (unsigned)byte);
267 /* save the digital input lines for this byte.. */
268 s->state |= ((unsigned int)byte) << offset;
271 /* now return the DIO lines to data[1] - note they came inverted! */
274 #ifdef DAMMIT_ITS_BROKEN
276 dev_dbg(dev->class_dev, "s->state %08x data_out %08x\n", s->state,
283 /* The input or output configuration of each digital line is
284 * configured by a special insn_config instruction. chanspec
285 * contains the channel to be changed, and data[0] contains the
286 * value COMEDI_INPUT or COMEDI_OUTPUT. */
287 static int pcmuio_dio_insn_config(struct comedi_device *dev,
288 struct comedi_subdevice *s,
289 struct comedi_insn *insn, unsigned int *data)
291 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
293 unsigned long ioaddr;
296 /* Compute ioaddr for this channel */
297 ioaddr = subpriv->iobases[byte_no];
300 writing a 0 an IO channel's bit sets the channel to INPUT
301 and pulls the line high as well
303 writing a 1 to an IO channel's bit pulls the line low
305 All channels are implicitly always in OUTPUT mode -- but when
306 they are high they can be considered to be in INPUT mode..
308 Thus, we only force channels low if the config request was INPUT,
309 otherwise we do nothing to the hardware. */
312 case INSN_CONFIG_DIO_OUTPUT:
313 /* save to io_bits -- don't actually do anything since
314 all input channels are also output channels... */
315 s->io_bits |= 1 << chan;
317 case INSN_CONFIG_DIO_INPUT:
318 /* write a 0 to the actual register representing the channel
319 to set it to 'input'. 0 means "float high". */
321 byte &= ~(1 << bit_no);
322 /**< set input channel to '0' */
324 /* write out byte -- this is the only time we actually affect the
325 hardware as all channels are implicitly output -- but input
326 channels are set to float-high */
329 /* save to io_bits */
330 s->io_bits &= ~(1 << chan);
333 case INSN_CONFIG_DIO_QUERY:
334 /* retrieve from shadow register */
336 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
348 static void switch_page(struct comedi_device *dev, int asic, int page)
350 const struct pcmuio_board *board = comedi_board(dev);
352 if (asic < 0 || asic >= board->num_asics)
353 return; /* paranoia */
354 if (page < 0 || page >= NUM_PAGES)
355 return; /* more paranoia */
357 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
358 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
360 /* now write out the shadow register */
361 outb(devpriv->asics[asic].pagelock,
362 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
365 static void init_asics(struct comedi_device *dev)
367 ASIC chip to defaults */
368 const struct pcmuio_board *board = comedi_board(dev);
371 for (asic = 0; asic < board->num_asics; ++asic) {
373 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
375 switch_page(dev, asic, 0); /* switch back to page 0 */
377 /* first, clear all the DIO port bits */
378 for (port = 0; port < PORTS_PER_ASIC; ++port)
379 outb(0, baseaddr + REG_PORT0 + port);
381 /* Next, clear all the paged registers for each page */
382 for (page = 1; page < NUM_PAGES; ++page) {
384 /* now clear all the paged registers */
385 switch_page(dev, asic, page);
386 for (reg = FIRST_PAGED_REG;
387 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
388 outb(0, baseaddr + reg);
391 /* DEBUG set rising edge interrupts on port0 of both asics */
392 /*switch_page(dev, asic, PAGE_POL);
393 outb(0xff, baseaddr + REG_POL0);
394 switch_page(dev, asic, PAGE_ENAB);
395 outb(0xff, baseaddr + REG_ENAB0); */
398 switch_page(dev, asic, 0); /* switch back to default page 0 */
404 static void lock_port(struct comedi_device *dev, int asic, int port)
406 const struct pcmuio_board *board = comedi_board(dev);
408 if (asic < 0 || asic >= board->num_asics)
409 return; /* paranoia */
410 if (port < 0 || port >= PORTS_PER_ASIC)
411 return; /* more paranoia */
413 devpriv->asics[asic].pagelock |= 0x1 << port;
414 /* now write out the shadow register */
415 outb(devpriv->asics[asic].pagelock,
416 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
419 static void unlock_port(struct comedi_device *dev, int asic, int port)
421 const struct pcmuio_board *board = comedi_board(dev);
423 if (asic < 0 || asic >= board->num_asics)
424 return; /* paranoia */
425 if (port < 0 || port >= PORTS_PER_ASIC)
426 return; /* more paranoia */
427 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
428 /* now write out the shadow register */
429 outb(devpriv->asics[asic].pagelock,
430 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
434 static void pcmuio_stop_intr(struct comedi_device *dev,
435 struct comedi_subdevice *s)
437 int nports, firstport, asic, port;
439 asic = subpriv->intr.asic;
441 return; /* not an interrupt subdev */
443 subpriv->intr.enabled_mask = 0;
444 subpriv->intr.active = 0;
445 s->async->inttrig = 0;
446 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
447 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
448 switch_page(dev, asic, PAGE_ENAB);
449 for (port = firstport; port < firstport + nports; ++port) {
450 /* disable all intrs for this subdev.. */
451 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
455 static irqreturn_t interrupt_pcmuio(int irq, void *d)
458 struct comedi_device *dev = (struct comedi_device *)d;
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 (s = dev->subdevices;
511 s < dev->subdevices + dev->n_subdevices;
513 if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
517 spin_lock_irqsave(&subpriv->
521 oldevents = s->async->events;
523 if (subpriv->intr.active) {
526 subpriv->intr.asic_chan)
534 subpriv->intr.enabled_mask)
543 async->cmd.chanlist_len;
547 ch = CR_CHAN(s->async->cmd.chanlist[n]);
548 if (mytrig & (1U << ch)) {
552 /* Write the scan to the buffer. */
553 if (comedi_buf_put(s->async, ((short *)&val)[0])
560 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
562 /* Overflow! Stop acquisition!! */
563 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
569 /* Check for end of acquisition. */
570 if (!subpriv->intr.continuous) {
571 /* stop_src == TRIG_COUNT */
572 if (subpriv->intr.stop_count > 0) {
573 subpriv->intr.stop_count--;
574 if (subpriv->intr.stop_count == 0) {
575 s->async->events |= COMEDI_CB_EOA;
576 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
586 spin_unlock_irqrestore
587 (&subpriv->intr.spinlock,
592 comedi_event(dev, s);
603 return IRQ_NONE; /* interrupt from other source */
607 static int pcmuio_start_intr(struct comedi_device *dev,
608 struct comedi_subdevice *s)
610 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
611 /* An empty acquisition! */
612 s->async->events |= COMEDI_CB_EOA;
613 subpriv->intr.active = 0;
616 unsigned bits = 0, pol_bits = 0, n;
617 int nports, firstport, asic, port;
618 struct comedi_cmd *cmd = &s->async->cmd;
620 asic = subpriv->intr.asic;
622 return 1; /* not an interrupt
624 subpriv->intr.enabled_mask = 0;
625 subpriv->intr.active = 1;
626 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
627 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
629 for (n = 0; n < cmd->chanlist_len; n++) {
630 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
631 pol_bits |= (CR_AREF(cmd->chanlist[n])
633 chanlist[n]) ? 1U : 0U)
634 << CR_CHAN(cmd->chanlist[n]);
637 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
638 1) << subpriv->intr.first_chan;
639 subpriv->intr.enabled_mask = bits;
641 switch_page(dev, asic, PAGE_ENAB);
642 for (port = firstport; port < firstport + nports; ++port) {
644 bits >> (subpriv->intr.first_chan + (port -
647 pol_bits >> (subpriv->intr.first_chan +
648 (port - firstport) * 8) & 0xff;
649 /* set enab intrs for this subdev.. */
651 devpriv->asics[asic].iobase + REG_ENAB0 + port);
652 switch_page(dev, asic, PAGE_POL);
654 devpriv->asics[asic].iobase + REG_ENAB0 + port);
660 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
664 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
665 if (subpriv->intr.active)
666 pcmuio_stop_intr(dev, s);
667 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
673 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
676 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
677 unsigned int trignum)
685 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
686 s->async->inttrig = 0;
687 if (subpriv->intr.active)
688 event = pcmuio_start_intr(dev, s);
690 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
693 comedi_event(dev, s);
699 * 'do_cmd' function for an 'INTERRUPT' subdevice.
701 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
703 struct comedi_cmd *cmd = &s->async->cmd;
707 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
708 subpriv->intr.active = 1;
710 /* Set up end of acquisition. */
711 switch (cmd->stop_src) {
713 subpriv->intr.continuous = 0;
714 subpriv->intr.stop_count = cmd->stop_arg;
718 subpriv->intr.continuous = 1;
719 subpriv->intr.stop_count = 0;
723 /* Set up start of acquisition. */
724 switch (cmd->start_src) {
726 s->async->inttrig = pcmuio_inttrig_start_intr;
730 event = pcmuio_start_intr(dev, s);
733 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
736 comedi_event(dev, s);
742 pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
743 struct comedi_cmd *cmd)
745 return comedi_pcm_cmdtest(dev, s, cmd);
748 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
750 const struct pcmuio_board *board = comedi_board(dev);
751 struct comedi_subdevice *s;
752 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
753 unsigned long iobase;
754 unsigned int irq[MAX_ASICS];
757 iobase = it->options[0];
758 irq[0] = it->options[1];
759 irq[1] = it->options[2];
761 dev_dbg(dev->class_dev, "%s: io: %lx attach\n",
762 dev->driver->driver_name, iobase);
764 dev->iobase = iobase;
766 if (!iobase || !request_region(iobase,
767 board->num_asics * ASIC_IOSIZE,
768 dev->driver->driver_name)) {
769 dev_err(dev->class_dev, "I/O port conflict\n");
773 dev->board_name = board->name;
776 * Allocate the private structure area. alloc_private() is a
777 * convenient macro defined in comedidev.h.
779 if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
780 dev_warn(dev->class_dev,
781 "cannot allocate private data structure\n");
785 for (asic = 0; asic < MAX_ASICS; ++asic) {
786 devpriv->asics[asic].num = asic;
787 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
788 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
789 this function when we
791 spin_lock_init(&devpriv->asics[asic].spinlock);
794 chans_left = CHANS_PER_ASIC * board->num_asics;
795 n_subdevs = CALC_N_SUBDEVS(chans_left);
797 kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
799 if (!devpriv->sprivs) {
800 dev_warn(dev->class_dev,
801 "cannot allocate subdevice private data structures\n");
805 ret = comedi_alloc_subdevices(dev, n_subdevs);
811 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
814 s = dev->subdevices + sdev_no;
815 s->private = devpriv->sprivs + sdev_no;
817 s->range_table = &range_digital;
818 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
819 s->type = COMEDI_SUBD_DIO;
820 s->insn_bits = pcmuio_dio_insn_bits;
821 s->insn_config = pcmuio_dio_insn_config;
822 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
823 subpriv->intr.asic = -1;
824 subpriv->intr.first_chan = -1;
825 subpriv->intr.asic_chan = -1;
826 subpriv->intr.num_asic_chans = -1;
827 subpriv->intr.active = 0;
830 /* save the ioport address for each 'port' of 8 channels in the
832 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
833 if (port >= PORTS_PER_ASIC) {
838 subpriv->iobases[byte_no] =
839 devpriv->asics[asic].iobase + port;
841 if (thisasic_chanct <
842 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
843 && subpriv->intr.asic < 0) {
844 /* this is an interrupt subdevice, so setup the struct */
845 subpriv->intr.asic = asic;
846 subpriv->intr.active = 0;
847 subpriv->intr.stop_count = 0;
848 subpriv->intr.first_chan = byte_no * 8;
849 subpriv->intr.asic_chan = thisasic_chanct;
850 subpriv->intr.num_asic_chans =
851 s->n_chan - subpriv->intr.first_chan;
852 dev->read_subdev = s;
853 s->subdev_flags |= SDF_CMD_READ;
854 s->cancel = pcmuio_cancel;
855 s->do_cmd = pcmuio_cmd;
856 s->do_cmdtest = pcmuio_cmdtest;
857 s->len_chanlist = subpriv->intr.num_asic_chans;
859 thisasic_chanct += CHANS_PER_PORT;
861 spin_lock_init(&subpriv->intr.spinlock);
863 chans_left -= s->n_chan;
866 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
872 init_asics(dev); /* clear out all the registers, basically */
874 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
876 && request_irq(irq[asic], interrupt_pcmuio,
877 IRQF_SHARED, board->name, dev)) {
879 /* unroll the allocated irqs.. */
880 for (i = asic - 1; i >= 0; --i) {
881 free_irq(irq[i], dev);
882 devpriv->asics[i].irq = irq[i] = 0;
886 devpriv->asics[asic].irq = irq[asic];
889 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
893 dev_dbg(dev->class_dev, "irq: %u\n", irq[0]);
894 if (irq[1] && board->num_asics == 2)
895 dev_dbg(dev->class_dev, "second ASIC irq: %u\n",
898 dev_dbg(dev->class_dev, "(IRQ mode disabled)\n");
905 static void pcmuio_detach(struct comedi_device *dev)
907 const struct pcmuio_board *board = comedi_board(dev);
911 release_region(dev->iobase, ASIC_IOSIZE * board->num_asics);
912 for (i = 0; i < MAX_ASICS; ++i) {
913 if (devpriv->asics[i].irq)
914 free_irq(devpriv->asics[i].irq, dev);
916 if (devpriv && devpriv->sprivs)
917 kfree(devpriv->sprivs);
920 static const struct pcmuio_board pcmuio_boards[] = {
932 static struct comedi_driver pcmuio_driver = {
933 .driver_name = "pcmuio",
934 .module = THIS_MODULE,
935 .attach = pcmuio_attach,
936 .detach = pcmuio_detach,
937 .board_name = &pcmuio_boards[0].name,
938 .offset = sizeof(struct pcmuio_board),
939 .num_names = ARRAY_SIZE(pcmuio_boards),
941 module_comedi_driver(pcmuio_driver);
943 MODULE_AUTHOR("Comedi http://www.comedi.org");
944 MODULE_DESCRIPTION("Comedi low-level driver");
945 MODULE_LICENSE("GPL");