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
36 (with edge-triggered interrupt support)
37 subdevice 3 - last 24 channels of the 48 channel DIO
38 (no interrupt support for this bank of channels)
42 Synchronous reads and writes are the only things implemented for AI and AO,
43 even though the hardware itself can do streaming acquisition, etc. Anyone
44 want to add asynchronous I/O for AI/AO as a feature? Be my guest...
46 Asynchronous I/O for the DIO subdevices *is* implemented, however! They are
47 basically edge-triggered interrupts for any configuration of the first
50 Also note that this interrupt support is untested.
52 A few words about edge-detection IRQ support (commands on DIO):
54 * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
55 of the board to the comedi_config command. The board IRQ is not jumpered
56 but rather configured through software, so any IRQ from 1-15 is OK.
58 * Due to the genericity of the comedi API, you need to create a special
59 comedi_command in order to use edge-triggered interrupts for DIO.
61 * Use comedi_commands with TRIG_NOW. Your callback will be called each
62 time an edge is detected on the specified DIO line(s), and the data
63 values will be two sample_t's, which should be concatenated to form
64 one 32-bit unsigned int. This value is the mask of channels that had
65 edges detected from your channel list. Note that the bits positions
66 in the mask correspond to positions in your chanlist when you
67 specified the command and *not* channel id's!
69 * To set the polarity of the edge-detection interrupts pass a nonzero value
70 for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
71 value for both CR_RANGE and CR_AREF if you want edge-down polarity.
73 Configuration Options:
74 [0] - I/O port base address
75 [1] - IRQ (optional -- for edge-detect interrupt support only,
76 leave out if you don't need this feature)
79 #include <linux/interrupt.h>
80 #include "../comedidev.h"
81 #include "pcm_common.h"
82 #include <linux/pci.h> /* for PCI devices */
84 /* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
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*1*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_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
98 #define ASIC_IOSIZE (0x0B)
99 #define PCMMIO48_IOSIZE ASIC_IOSIZE
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 /*
122 * page selector register, upper 2 bits select
123 * a page and bits 0-5 are used to 'lock down'
124 * a particular port above to make it readonly.
129 #define REG_ENAB0 0x8
130 #define REG_ENAB1 0x9
131 #define REG_ENAB2 0xA
132 #define REG_INT_ID0 0x8
133 #define REG_INT_ID1 0x9
134 #define REG_INT_ID2 0xA
136 #define NUM_PAGED_REGS 3
138 #define FIRST_PAGED_REG 0x8
139 #define REG_PAGE_BITOFFSET 6
140 #define REG_LOCK_BITOFFSET 0
141 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
142 #define REG_LOCK_MASK (~(REG_PAGE_MASK))
145 #define PAGE_INT_ID 3
147 typedef int (*comedi_insn_fn_t) (struct comedi_device *,
148 struct comedi_subdevice *,
149 struct comedi_insn *, unsigned int *);
151 static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *,
152 struct comedi_insn *, unsigned int *);
153 static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *,
154 struct comedi_insn *, unsigned int *);
155 static int ao_winsn(struct comedi_device *, struct comedi_subdevice *,
156 struct comedi_insn *, unsigned int *);
159 * Board descriptions for two imaginary boards. Describing the
160 * boards in this way is optional, and completely driver-dependent.
161 * Some drivers use arrays such as this, other do not.
163 struct pcmmio_board {
165 const int dio_num_asics;
166 const int dio_num_ports;
167 const int total_iosize;
170 const int n_ai_chans;
171 const int n_ao_chans;
172 const struct comedi_lrange *ai_range_table, *ao_range_table;
173 comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
176 static const struct comedi_lrange ranges_ai = {
177 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
180 static const struct comedi_lrange ranges_ao = {
181 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
182 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
185 static const struct pcmmio_board pcmmio_boards[] = {
195 .ai_range_table = &ranges_ai,
196 .ao_range_table = &ranges_ao,
197 .ai_rinsn = ai_rinsn,
198 .ao_rinsn = ao_rinsn,
199 .ao_winsn = ao_winsn},
203 * Useful for shorthand access to the particular board structure
205 #define thisboard ((const struct pcmmio_board *)dev->board_ptr)
207 /* this structure is for data unique to this subdevice. */
208 struct pcmmio_subdev_private {
211 /* for DIO: mapping of halfwords (bytes)
212 in port/chanarray to iobase */
213 unsigned long iobases[PORTS_PER_SUBDEV];
216 unsigned long iobase;
221 /* The below is only used for intr subdevices */
224 * if non-negative, this subdev has an
229 * if nonnegative, the first channel id for
234 * the number of asic channels in this subdev
235 * that have interrutps
239 * if nonnegative, the first channel id with
240 * respect to the asic that has interrupts
244 * subdev-relative channel mask for channels
245 * we are interested in
255 /* the last unsigned int data written */
256 unsigned int shadow_samples[8];
262 * this structure is for data unique to this hardware driver. If
263 * several hardware drivers keep similar information in this structure,
264 * feel free to suggest moving the variable to the struct comedi_device struct.
266 struct pcmmio_private {
269 unsigned char pagelock; /* current page and lock */
270 /* shadow of POLx registers */
271 unsigned char pol[NUM_PAGED_REGS];
272 /* shadow of ENABx registers */
273 unsigned char enab[NUM_PAGED_REGS];
275 unsigned long iobase;
279 struct pcmmio_subdev_private *sprivs;
283 * most drivers define the following macro to make it easy to
284 * access the private structure.
286 #define devpriv ((struct pcmmio_private *)dev->private)
287 #define subpriv ((struct pcmmio_subdev_private *)s->private)
289 * The struct comedi_driver structure tells the Comedi core module
290 * which functions to call to configure/deconfigure (attach/detach)
291 * the board, and also about the kernel module that contains
294 static int pcmmio_attach(struct comedi_device *dev,
295 struct comedi_devconfig *it);
296 static int pcmmio_detach(struct comedi_device *dev);
298 static struct comedi_driver driver = {
299 .driver_name = "pcmmio",
300 .module = THIS_MODULE,
301 .attach = pcmmio_attach,
302 .detach = pcmmio_detach,
303 /* It is not necessary to implement the following members if you are
304 * writing a driver for a ISA PnP or PCI card */
305 /* Most drivers will support multiple types of boards by
306 * having an array of board structures. These were defined
307 * in pcmmio_boards[] above. Note that the element 'name'
308 * was first in the structure -- Comedi uses this fact to
309 * extract the name of the board without knowing any details
310 * about the structure except for its length.
311 * When a device is attached (by comedi_config), the name
312 * of the device is given to Comedi, and Comedi tries to
313 * match it by going through the list of board names. If
314 * there is a match, the address of the pointer is put
315 * into dev->board_ptr and driver->attach() is called.
317 * Note that these are not necessary if you can determine
318 * the type of board in software. ISA PnP, PCI, and PCMCIA
319 * devices are such boards.
321 .board_name = &pcmmio_boards[0].name,
322 .offset = sizeof(struct pcmmio_board),
323 .num_names = ARRAY_SIZE(pcmmio_boards),
326 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
327 struct comedi_subdevice *s,
328 struct comedi_insn *insn, unsigned int *data);
329 static int pcmmio_dio_insn_config(struct comedi_device *dev,
330 struct comedi_subdevice *s,
331 struct comedi_insn *insn, unsigned int *data);
333 static irqreturn_t interrupt_pcmmio(int irq, void *d);
334 static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
335 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
336 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
337 static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
338 struct comedi_cmd *cmd);
340 /* some helper functions to deal with specifics of this device's registers */
341 /* sets up/clears ASIC chips to defaults */
342 static void init_asics(struct comedi_device *dev);
343 static void switch_page(struct comedi_device *dev, int asic, int page);
345 static void lock_port(struct comedi_device *dev, int asic, int port);
346 static void unlock_port(struct comedi_device *dev, int asic, int port);
350 * Attach is called by the Comedi core to configure the driver
351 * for a particular board. If you specified a board_name array
352 * in the driver structure, dev->board_ptr contains that
355 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
357 struct comedi_subdevice *s;
358 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
360 unsigned long iobase;
361 unsigned int irq[MAX_ASICS];
363 iobase = it->options[0];
364 irq[0] = it->options[1];
366 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
369 dev->iobase = iobase;
371 if (!iobase || !request_region(iobase,
372 thisboard->total_iosize,
373 driver.driver_name)) {
374 printk("I/O port conflict\n");
379 * Initialize dev->board_name. Note that we can use the "thisboard"
380 * macro now, since we just initialized it in the last line.
382 dev->board_name = thisboard->name;
385 * Allocate the private structure area. alloc_private() is a
386 * convenient macro defined in comedidev.h.
388 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
389 printk("cannot allocate private data structure\n");
393 for (asic = 0; asic < MAX_ASICS; ++asic) {
394 devpriv->asics[asic].num = asic;
395 devpriv->asics[asic].iobase =
396 dev->iobase + 16 + asic * ASIC_IOSIZE;
398 * this gets actually set at the end of this function when we
401 devpriv->asics[asic].irq = 0;
402 spin_lock_init(&devpriv->asics[asic].spinlock);
405 chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
406 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
407 n_subdevs = n_dio_subdevs + 2;
409 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
411 if (!devpriv->sprivs) {
412 printk("cannot allocate subdevice private data structures\n");
416 * Allocate the subdevice structures. alloc_subdevice() is a
417 * convenient macro defined in comedidev.h.
419 * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
421 if (alloc_subdevices(dev, n_subdevs) < 0) {
422 printk("cannot allocate subdevice data structures\n");
428 s = dev->subdevices + sdev_no;
429 s->private = devpriv->sprivs + sdev_no;
430 s->maxdata = (1 << thisboard->ai_bits) - 1;
431 s->range_table = thisboard->ai_range_table;
432 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
433 s->type = COMEDI_SUBD_AI;
434 s->n_chan = thisboard->n_ai_chans;
435 s->len_chanlist = s->n_chan;
436 s->insn_read = thisboard->ai_rinsn;
437 subpriv->iobase = dev->iobase + 0;
438 /* initialize the resource enable register by clearing it */
439 outb(0, subpriv->iobase + 3);
440 outb(0, subpriv->iobase + 4 + 3);
444 s = dev->subdevices + sdev_no;
445 s->private = devpriv->sprivs + sdev_no;
446 s->maxdata = (1 << thisboard->ao_bits) - 1;
447 s->range_table = thisboard->ao_range_table;
448 s->subdev_flags = SDF_READABLE;
449 s->type = COMEDI_SUBD_AO;
450 s->n_chan = thisboard->n_ao_chans;
451 s->len_chanlist = s->n_chan;
452 s->insn_read = thisboard->ao_rinsn;
453 s->insn_write = thisboard->ao_winsn;
454 subpriv->iobase = dev->iobase + 8;
455 /* initialize the resource enable register by clearing it */
456 outb(0, subpriv->iobase + 3);
457 outb(0, subpriv->iobase + 4 + 3);
462 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
465 s = dev->subdevices + sdev_no;
466 s->private = devpriv->sprivs + sdev_no;
468 s->range_table = &range_digital;
469 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
470 s->type = COMEDI_SUBD_DIO;
471 s->insn_bits = pcmmio_dio_insn_bits;
472 s->insn_config = pcmmio_dio_insn_config;
473 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
474 subpriv->dio.intr.asic = -1;
475 subpriv->dio.intr.first_chan = -1;
476 subpriv->dio.intr.asic_chan = -1;
477 subpriv->dio.intr.num_asic_chans = -1;
478 subpriv->dio.intr.active = 0;
481 /* save the ioport address for each 'port' of 8 channels in the
483 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
484 if (port >= PORTS_PER_ASIC) {
489 subpriv->iobases[byte_no] =
490 devpriv->asics[asic].iobase + port;
492 if (thisasic_chanct <
493 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
494 && subpriv->dio.intr.asic < 0) {
496 * this is an interrupt subdevice,
497 * so setup the struct
499 subpriv->dio.intr.asic = asic;
500 subpriv->dio.intr.active = 0;
501 subpriv->dio.intr.stop_count = 0;
502 subpriv->dio.intr.first_chan = byte_no * 8;
503 subpriv->dio.intr.asic_chan = thisasic_chanct;
504 subpriv->dio.intr.num_asic_chans =
505 s->n_chan - subpriv->dio.intr.first_chan;
506 s->cancel = pcmmio_cancel;
507 s->do_cmd = pcmmio_cmd;
508 s->do_cmdtest = pcmmio_cmdtest;
510 subpriv->dio.intr.num_asic_chans;
512 thisasic_chanct += CHANS_PER_PORT;
514 spin_lock_init(&subpriv->dio.intr.spinlock);
516 chans_left -= s->n_chan;
520 * reset the asic to our first asic,
529 init_asics(dev); /* clear out all the registers, basically */
531 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
533 && request_irq(irq[asic], interrupt_pcmmio,
534 IRQF_SHARED, thisboard->name, dev)) {
536 /* unroll the allocated irqs.. */
537 for (i = asic - 1; i >= 0; --i) {
538 free_irq(irq[i], dev);
539 devpriv->asics[i].irq = irq[i] = 0;
543 devpriv->asics[asic].irq = irq[asic];
546 dev->irq = irq[0]; /*
547 * grr.. wish comedi dev struct supported
552 printk("irq: %u ", irq[0]);
553 if (thisboard->dio_num_asics == 2 && irq[1])
554 printk("second ASIC irq: %u ", irq[1]);
556 printk("(IRQ mode disabled) ");
559 printk("attached\n");
565 * _detach is called to deconfigure a device. It should deallocate
567 * This function is also called when _attach() fails, so it should be
568 * careful not to release resources that were not necessarily
569 * allocated by _attach(). dev->private and dev->subdevices are
570 * deallocated automatically by the core.
572 static int pcmmio_detach(struct comedi_device *dev)
576 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
578 release_region(dev->iobase, thisboard->total_iosize);
580 for (i = 0; i < MAX_ASICS; ++i) {
581 if (devpriv && devpriv->asics[i].irq)
582 free_irq(devpriv->asics[i].irq, dev);
585 if (devpriv && devpriv->sprivs)
586 kfree(devpriv->sprivs);
591 /* DIO devices are slightly special. Although it is possible to
592 * implement the insn_read/insn_write interface, it is much more
593 * useful to applications if you implement the insn_bits interface.
594 * This allows packed reading/writing of the DIO channels. The
595 * comedi core can convert between insn_bits and insn_read/write */
596 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
597 struct comedi_subdevice *s,
598 struct comedi_insn *insn, unsigned int *data)
605 reading a 0 means this channel was high
606 writine a 0 sets the channel high
607 reading a 1 means this channel was low
608 writing a 1 means set this channel low
610 Therefore everything is always inverted. */
612 /* The insn data is a mask in data[0] and the new data
613 * in data[1], each channel cooresponding to a bit. */
615 #ifdef DAMMIT_ITS_BROKEN
617 printk("write mask: %08x data: %08x\n", data[0], data[1]);
622 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
623 /* address of 8-bit port */
624 unsigned long ioaddr = subpriv->iobases[byte_no],
625 /* bit offset of port in 32-bit doubleword */
626 offset = byte_no * 8;
627 /* this 8-bit port's data */
628 unsigned char byte = 0,
629 /* The write mask for this port (if any) */
630 write_mask_byte = (data[0] >> offset) & 0xff,
631 /* The data byte for this port */
632 data_byte = (data[1] >> offset) & 0xff;
634 byte = inb(ioaddr); /* read all 8-bits for this port */
636 #ifdef DAMMIT_ITS_BROKEN
639 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
640 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
641 offset, ioaddr, (unsigned)byte);
644 if (write_mask_byte) {
646 * this byte has some write_bits
647 * -- so set the output lines
649 /* clear bits for write mask */
650 byte &= ~write_mask_byte;
651 /* set to inverted data_byte */
652 byte |= ~data_byte & write_mask_byte;
653 /* Write out the new digital output state */
656 #ifdef DAMMIT_ITS_BROKEN
658 printk("data_out_byte %02x\n", (unsigned)byte);
660 /* save the digital input lines for this byte.. */
661 s->state |= ((unsigned int)byte) << offset;
664 /* now return the DIO lines to data[1] - note they came inverted! */
667 #ifdef DAMMIT_ITS_BROKEN
669 printk("s->state %08x data_out %08x\n", s->state, data[1]);
675 /* The input or output configuration of each digital line is
676 * configured by a special insn_config instruction. chanspec
677 * contains the channel to be changed, and data[0] contains the
678 * value COMEDI_INPUT or COMEDI_OUTPUT. */
679 static int pcmmio_dio_insn_config(struct comedi_device *dev,
680 struct comedi_subdevice *s,
681 struct comedi_insn *insn, unsigned int *data)
683 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
685 unsigned long ioaddr;
688 /* Compute ioaddr for this channel */
689 ioaddr = subpriv->iobases[byte_no];
692 writing a 0 an IO channel's bit sets the channel to INPUT
693 and pulls the line high as well
695 writing a 1 to an IO channel's bit pulls the line low
697 All channels are implicitly always in OUTPUT mode -- but when
698 they are high they can be considered to be in INPUT mode..
700 Thus, we only force channels low if the config request was INPUT,
701 otherwise we do nothing to the hardware. */
704 case INSN_CONFIG_DIO_OUTPUT:
705 /* save to io_bits -- don't actually do anything since
706 all input channels are also output channels... */
707 s->io_bits |= 1 << chan;
709 case INSN_CONFIG_DIO_INPUT:
710 /* write a 0 to the actual register representing the channel
711 to set it to 'input'. 0 means "float high". */
713 byte &= ~(1 << bit_no);
714 /**< set input channel to '0' */
717 * write out byte -- this is the only time we actually affect
718 * the hardware as all channels are implicitly output
719 * -- but input channels are set to float-high
723 /* save to io_bits */
724 s->io_bits &= ~(1 << chan);
727 case INSN_CONFIG_DIO_QUERY:
728 /* retreive from shadow register */
730 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
742 static void init_asics(struct comedi_device *dev)
744 ASIC chip to defaults */
747 for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
749 unsigned long baseaddr = devpriv->asics[asic].iobase;
751 switch_page(dev, asic, 0); /* switch back to page 0 */
753 /* first, clear all the DIO port bits */
754 for (port = 0; port < PORTS_PER_ASIC; ++port)
755 outb(0, baseaddr + REG_PORT0 + port);
757 /* Next, clear all the paged registers for each page */
758 for (page = 1; page < NUM_PAGES; ++page) {
760 /* now clear all the paged registers */
761 switch_page(dev, asic, page);
762 for (reg = FIRST_PAGED_REG;
763 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
764 outb(0, baseaddr + reg);
767 /* DEBUG set rising edge interrupts on port0 of both asics */
768 /*switch_page(dev, asic, PAGE_POL);
769 outb(0xff, baseaddr + REG_POL0);
770 switch_page(dev, asic, PAGE_ENAB);
771 outb(0xff, baseaddr + REG_ENAB0); */
774 /* switch back to default page 0 */
775 switch_page(dev, asic, 0);
779 static void switch_page(struct comedi_device *dev, int asic, int page)
781 if (asic < 0 || asic >= thisboard->dio_num_asics)
782 return; /* paranoia */
783 if (page < 0 || page >= NUM_PAGES)
784 return; /* more paranoia */
786 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
787 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
789 /* now write out the shadow register */
790 outb(devpriv->asics[asic].pagelock,
791 devpriv->asics[asic].iobase + REG_PAGELOCK);
795 static void lock_port(struct comedi_device *dev, int asic, int port)
797 if (asic < 0 || asic >= thisboard->dio_num_asics)
798 return; /* paranoia */
799 if (port < 0 || port >= PORTS_PER_ASIC)
800 return; /* more paranoia */
802 devpriv->asics[asic].pagelock |= 0x1 << port;
803 /* now write out the shadow register */
804 outb(devpriv->asics[asic].pagelock,
805 devpriv->asics[asic].iobase + REG_PAGELOCK);
809 static void unlock_port(struct comedi_device *dev, int asic, int port)
811 if (asic < 0 || asic >= thisboard->dio_num_asics)
812 return; /* paranoia */
813 if (port < 0 || port >= PORTS_PER_ASIC)
814 return; /* more paranoia */
815 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
816 /* now write out the shadow register */
817 outb(devpriv->asics[asic].pagelock,
818 devpriv->asics[asic].iobase + REG_PAGELOCK);
822 static irqreturn_t interrupt_pcmmio(int irq, void *d)
825 struct comedi_device *dev = (struct comedi_device *)d;
827 for (asic = 0; asic < MAX_ASICS; ++asic) {
828 if (irq == devpriv->asics[asic].irq) {
830 unsigned triggered = 0;
831 unsigned long iobase = devpriv->asics[asic].iobase;
832 /* it is an interrupt for ASIC #asic */
833 unsigned char int_pend;
835 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
838 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
842 for (port = 0; port < INTR_PORTS_PER_ASIC;
844 if (int_pend & (0x1 << port)) {
846 io_lines_with_edges = 0;
847 switch_page(dev, asic,
849 io_lines_with_edges =
853 if (io_lines_with_edges)
863 io_lines_with_edges <<
871 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
875 struct comedi_subdevice *s;
877 * TODO here: dispatch io lines to subdevs
881 ("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
882 irq, asic, triggered);
883 for (s = dev->subdevices + 2;
884 s < dev->subdevices + dev->n_subdevices;
887 * this is an interrupt subdev,
888 * and it matches this asic!
890 if (subpriv->dio.intr.asic == asic) {
894 spin_lock_irqsave(&subpriv->dio.
898 oldevents = s->async->events;
900 if (subpriv->dio.intr.active) {
903 subpriv->dio.intr.asic_chan)
920 async->cmd.chanlist_len;
924 ch = CR_CHAN(s->async->cmd.chanlist[n]);
925 if (mytrig & (1U << ch))
928 /* Write the scan to the buffer. */
929 if (comedi_buf_put(s->async, ((short *)&val)[0])
935 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
937 /* Overflow! Stop acquisition!! */
938 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
944 /* Check for end of acquisition. */
945 if (!subpriv->dio.intr.continuous) {
946 /* stop_src == TRIG_COUNT */
947 if (subpriv->dio.intr.stop_count > 0) {
948 subpriv->dio.intr.stop_count--;
949 if (subpriv->dio.intr.stop_count == 0) {
950 s->async->events |= COMEDI_CB_EOA;
951 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
961 spin_unlock_irqrestore
967 comedi_event(dev, s);
978 return IRQ_NONE; /* interrupt from other source */
982 static void pcmmio_stop_intr(struct comedi_device *dev,
983 struct comedi_subdevice *s)
985 int nports, firstport, asic, port;
987 asic = subpriv->dio.intr.asic;
989 return; /* not an interrupt subdev */
991 subpriv->dio.intr.enabled_mask = 0;
992 subpriv->dio.intr.active = 0;
993 s->async->inttrig = 0;
994 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
995 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
996 switch_page(dev, asic, PAGE_ENAB);
997 for (port = firstport; port < firstport + nports; ++port) {
998 /* disable all intrs for this subdev.. */
999 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
1003 static int pcmmio_start_intr(struct comedi_device *dev,
1004 struct comedi_subdevice *s)
1006 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
1007 /* An empty acquisition! */
1008 s->async->events |= COMEDI_CB_EOA;
1009 subpriv->dio.intr.active = 0;
1012 unsigned bits = 0, pol_bits = 0, n;
1013 int nports, firstport, asic, port;
1014 struct comedi_cmd *cmd = &s->async->cmd;
1016 asic = subpriv->dio.intr.asic;
1018 return 1; /* not an interrupt
1020 subpriv->dio.intr.enabled_mask = 0;
1021 subpriv->dio.intr.active = 1;
1022 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
1023 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
1024 if (cmd->chanlist) {
1025 for (n = 0; n < cmd->chanlist_len; n++) {
1026 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
1027 pol_bits |= (CR_AREF(cmd->chanlist[n])
1029 chanlist[n]) ? 1U : 0U)
1030 << CR_CHAN(cmd->chanlist[n]);
1033 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
1034 1) << subpriv->dio.intr.first_chan;
1035 subpriv->dio.intr.enabled_mask = bits;
1039 * the below code configures the board
1040 * to use a specific IRQ from 0-15.
1044 * set resource enable register
1045 * to enable IRQ operation
1047 outb(1 << 4, dev->iobase + 3);
1048 /* set bits 0-3 of b to the irq number from 0-15 */
1049 b = dev->irq & ((1 << 4) - 1);
1050 outb(b, dev->iobase + 2);
1051 /* done, we told the board what irq to use */
1054 switch_page(dev, asic, PAGE_ENAB);
1055 for (port = firstport; port < firstport + nports; ++port) {
1057 bits >> (subpriv->dio.intr.first_chan + (port -
1060 pol_bits >> (subpriv->dio.intr.first_chan +
1061 (port - firstport) * 8) & 0xff;
1062 /* set enab intrs for this subdev.. */
1064 devpriv->asics[asic].iobase + REG_ENAB0 + port);
1065 switch_page(dev, asic, PAGE_POL);
1067 devpriv->asics[asic].iobase + REG_ENAB0 + port);
1073 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
1075 unsigned long flags;
1077 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1078 if (subpriv->dio.intr.active)
1079 pcmmio_stop_intr(dev, s);
1080 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1086 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
1089 pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
1090 unsigned int trignum)
1092 unsigned long flags;
1098 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1099 s->async->inttrig = 0;
1100 if (subpriv->dio.intr.active)
1101 event = pcmmio_start_intr(dev, s);
1102 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1105 comedi_event(dev, s);
1111 * 'do_cmd' function for an 'INTERRUPT' subdevice.
1113 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1115 struct comedi_cmd *cmd = &s->async->cmd;
1116 unsigned long flags;
1119 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1120 subpriv->dio.intr.active = 1;
1122 /* Set up end of acquisition. */
1123 switch (cmd->stop_src) {
1125 subpriv->dio.intr.continuous = 0;
1126 subpriv->dio.intr.stop_count = cmd->stop_arg;
1130 subpriv->dio.intr.continuous = 1;
1131 subpriv->dio.intr.stop_count = 0;
1135 /* Set up start of acquisition. */
1136 switch (cmd->start_src) {
1138 s->async->inttrig = pcmmio_inttrig_start_intr;
1142 event = pcmmio_start_intr(dev, s);
1145 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1148 comedi_event(dev, s);
1154 pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1155 struct comedi_cmd *cmd)
1157 return comedi_pcm_cmdtest(dev, s, cmd);
1160 static int adc_wait_ready(unsigned long iobase)
1162 unsigned long retry = 100000;
1164 if (inb(iobase + 3) & 0x80)
1169 /* All this is for AI and AO */
1170 static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1171 struct comedi_insn *insn, unsigned int *data)
1174 unsigned long iobase = subpriv->iobase;
1177 1. write the CMD byte (to BASE+2)
1178 2. read junk lo byte (BASE+0)
1179 3. read junk hi byte (BASE+1)
1180 4. (mux settled so) write CMD byte again (BASE+2)
1181 5. read valid lo byte(BASE+0)
1182 6. read valid hi byte(BASE+1)
1184 Additionally note that the BASE += 4 if the channel >= 8
1187 /* convert n samples */
1188 for (n = 0; n < insn->n; n++) {
1189 unsigned chan = CR_CHAN(insn->chanspec), range =
1190 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
1191 unsigned char command_byte = 0;
1192 unsigned iooffset = 0;
1193 short sample, adc_adjust = 0;
1196 chan -= 8, iooffset = 4; /*
1197 * use the second dword
1201 if (aref != AREF_DIFF) {
1203 command_byte |= 1 << 7; /*
1204 * set bit 7 to indicate
1209 adc_adjust = 0x8000; /*
1211 * (-5,5 .. -10,10 need to be
1212 * adjusted -- that is.. they
1213 * need to wrap around by
1218 command_byte |= 1 << 6; /*
1219 * odd-numbered channels
1224 /* select the channel, bits 4-5 == chan/2 */
1225 command_byte |= ((chan / 2) & 0x3) << 4;
1227 /* set the range, bits 2-3 */
1228 command_byte |= (range & 0x3) << 2;
1230 /* need to do this twice to make sure mux settled */
1231 /* chan/range/aref select */
1232 outb(command_byte, iobase + iooffset + 2);
1234 /* wait for the adc to say it finised the conversion */
1235 adc_wait_ready(iobase + iooffset);
1237 /* select the chan/range/aref AGAIN */
1238 outb(command_byte, iobase + iooffset + 2);
1240 adc_wait_ready(iobase + iooffset);
1242 /* read data lo byte */
1243 sample = inb(iobase + iooffset + 0);
1245 /* read data hi byte */
1246 sample |= inb(iobase + iooffset + 1) << 8;
1247 sample += adc_adjust; /* adjustment .. munge data */
1250 /* return the number of samples read/written */
1254 static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1255 struct comedi_insn *insn, unsigned int *data)
1258 for (n = 0; n < insn->n; n++) {
1259 unsigned chan = CR_CHAN(insn->chanspec);
1260 if (chan < s->n_chan)
1261 data[n] = subpriv->ao.shadow_samples[chan];
1266 static int wait_dac_ready(unsigned long iobase)
1268 unsigned long retry = 100000L;
1270 /* This may seem like an absurd way to handle waiting and violates the
1271 "no busy waiting" policy. The fact is that the hardware is
1272 normally so fast that we usually only need one time through the loop
1273 anyway. The longer timeout is for rare occasions and for detecting
1274 non-existant hardware. */
1277 if (inb(iobase + 3) & 0x80)
1284 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
1285 struct comedi_insn *insn, unsigned int *data)
1288 unsigned iobase = subpriv->iobase, iooffset = 0;
1290 for (n = 0; n < insn->n; n++) {
1291 unsigned chan = CR_CHAN(insn->chanspec), range =
1292 CR_RANGE(insn->chanspec);
1293 if (chan < s->n_chan) {
1294 unsigned char command_byte = 0, range_byte =
1295 range & ((1 << 4) - 1);
1297 chan -= 4, iooffset += 4;
1298 /* set the range.. */
1299 outb(range_byte, iobase + iooffset + 0);
1300 outb(0, iobase + iooffset + 1);
1302 /* tell it to begin */
1303 command_byte = (chan << 1) | 0x60;
1304 outb(command_byte, iobase + iooffset + 2);
1306 wait_dac_ready(iobase + iooffset);
1308 /* low order byte */
1309 outb(data[n] & 0xff, iobase + iooffset + 0);
1311 /* high order byte */
1312 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
1315 * set bit 4 of command byte to indicate
1316 * data is loaded and trigger conversion
1318 command_byte = 0x70 | (chan << 1);
1319 /* trigger converion */
1320 outb(command_byte, iobase + iooffset + 2);
1322 wait_dac_ready(iobase + iooffset);
1324 /* save to shadow register for ao_rinsn */
1325 subpriv->ao.shadow_samples[chan] = data[n];
1332 * A convenient macro that defines init_module() and cleanup_module(),
1335 COMEDI_INITCLEANUP(driver);