2 comedi/drivers/pcmmio.c
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.
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-MIO multifunction board
25 Devices: [Winsystems] PCM-MIO (pcmmio)
26 Author: Calin Culianu <calin@ajvar.org>
27 Updated: Wed, May 16 2007 16:21:10 -0500
30 A driver for the relatively new PCM-MIO multifunction board from
31 Winsystems. This board is a PC-104 based I/O board. It contains
33 subdevice 0 - 16 channels of 16-bit AI
34 subdevice 1 - 8 channels of 16-bit AO
35 subdevice 2 - first 24 channels of the 48 channel of DIO (with edge-triggered interrupt support)
36 subdevice 3 - last 24 channels of the 48 channel DIO (no interrupt support for this bank of channels)
40 Synchronous reads and writes are the only things implemented for AI and AO,
41 even though the hardware itself can do streaming acquisition, etc. Anyone
42 want to add asynchronous I/O for AI/AO as a feature? Be my guest...
44 Asynchronous I/O for the DIO subdevices *is* implemented, however! They are
45 basically edge-triggered interrupts for any configuration of the first
48 Also note that this interrupt support is untested.
50 A few words about edge-detection IRQ support (commands on DIO):
52 * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
53 of the board to the comedi_config command. The board IRQ is not jumpered
54 but rather configured through software, so any IRQ from 1-15 is OK.
56 * Due to the genericity of the comedi API, you need to create a special
57 comedi_command in order to use edge-triggered interrupts for DIO.
59 * Use comedi_commands with TRIG_NOW. Your callback will be called each
60 time an edge is detected on the specified DIO line(s), and the data
61 values will be two sample_t's, which should be concatenated to form
62 one 32-bit unsigned int. This value is the mask of channels that had
63 edges detected from your channel list. Note that the bits positions
64 in the mask correspond to positions in your chanlist when you
65 specified the command and *not* channel id's!
67 * To set the polarity of the edge-detection interrupts pass a nonzero value
68 for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
69 value for both CR_RANGE and CR_AREF if you want edge-down polarity.
71 Configuration Options:
72 [0] - I/O port base address
73 [1] - IRQ (optional -- for edge-detect interrupt support only, leave out if you don't need this feature)
76 #include "../comedidev.h"
77 #include <linux/pci.h> /* for PCI devices */
79 #define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
81 /* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
82 #define CHANS_PER_PORT 8
83 #define PORTS_PER_ASIC 6
84 #define INTR_PORTS_PER_ASIC 3
85 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
86 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
87 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
88 #define INTR_CHANS_PER_ASIC 24
89 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
90 #define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
91 #define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
92 #define SDEV_NO ((int)(s - dev->subdevices))
93 #define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
95 #define ASIC_IOSIZE (0x0B)
96 #define PCMMIO48_IOSIZE ASIC_IOSIZE
98 /* Some offsets - these are all in the 16byte IO memory offset from
99 the base address. Note that there is a paging scheme to swap out
100 offsets 0x8-0xA using the PAGELOCK register. See the table below.
102 Register(s) Pages R/W? Description
103 --------------------------------------------------------------
104 REG_PORTx All R/W Read/Write/Configure IO
105 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
106 REG_PAGELOCK All WriteOnly Select a page
107 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
108 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
109 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
111 #define REG_PORT0 0x0
112 #define REG_PORT1 0x1
113 #define REG_PORT2 0x2
114 #define REG_PORT3 0x3
115 #define REG_PORT4 0x4
116 #define REG_PORT5 0x5
117 #define REG_INT_PENDING 0x6
118 #define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page
119 and bits 0-5 are used to 'lock down' a particular
120 port above to make it readonly. */
124 #define REG_ENAB0 0x8
125 #define REG_ENAB1 0x9
126 #define REG_ENAB2 0xA
127 #define REG_INT_ID0 0x8
128 #define REG_INT_ID1 0x9
129 #define REG_INT_ID2 0xA
131 #define NUM_PAGED_REGS 3
133 #define FIRST_PAGED_REG 0x8
134 #define REG_PAGE_BITOFFSET 6
135 #define REG_LOCK_BITOFFSET 0
136 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
137 #define REG_LOCK_MASK ~(REG_PAGE_MASK)
140 #define PAGE_INT_ID 3
142 typedef int (*comedi_insn_fn_t) (struct comedi_device *, struct comedi_subdevice *,
143 struct comedi_insn *, unsigned int *);
145 static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
147 static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
149 static int ao_winsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
153 * Board descriptions for two imaginary boards. Describing the
154 * boards in this way is optional, and completely driver-dependent.
155 * Some drivers use arrays such as this, other do not.
157 struct pcmmio_board {
159 const int dio_num_asics;
160 const int dio_num_ports;
161 const int total_iosize;
164 const int n_ai_chans;
165 const int n_ao_chans;
166 const struct comedi_lrange *ai_range_table, *ao_range_table;
167 comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
170 static const struct comedi_lrange ranges_ai =
171 { 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0.,
175 static const struct comedi_lrange ranges_ao =
176 { 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
177 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
180 static const struct pcmmio_board pcmmio_boards[] = {
190 ai_range_table:&ranges_ai,
191 ao_range_table:&ranges_ao,
198 * Useful for shorthand access to the particular board structure
200 #define thisboard ((const struct pcmmio_board *)dev->board_ptr)
202 /* this structure is for data unique to this subdevice. */
203 struct pcmmio_subdev_private {
206 /* for DIO: mapping of halfwords (bytes) in port/chanarray to iobase */
207 unsigned long iobases[PORTS_PER_SUBDEV];
210 unsigned long iobase;
215 /* The below is only used for intr subdevices */
217 int asic; /* if non-negative, this subdev has an interrupt asic */
218 int first_chan; /* if nonnegative, the first channel id for
220 int num_asic_chans; /* the number of asic channels in this subdev
221 that have interrutps */
222 int asic_chan; /* if nonnegative, the first channel id with
223 respect to the asic that has interrupts */
224 int enabled_mask; /* subdev-relative channel mask for channels
225 we are interested in */
233 unsigned int shadow_samples[8]; /* the last unsigned int data written */
238 /* this structure is for data unique to this hardware driver. If
239 several hardware drivers keep similar information in this structure,
240 feel free to suggest moving the variable to the struct comedi_device struct. */
241 struct pcmmio_private {
244 unsigned char pagelock; /* current page and lock */
245 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
246 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
248 unsigned long iobase;
252 struct pcmmio_subdev_private *sprivs;
256 * most drivers define the following macro to make it easy to
257 * access the private structure.
259 #define devpriv ((struct pcmmio_private *)dev->private)
260 #define subpriv ((struct pcmmio_subdev_private *)s->private)
262 * The struct comedi_driver structure tells the Comedi core module
263 * which functions to call to configure/deconfigure (attach/detach)
264 * the board, and also about the kernel module that contains
267 static int pcmmio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
268 static int pcmmio_detach(struct comedi_device * dev);
270 static struct comedi_driver driver = {
271 driver_name:"pcmmio",
273 attach:pcmmio_attach,
274 detach:pcmmio_detach,
275 /* It is not necessary to implement the following members if you are
276 * writing a driver for a ISA PnP or PCI card */
277 /* Most drivers will support multiple types of boards by
278 * having an array of board structures. These were defined
279 * in pcmmio_boards[] above. Note that the element 'name'
280 * was first in the structure -- Comedi uses this fact to
281 * extract the name of the board without knowing any details
282 * about the structure except for its length.
283 * When a device is attached (by comedi_config), the name
284 * of the device is given to Comedi, and Comedi tries to
285 * match it by going through the list of board names. If
286 * there is a match, the address of the pointer is put
287 * into dev->board_ptr and driver->attach() is called.
289 * Note that these are not necessary if you can determine
290 * the type of board in software. ISA PnP, PCI, and PCMCIA
291 * devices are such boards.
293 board_name:&pcmmio_boards[0].name,
294 offset:sizeof(struct pcmmio_board),
295 num_names:sizeof(pcmmio_boards) / sizeof(struct pcmmio_board),
298 static int pcmmio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
299 struct comedi_insn * insn, unsigned int * data);
300 static int pcmmio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
301 struct comedi_insn * insn, unsigned int * data);
303 static irqreturn_t interrupt_pcmmio(int irq, void *d PT_REGS_ARG);
304 static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
305 static int pcmmio_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
306 static int pcmmio_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
307 static int pcmmio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
308 struct comedi_cmd * cmd);
310 /* some helper functions to deal with specifics of this device's registers */
311 static void init_asics(struct comedi_device * dev); /* sets up/clears ASIC chips to defaults */
312 static void switch_page(struct comedi_device * dev, int asic, int page);
314 static void lock_port(struct comedi_device * dev, int asic, int port);
315 static void unlock_port(struct comedi_device * dev, int asic, int port);
319 * Attach is called by the Comedi core to configure the driver
320 * for a particular board. If you specified a board_name array
321 * in the driver structure, dev->board_ptr contains that
324 static int pcmmio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
326 struct comedi_subdevice *s;
327 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
329 unsigned long iobase;
330 unsigned int irq[MAX_ASICS];
332 iobase = it->options[0];
333 irq[0] = it->options[1];
335 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
338 dev->iobase = iobase;
340 if (!iobase || !request_region(iobase,
341 thisboard->total_iosize, driver.driver_name)) {
342 printk("I/O port conflict\n");
347 * Initialize dev->board_name. Note that we can use the "thisboard"
348 * macro now, since we just initialized it in the last line.
350 dev->board_name = thisboard->name;
353 * Allocate the private structure area. alloc_private() is a
354 * convenient macro defined in comedidev.h.
356 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
357 printk("cannot allocate private data structure\n");
361 for (asic = 0; asic < MAX_ASICS; ++asic) {
362 devpriv->asics[asic].num = asic;
363 devpriv->asics[asic].iobase =
364 dev->iobase + 16 + asic * ASIC_IOSIZE;
365 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
366 this function when we
367 comedi_request_irqs */
368 spin_lock_init(&devpriv->asics[asic].spinlock);
371 chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
372 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
373 n_subdevs = n_dio_subdevs + 2;
375 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private), GFP_KERNEL);
376 if (!devpriv->sprivs) {
377 printk("cannot allocate subdevice private data structures\n");
381 * Allocate the subdevice structures. alloc_subdevice() is a
382 * convenient macro defined in comedidev.h.
384 * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
386 if (alloc_subdevices(dev, n_subdevs) < 0) {
387 printk("cannot allocate subdevice data structures\n");
393 s = dev->subdevices + sdev_no;
394 s->private = devpriv->sprivs + sdev_no;
395 s->maxdata = (1 << thisboard->ai_bits) - 1;
396 s->range_table = thisboard->ai_range_table;
397 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
398 s->type = COMEDI_SUBD_AI;
399 s->n_chan = thisboard->n_ai_chans;
400 s->len_chanlist = s->n_chan;
401 s->insn_read = thisboard->ai_rinsn;
402 subpriv->iobase = dev->iobase + 0;
403 /* initialize the resource enable register by clearing it */
404 outb(0, subpriv->iobase + 3);
405 outb(0, subpriv->iobase + 4 + 3);
409 s = dev->subdevices + sdev_no;
410 s->private = devpriv->sprivs + sdev_no;
411 s->maxdata = (1 << thisboard->ao_bits) - 1;
412 s->range_table = thisboard->ao_range_table;
413 s->subdev_flags = SDF_READABLE;
414 s->type = COMEDI_SUBD_AO;
415 s->n_chan = thisboard->n_ao_chans;
416 s->len_chanlist = s->n_chan;
417 s->insn_read = thisboard->ao_rinsn;
418 s->insn_write = thisboard->ao_winsn;
419 subpriv->iobase = dev->iobase + 8;
420 /* initialize the resource enable register by clearing it */
421 outb(0, subpriv->iobase + 3);
422 outb(0, subpriv->iobase + 4 + 3);
427 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
430 s = dev->subdevices + sdev_no;
431 s->private = devpriv->sprivs + sdev_no;
433 s->range_table = &range_digital;
434 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
435 s->type = COMEDI_SUBD_DIO;
436 s->insn_bits = pcmmio_dio_insn_bits;
437 s->insn_config = pcmmio_dio_insn_config;
438 s->n_chan = MIN(chans_left, MAX_CHANS_PER_SUBDEV);
439 subpriv->dio.intr.asic = -1;
440 subpriv->dio.intr.first_chan = -1;
441 subpriv->dio.intr.asic_chan = -1;
442 subpriv->dio.intr.num_asic_chans = -1;
443 subpriv->dio.intr.active = 0;
446 /* save the ioport address for each 'port' of 8 channels in the
448 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
449 if (port >= PORTS_PER_ASIC) {
454 subpriv->iobases[byte_no] =
455 devpriv->asics[asic].iobase + port;
457 if (thisasic_chanct <
458 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
459 && subpriv->dio.intr.asic < 0) {
460 /* this is an interrupt subdevice, so setup the struct */
461 subpriv->dio.intr.asic = asic;
462 subpriv->dio.intr.active = 0;
463 subpriv->dio.intr.stop_count = 0;
464 subpriv->dio.intr.first_chan = byte_no * 8;
465 subpriv->dio.intr.asic_chan = thisasic_chanct;
466 subpriv->dio.intr.num_asic_chans =
468 subpriv->dio.intr.first_chan;
469 s->cancel = pcmmio_cancel;
470 s->do_cmd = pcmmio_cmd;
471 s->do_cmdtest = pcmmio_cmdtest;
473 subpriv->dio.intr.num_asic_chans;
475 thisasic_chanct += CHANS_PER_PORT;
477 spin_lock_init(&subpriv->dio.intr.spinlock);
479 chans_left -= s->n_chan;
482 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
488 init_asics(dev); /* clear out all the registers, basically */
490 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
492 && comedi_request_irq(irq[asic], interrupt_pcmmio,
493 IRQF_SHARED, thisboard->name, dev)) {
495 /* unroll the allocated irqs.. */
496 for (i = asic - 1; i >= 0; --i) {
497 comedi_free_irq(irq[i], dev);
498 devpriv->asics[i].irq = irq[i] = 0;
502 devpriv->asics[asic].irq = irq[asic];
505 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
509 printk("irq: %u ", irq[0]);
510 if (irq[1] && thisboard->dio_num_asics == 2)
511 printk("second ASIC irq: %u ", irq[1]);
513 printk("(IRQ mode disabled) ");
516 printk("attached\n");
522 * _detach is called to deconfigure a device. It should deallocate
524 * This function is also called when _attach() fails, so it should be
525 * careful not to release resources that were not necessarily
526 * allocated by _attach(). dev->private and dev->subdevices are
527 * deallocated automatically by the core.
529 static int pcmmio_detach(struct comedi_device * dev)
533 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
535 release_region(dev->iobase, thisboard->total_iosize);
537 for (i = 0; i < MAX_ASICS; ++i) {
538 if (devpriv && devpriv->asics[i].irq)
539 comedi_free_irq(devpriv->asics[i].irq, dev);
542 if (devpriv && devpriv->sprivs)
543 kfree(devpriv->sprivs);
548 /* DIO devices are slightly special. Although it is possible to
549 * implement the insn_read/insn_write interface, it is much more
550 * useful to applications if you implement the insn_bits interface.
551 * This allows packed reading/writing of the DIO channels. The
552 * comedi core can convert between insn_bits and insn_read/write */
553 static int pcmmio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
554 struct comedi_insn * insn, unsigned int * data)
561 reading a 0 means this channel was high
562 writine a 0 sets the channel high
563 reading a 1 means this channel was low
564 writing a 1 means set this channel low
566 Therefore everything is always inverted. */
568 /* The insn data is a mask in data[0] and the new data
569 * in data[1], each channel cooresponding to a bit. */
571 #ifdef DAMMIT_ITS_BROKEN
573 printk("write mask: %08x data: %08x\n", data[0], data[1]);
578 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
579 /* address of 8-bit port */
580 unsigned long ioaddr = subpriv->iobases[byte_no],
581 /* bit offset of port in 32-bit doubleword */
582 offset = byte_no * 8;
583 /* this 8-bit port's data */
584 unsigned char byte = 0,
585 /* The write mask for this port (if any) */
586 write_mask_byte = (data[0] >> offset) & 0xff,
587 /* The data byte for this port */
588 data_byte = (data[1] >> offset) & 0xff;
590 byte = inb(ioaddr); /* read all 8-bits for this port */
592 #ifdef DAMMIT_ITS_BROKEN
594 printk("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
597 if (write_mask_byte) {
598 /* this byte has some write_bits -- so set the output lines */
599 byte &= ~write_mask_byte; /* clear bits for write mask */
600 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
601 /* Write out the new digital output state */
604 #ifdef DAMMIT_ITS_BROKEN
606 printk("data_out_byte %02x\n", (unsigned)byte);
608 /* save the digital input lines for this byte.. */
609 s->state |= ((unsigned int)byte) << offset;
612 /* now return the DIO lines to data[1] - note they came inverted! */
615 #ifdef DAMMIT_ITS_BROKEN
617 printk("s->state %08x data_out %08x\n", s->state, data[1]);
623 /* The input or output configuration of each digital line is
624 * configured by a special insn_config instruction. chanspec
625 * contains the channel to be changed, and data[0] contains the
626 * value COMEDI_INPUT or COMEDI_OUTPUT. */
627 static int pcmmio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
628 struct comedi_insn * insn, unsigned int * data)
630 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
632 unsigned long ioaddr;
635 /* Compute ioaddr for this channel */
636 ioaddr = subpriv->iobases[byte_no];
639 writing a 0 an IO channel's bit sets the channel to INPUT
640 and pulls the line high as well
642 writing a 1 to an IO channel's bit pulls the line low
644 All channels are implicitly always in OUTPUT mode -- but when
645 they are high they can be considered to be in INPUT mode..
647 Thus, we only force channels low if the config request was INPUT,
648 otherwise we do nothing to the hardware. */
651 case INSN_CONFIG_DIO_OUTPUT:
652 /* save to io_bits -- don't actually do anything since
653 all input channels are also output channels... */
654 s->io_bits |= 1 << chan;
656 case INSN_CONFIG_DIO_INPUT:
657 /* write a 0 to the actual register representing the channel
658 to set it to 'input'. 0 means "float high". */
660 byte &= ~(1 << bit_no);
661 /**< set input channel to '0' */
663 /* write out byte -- this is the only time we actually affect the
664 hardware as all channels are implicitly output -- but input
665 channels are set to float-high */
668 /* save to io_bits */
669 s->io_bits &= ~(1 << chan);
672 case INSN_CONFIG_DIO_QUERY:
673 /* retreive from shadow register */
676 io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
688 static void init_asics(struct comedi_device * dev)
690 ASIC chip to defaults */
693 for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
695 unsigned long baseaddr = devpriv->asics[asic].iobase;
697 switch_page(dev, asic, 0); /* switch back to page 0 */
699 /* first, clear all the DIO port bits */
700 for (port = 0; port < PORTS_PER_ASIC; ++port)
701 outb(0, baseaddr + REG_PORT0 + port);
703 /* Next, clear all the paged registers for each page */
704 for (page = 1; page < NUM_PAGES; ++page) {
706 /* now clear all the paged registers */
707 switch_page(dev, asic, page);
708 for (reg = FIRST_PAGED_REG;
709 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
710 outb(0, baseaddr + reg);
713 /* DEBUG set rising edge interrupts on port0 of both asics */
714 /*switch_page(dev, asic, PAGE_POL);
715 outb(0xff, baseaddr + REG_POL0);
716 switch_page(dev, asic, PAGE_ENAB);
717 outb(0xff, baseaddr + REG_ENAB0); */
720 switch_page(dev, asic, 0); /* switch back to default page 0 */
725 static void switch_page(struct comedi_device * dev, int asic, int page)
727 if (asic < 0 || asic >= thisboard->dio_num_asics)
728 return; /* paranoia */
729 if (page < 0 || page >= NUM_PAGES)
730 return; /* more paranoia */
732 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
733 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
735 /* now write out the shadow register */
736 outb(devpriv->asics[asic].pagelock,
737 devpriv->asics[asic].iobase + REG_PAGELOCK);
741 static void lock_port(struct comedi_device * dev, int asic, int port)
743 if (asic < 0 || asic >= thisboard->dio_num_asics)
744 return; /* paranoia */
745 if (port < 0 || port >= PORTS_PER_ASIC)
746 return; /* more paranoia */
748 devpriv->asics[asic].pagelock |= 0x1 << port;
749 /* now write out the shadow register */
750 outb(devpriv->asics[asic].pagelock,
751 devpriv->asics[asic].iobase + REG_PAGELOCK);
755 static void unlock_port(struct comedi_device * dev, int asic, int port)
757 if (asic < 0 || asic >= thisboard->dio_num_asics)
758 return; /* paranoia */
759 if (port < 0 || port >= PORTS_PER_ASIC)
760 return; /* more paranoia */
761 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
762 /* now write out the shadow register */
763 outb(devpriv->asics[asic].pagelock,
764 devpriv->asics[asic].iobase + REG_PAGELOCK);
768 static irqreturn_t interrupt_pcmmio(int irq, void *d PT_REGS_ARG)
771 struct comedi_device *dev = (struct comedi_device *) d;
773 for (asic = 0; asic < MAX_ASICS; ++asic) {
774 if (irq == devpriv->asics[asic].irq) {
776 unsigned triggered = 0;
777 unsigned long iobase = devpriv->asics[asic].iobase;
778 /* it is an interrupt for ASIC #asic */
779 unsigned char int_pend;
781 comedi_spin_lock_irqsave(&devpriv->asics[asic].spinlock,
784 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
788 for (port = 0; port < INTR_PORTS_PER_ASIC;
790 if (int_pend & (0x1 << port)) {
792 io_lines_with_edges = 0;
793 switch_page(dev, asic,
795 io_lines_with_edges =
799 if (io_lines_with_edges)
800 /* clear pending interrupt */
806 io_lines_with_edges <<
814 comedi_spin_unlock_irqrestore(&devpriv->asics[asic].
818 struct comedi_subdevice *s;
819 /* TODO here: dispatch io lines to subdevs with commands.. */
820 printk("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", irq, asic, triggered);
821 for (s = dev->subdevices + 2;
822 s < dev->subdevices + dev->n_subdevices;
824 if (subpriv->dio.intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
828 comedi_spin_lock_irqsave
832 oldevents = s->async->events;
834 if (subpriv->dio.intr.active) {
841 & ((0x1 << subpriv->dio.intr.num_asic_chans) - 1)) << subpriv->dio.intr.first_chan;
842 if (mytrig & subpriv->
856 ch = CR_CHAN(s->async->cmd.chanlist[n]);
857 if (mytrig & (1U << ch)) {
861 /* Write the scan to the buffer. */
862 if (comedi_buf_put(s->async, ((short *) & val)[0])
865 (s->async, ((short *) & val)[1])) {
866 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
868 /* Overflow! Stop acquisition!! */
869 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
875 /* Check for end of acquisition. */
881 /* stop_src == TRIG_COUNT */
882 if (subpriv->dio.intr.stop_count > 0) {
887 if (subpriv->dio.intr.stop_count == 0) {
888 s->async->events |= COMEDI_CB_EOA;
889 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
899 comedi_spin_unlock_irqrestore
905 comedi_event(dev, s);
916 return IRQ_NONE; /* interrupt from other source */
920 static void pcmmio_stop_intr(struct comedi_device * dev, struct comedi_subdevice * s)
922 int nports, firstport, asic, port;
924 if ((asic = subpriv->dio.intr.asic) < 0)
925 return; /* not an interrupt subdev */
927 subpriv->dio.intr.enabled_mask = 0;
928 subpriv->dio.intr.active = 0;
929 s->async->inttrig = 0;
930 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
931 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
932 switch_page(dev, asic, PAGE_ENAB);
933 for (port = firstport; port < firstport + nports; ++port) {
934 /* disable all intrs for this subdev.. */
935 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
939 static int pcmmio_start_intr(struct comedi_device * dev, struct comedi_subdevice * s)
941 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
942 /* An empty acquisition! */
943 s->async->events |= COMEDI_CB_EOA;
944 subpriv->dio.intr.active = 0;
947 unsigned bits = 0, pol_bits = 0, n;
948 int nports, firstport, asic, port;
949 struct comedi_cmd *cmd = &s->async->cmd;
951 if ((asic = subpriv->dio.intr.asic) < 0)
952 return 1; /* not an interrupt
954 subpriv->dio.intr.enabled_mask = 0;
955 subpriv->dio.intr.active = 1;
956 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
957 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
959 for (n = 0; n < cmd->chanlist_len; n++) {
960 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
961 pol_bits |= (CR_AREF(cmd->chanlist[n])
962 || CR_RANGE(cmd->chanlist[n]) ? 1U : 0U)
963 << CR_CHAN(cmd->chanlist[n]);
966 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
967 1) << subpriv->dio.intr.first_chan;
968 subpriv->dio.intr.enabled_mask = bits;
970 { /* the below code configures the board to use a specific IRQ from 0-15. */
972 /* set resource enable register to enable IRQ operation */
973 outb(1 << 4, dev->iobase + 3);
974 /* set bits 0-3 of b to the irq number from 0-15 */
975 b = dev->irq & ((1 << 4) - 1);
976 outb(b, dev->iobase + 2);
977 /* done, we told the board what irq to use */
980 switch_page(dev, asic, PAGE_ENAB);
981 for (port = firstport; port < firstport + nports; ++port) {
983 bits >> (subpriv->dio.intr.first_chan + (port -
984 firstport) * 8) & 0xff, pol =
985 pol_bits >> (subpriv->dio.intr.first_chan +
986 (port - firstport) * 8) & 0xff;
987 /* set enab intrs for this subdev.. */
989 devpriv->asics[asic].iobase + REG_ENAB0 + port);
990 switch_page(dev, asic, PAGE_POL);
992 devpriv->asics[asic].iobase + REG_ENAB0 + port);
998 static int pcmmio_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
1000 unsigned long flags;
1002 comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1003 if (subpriv->dio.intr.active)
1004 pcmmio_stop_intr(dev, s);
1005 comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1011 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
1014 pcmmio_inttrig_start_intr(struct comedi_device * dev, struct comedi_subdevice * s,
1015 unsigned int trignum)
1017 unsigned long flags;
1023 comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1024 s->async->inttrig = 0;
1025 if (subpriv->dio.intr.active) {
1026 event = pcmmio_start_intr(dev, s);
1028 comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1031 comedi_event(dev, s);
1038 * 'do_cmd' function for an 'INTERRUPT' subdevice.
1040 static int pcmmio_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
1042 struct comedi_cmd *cmd = &s->async->cmd;
1043 unsigned long flags;
1046 comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1047 subpriv->dio.intr.active = 1;
1049 /* Set up end of acquisition. */
1050 switch (cmd->stop_src) {
1052 subpriv->dio.intr.continuous = 0;
1053 subpriv->dio.intr.stop_count = cmd->stop_arg;
1057 subpriv->dio.intr.continuous = 1;
1058 subpriv->dio.intr.stop_count = 0;
1062 /* Set up start of acquisition. */
1063 switch (cmd->start_src) {
1065 s->async->inttrig = pcmmio_inttrig_start_intr;
1069 event = pcmmio_start_intr(dev, s);
1072 comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1075 comedi_event(dev, s);
1082 * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
1085 pcmmio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, struct comedi_cmd * cmd)
1090 /* step 1: make sure trigger sources are trivially valid */
1092 tmp = cmd->start_src;
1093 cmd->start_src &= (TRIG_NOW | TRIG_INT);
1094 if (!cmd->start_src || tmp != cmd->start_src)
1097 tmp = cmd->scan_begin_src;
1098 cmd->scan_begin_src &= TRIG_EXT;
1099 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
1102 tmp = cmd->convert_src;
1103 cmd->convert_src &= TRIG_NOW;
1104 if (!cmd->convert_src || tmp != cmd->convert_src)
1107 tmp = cmd->scan_end_src;
1108 cmd->scan_end_src &= TRIG_COUNT;
1109 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
1112 tmp = cmd->stop_src;
1113 cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
1114 if (!cmd->stop_src || tmp != cmd->stop_src)
1120 /* step 2: make sure trigger sources are unique and mutually compatible */
1122 /* these tests are true if more than one _src bit is set */
1123 if ((cmd->start_src & (cmd->start_src - 1)) != 0)
1125 if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
1127 if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
1129 if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
1131 if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
1137 /* step 3: make sure arguments are trivially compatible */
1139 /* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
1140 if (cmd->start_arg != 0) {
1145 /* cmd->scan_begin_src == TRIG_EXT */
1146 if (cmd->scan_begin_arg != 0) {
1147 cmd->scan_begin_arg = 0;
1151 /* cmd->convert_src == TRIG_NOW */
1152 if (cmd->convert_arg != 0) {
1153 cmd->convert_arg = 0;
1157 /* cmd->scan_end_src == TRIG_COUNT */
1158 if (cmd->scan_end_arg != cmd->chanlist_len) {
1159 cmd->scan_end_arg = cmd->chanlist_len;
1163 switch (cmd->stop_src) {
1165 /* any count allowed */
1168 if (cmd->stop_arg != 0) {
1180 /* step 4: fix up any arguments */
1182 /* if (err) return 4; */
1187 static int adc_wait_ready(unsigned long iobase)
1189 unsigned long retry = 100000;
1191 if (inb(iobase + 3) & 0x80)
1196 /* All this is for AI and AO */
1197 static int ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
1198 struct comedi_insn * insn, unsigned int * data)
1201 unsigned long iobase = subpriv->iobase;
1204 1. write the CMD byte (to BASE+2)
1205 2. read junk lo byte (BASE+0)
1206 3. read junk hi byte (BASE+1)
1207 4. (mux settled so) write CMD byte again (BASE+2)
1208 5. read valid lo byte(BASE+0)
1209 6. read valid hi byte(BASE+1)
1211 Additionally note that the BASE += 4 if the channel >= 8
1214 /* convert n samples */
1215 for (n = 0; n < insn->n; n++) {
1216 unsigned chan = CR_CHAN(insn->chanspec), range =
1217 CR_RANGE(insn->chanspec), aref =
1218 CR_AREF(insn->chanspec);
1219 unsigned char command_byte = 0;
1220 unsigned iooffset = 0;
1221 short sample, adc_adjust = 0;
1224 chan -= 8, iooffset = 4; /* use the second dword for channels > 7 */
1226 if (aref != AREF_DIFF) {
1228 command_byte |= 1 << 7; /* set bit 7 to indicate single-ended */
1231 adc_adjust = 0x8000; /* bipolar ranges (-5,5 .. -10,10 need to be adjusted -- that is.. they need to wrap around by adding 0x8000 */
1234 command_byte |= 1 << 6; /* odd-numbered channels have bit 6 set */
1237 /* select the channel, bits 4-5 == chan/2 */
1238 command_byte |= ((chan / 2) & 0x3) << 4;
1240 /* set the range, bits 2-3 */
1241 command_byte |= (range & 0x3) << 2;
1243 /* need to do this twice to make sure mux settled */
1244 outb(command_byte, iobase + iooffset + 2); /* chan/range/aref select */
1246 adc_wait_ready(iobase + iooffset); /* wait for the adc to say it finised the conversion */
1248 outb(command_byte, iobase + iooffset + 2); /* select the chan/range/aref AGAIN */
1250 adc_wait_ready(iobase + iooffset);
1252 sample = inb(iobase + iooffset + 0); /* read data lo byte */
1253 sample |= inb(iobase + iooffset + 1) << 8; /* read data hi byte */
1254 sample += adc_adjust; /* adjustment .. munge data */
1257 /* return the number of samples read/written */
1261 static int ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
1262 struct comedi_insn * insn, unsigned int * data)
1265 for (n = 0; n < insn->n; n++) {
1266 unsigned chan = CR_CHAN(insn->chanspec);
1267 if (chan < s->n_chan)
1268 data[n] = subpriv->ao.shadow_samples[chan];
1273 static int wait_dac_ready(unsigned long iobase)
1275 unsigned long retry = 100000L;
1277 /* This may seem like an absurd way to handle waiting and violates the
1278 "no busy waiting" policy. The fact is that the hardware is
1279 normally so fast that we usually only need one time through the loop
1280 anyway. The longer timeout is for rare occasions and for detecting
1281 non-existant hardware. */
1284 if (inb(iobase + 3) & 0x80)
1291 static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
1292 struct comedi_insn * insn, unsigned int * data)
1295 unsigned iobase = subpriv->iobase, iooffset = 0;
1297 for (n = 0; n < insn->n; n++) {
1298 unsigned chan = CR_CHAN(insn->chanspec), range =
1299 CR_RANGE(insn->chanspec);
1300 if (chan < s->n_chan) {
1301 unsigned char command_byte = 0, range_byte =
1302 range & ((1 << 4) - 1);
1304 chan -= 4, iooffset += 4;
1305 /* set the range.. */
1306 outb(range_byte, iobase + iooffset + 0);
1307 outb(0, iobase + iooffset + 1);
1309 /* tell it to begin */
1310 command_byte = (chan << 1) | 0x60;
1311 outb(command_byte, iobase + iooffset + 2);
1313 wait_dac_ready(iobase + iooffset);
1315 outb(data[n] & 0xff, iobase + iooffset + 0); /* low order byte */
1316 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1); /* high order byte */
1317 command_byte = 0x70 | (chan << 1); /* set bit 4 of command byte to indicate data is loaded and trigger conversion */
1318 /* trigger converion */
1319 outb(command_byte, iobase + iooffset + 2);
1321 wait_dac_ready(iobase + iooffset);
1323 subpriv->ao.shadow_samples[chan] = data[n]; /* save to shadow register for ao_rinsn */
1330 * A convenient macro that defines init_module() and cleanup_module(),
1333 COMEDI_INITCLEANUP(driver);