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 <linux/slab.h>
82 #include "../comedidev.h"
84 #include "comedi_fc.h"
86 /* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
87 #define CHANS_PER_PORT 8
88 #define PORTS_PER_ASIC 6
89 #define INTR_PORTS_PER_ASIC 3
90 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
91 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
92 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
93 #define INTR_CHANS_PER_ASIC 24
94 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
95 #define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
96 #define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
97 #define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
99 #define ASIC_IOSIZE (0x0B)
100 #define PCMMIO48_IOSIZE ASIC_IOSIZE
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 /*
123 * page selector register, upper 2 bits select
124 * a page and bits 0-5 are used to 'lock down'
125 * a particular port above to make it readonly.
130 #define REG_ENAB0 0x8
131 #define REG_ENAB1 0x9
132 #define REG_ENAB2 0xA
133 #define REG_INT_ID0 0x8
134 #define REG_INT_ID1 0x9
135 #define REG_INT_ID2 0xA
137 #define NUM_PAGED_REGS 3
139 #define FIRST_PAGED_REG 0x8
140 #define REG_PAGE_BITOFFSET 6
141 #define REG_LOCK_BITOFFSET 0
142 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
143 #define REG_LOCK_MASK (~(REG_PAGE_MASK))
146 #define PAGE_INT_ID 3
148 static const struct comedi_lrange ranges_ai = {
149 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
152 static const struct comedi_lrange ranges_ao = {
153 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
154 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
157 /* this structure is for data unique to this subdevice. */
158 struct pcmmio_subdev_private {
161 /* for DIO: mapping of halfwords (bytes)
162 in port/chanarray to iobase */
163 unsigned long iobases[PORTS_PER_SUBDEV];
166 unsigned long iobase;
171 /* The below is only used for intr subdevices */
174 * if non-negative, this subdev has an
179 * if nonnegative, the first channel id for
184 * the number of asic channels in this subdev
185 * that have interrutps
189 * if nonnegative, the first channel id with
190 * respect to the asic that has interrupts
194 * subdev-relative channel mask for channels
195 * we are interested in
205 /* the last unsigned int data written */
206 unsigned int shadow_samples[8];
212 * this structure is for data unique to this hardware driver. If
213 * several hardware drivers keep similar information in this structure,
214 * feel free to suggest moving the variable to the struct comedi_device struct.
216 struct pcmmio_private {
219 unsigned char pagelock; /* current page and lock */
220 /* shadow of POLx registers */
221 unsigned char pol[NUM_PAGED_REGS];
222 /* shadow of ENABx registers */
223 unsigned char enab[NUM_PAGED_REGS];
225 unsigned long iobase;
229 struct pcmmio_subdev_private *sprivs;
232 #define subpriv ((struct pcmmio_subdev_private *)s->private)
234 /* DIO devices are slightly special. Although it is possible to
235 * implement the insn_read/insn_write interface, it is much more
236 * useful to applications if you implement the insn_bits interface.
237 * This allows packed reading/writing of the DIO channels. The
238 * comedi core can convert between insn_bits and insn_read/write */
239 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
240 struct comedi_subdevice *s,
241 struct comedi_insn *insn, unsigned int *data)
246 reading a 0 means this channel was high
247 writine a 0 sets the channel high
248 reading a 1 means this channel was low
249 writing a 1 means set this channel low
251 Therefore everything is always inverted. */
253 /* The insn data is a mask in data[0] and the new data
254 * in data[1], each channel cooresponding to a bit. */
256 #ifdef DAMMIT_ITS_BROKEN
258 printk(KERN_DEBUG "write mask: %08x data: %08x\n", data[0], data[1]);
263 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
264 /* address of 8-bit port */
265 unsigned long ioaddr = subpriv->iobases[byte_no],
266 /* bit offset of port in 32-bit doubleword */
267 offset = byte_no * 8;
268 /* this 8-bit port's data */
269 unsigned char byte = 0,
270 /* The write mask for this port (if any) */
271 write_mask_byte = (data[0] >> offset) & 0xff,
272 /* The data byte for this port */
273 data_byte = (data[1] >> offset) & 0xff;
275 byte = inb(ioaddr); /* read all 8-bits for this port */
277 #ifdef DAMMIT_ITS_BROKEN
280 (KERN_DEBUG "byte %d wmb %02x db %02x offset %02d io %04x,"
281 " data_in %02x ", byte_no, (unsigned)write_mask_byte,
282 (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
285 if (write_mask_byte) {
287 * this byte has some write_bits
288 * -- so set the output lines
290 /* clear bits for write mask */
291 byte &= ~write_mask_byte;
292 /* set to inverted data_byte */
293 byte |= ~data_byte & write_mask_byte;
294 /* Write out the new digital output state */
297 #ifdef DAMMIT_ITS_BROKEN
299 printk(KERN_DEBUG "data_out_byte %02x\n", (unsigned)byte);
301 /* save the digital input lines for this byte.. */
302 s->state |= ((unsigned int)byte) << offset;
305 /* now return the DIO lines to data[1] - note they came inverted! */
308 #ifdef DAMMIT_ITS_BROKEN
310 printk(KERN_DEBUG "s->state %08x data_out %08x\n", s->state, data[1]);
316 /* The input or output configuration of each digital line is
317 * configured by a special insn_config instruction. chanspec
318 * contains the channel to be changed, and data[0] contains the
319 * value COMEDI_INPUT or COMEDI_OUTPUT. */
320 static int pcmmio_dio_insn_config(struct comedi_device *dev,
321 struct comedi_subdevice *s,
322 struct comedi_insn *insn, unsigned int *data)
324 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
326 unsigned long ioaddr;
329 /* Compute ioaddr for this channel */
330 ioaddr = subpriv->iobases[byte_no];
333 writing a 0 an IO channel's bit sets the channel to INPUT
334 and pulls the line high as well
336 writing a 1 to an IO channel's bit pulls the line low
338 All channels are implicitly always in OUTPUT mode -- but when
339 they are high they can be considered to be in INPUT mode..
341 Thus, we only force channels low if the config request was INPUT,
342 otherwise we do nothing to the hardware. */
345 case INSN_CONFIG_DIO_OUTPUT:
346 /* save to io_bits -- don't actually do anything since
347 all input channels are also output channels... */
348 s->io_bits |= 1 << chan;
350 case INSN_CONFIG_DIO_INPUT:
351 /* write a 0 to the actual register representing the channel
352 to set it to 'input'. 0 means "float high". */
354 byte &= ~(1 << bit_no);
355 /**< set input channel to '0' */
358 * write out byte -- this is the only time we actually affect
359 * the hardware as all channels are implicitly output
360 * -- but input channels are set to float-high
364 /* save to io_bits */
365 s->io_bits &= ~(1 << chan);
368 case INSN_CONFIG_DIO_QUERY:
369 /* retrieve from shadow register */
371 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
383 static void switch_page(struct comedi_device *dev, int asic, int page)
385 struct pcmmio_private *devpriv = dev->private;
387 if (asic < 0 || asic >= 1)
388 return; /* paranoia */
389 if (page < 0 || page >= NUM_PAGES)
390 return; /* more paranoia */
392 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
393 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
395 /* now write out the shadow register */
396 outb(devpriv->asics[asic].pagelock,
397 devpriv->asics[asic].iobase + REG_PAGELOCK);
400 static void init_asics(struct comedi_device *dev)
402 ASIC chip to defaults */
403 struct pcmmio_private *devpriv = dev->private;
406 for (asic = 0; asic < 1; ++asic) {
408 unsigned long baseaddr = devpriv->asics[asic].iobase;
410 switch_page(dev, asic, 0); /* switch back to page 0 */
412 /* first, clear all the DIO port bits */
413 for (port = 0; port < PORTS_PER_ASIC; ++port)
414 outb(0, baseaddr + REG_PORT0 + port);
416 /* Next, clear all the paged registers for each page */
417 for (page = 1; page < NUM_PAGES; ++page) {
419 /* now clear all the paged registers */
420 switch_page(dev, asic, page);
421 for (reg = FIRST_PAGED_REG;
422 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
423 outb(0, baseaddr + reg);
426 /* DEBUG set rising edge interrupts on port0 of both asics */
427 /*switch_page(dev, asic, PAGE_POL);
428 outb(0xff, baseaddr + REG_POL0);
429 switch_page(dev, asic, PAGE_ENAB);
430 outb(0xff, baseaddr + REG_ENAB0); */
433 /* switch back to default page 0 */
434 switch_page(dev, asic, 0);
439 static void lock_port(struct comedi_device *dev, int asic, int port)
441 struct pcmmio_private *devpriv = dev->private;
443 if (asic < 0 || asic >= 1)
444 return; /* paranoia */
445 if (port < 0 || port >= PORTS_PER_ASIC)
446 return; /* more paranoia */
448 devpriv->asics[asic].pagelock |= 0x1 << port;
449 /* now write out the shadow register */
450 outb(devpriv->asics[asic].pagelock,
451 devpriv->asics[asic].iobase + REG_PAGELOCK);
455 static void unlock_port(struct comedi_device *dev, int asic, int port)
457 struct pcmmio_private *devpriv = dev->private;
459 if (asic < 0 || asic >= 1)
460 return; /* paranoia */
461 if (port < 0 || port >= PORTS_PER_ASIC)
462 return; /* more paranoia */
463 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
464 /* now write out the shadow register */
465 outb(devpriv->asics[asic].pagelock,
466 devpriv->asics[asic].iobase + REG_PAGELOCK);
470 static void pcmmio_stop_intr(struct comedi_device *dev,
471 struct comedi_subdevice *s)
473 struct pcmmio_private *devpriv = dev->private;
474 int nports, firstport, asic, port;
476 asic = subpriv->dio.intr.asic;
478 return; /* not an interrupt subdev */
480 subpriv->dio.intr.enabled_mask = 0;
481 subpriv->dio.intr.active = 0;
482 s->async->inttrig = NULL;
483 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
484 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
485 switch_page(dev, asic, PAGE_ENAB);
486 for (port = firstport; port < firstport + nports; ++port) {
487 /* disable all intrs for this subdev.. */
488 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
492 static irqreturn_t interrupt_pcmmio(int irq, void *d)
495 struct comedi_device *dev = (struct comedi_device *)d;
496 struct pcmmio_private *devpriv = dev->private;
499 for (asic = 0; asic < MAX_ASICS; ++asic) {
500 if (irq == devpriv->asics[asic].irq) {
502 unsigned triggered = 0;
503 unsigned long iobase = devpriv->asics[asic].iobase;
504 /* it is an interrupt for ASIC #asic */
505 unsigned char int_pend;
507 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
510 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
514 for (port = 0; port < INTR_PORTS_PER_ASIC;
516 if (int_pend & (0x1 << port)) {
518 io_lines_with_edges = 0;
519 switch_page(dev, asic,
521 io_lines_with_edges =
525 if (io_lines_with_edges)
535 io_lines_with_edges <<
543 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
547 struct comedi_subdevice *s;
549 * TODO here: dispatch io lines to subdevs
553 (KERN_DEBUG "got edge detect interrupt %d asic %d which_chans: %06x\n",
554 irq, asic, triggered);
555 for (i = 2; i < dev->n_subdevices; i++) {
556 s = &dev->subdevices[i];
558 * this is an interrupt subdev,
559 * and it matches this asic!
561 if (subpriv->dio.intr.asic == asic) {
565 spin_lock_irqsave(&subpriv->dio.
569 oldevents = s->async->events;
571 if (subpriv->dio.intr.active) {
574 subpriv->dio.intr.asic_chan)
591 async->cmd.chanlist_len;
595 ch = CR_CHAN(s->async->cmd.chanlist[n]);
596 if (mytrig & (1U << ch))
599 /* Write the scan to the buffer. */
600 if (comedi_buf_put(s->async, ((short *)&val)[0])
606 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
608 /* Overflow! Stop acquisition!! */
609 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
615 /* Check for end of acquisition. */
616 if (!subpriv->dio.intr.continuous) {
617 /* stop_src == TRIG_COUNT */
618 if (subpriv->dio.intr.stop_count > 0) {
619 subpriv->dio.intr.stop_count--;
620 if (subpriv->dio.intr.stop_count == 0) {
621 s->async->events |= COMEDI_CB_EOA;
622 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
632 spin_unlock_irqrestore
638 comedi_event(dev, s);
649 return IRQ_NONE; /* interrupt from other source */
653 static int pcmmio_start_intr(struct comedi_device *dev,
654 struct comedi_subdevice *s)
656 struct pcmmio_private *devpriv = dev->private;
658 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
659 /* An empty acquisition! */
660 s->async->events |= COMEDI_CB_EOA;
661 subpriv->dio.intr.active = 0;
664 unsigned bits = 0, pol_bits = 0, n;
665 int nports, firstport, asic, port;
666 struct comedi_cmd *cmd = &s->async->cmd;
668 asic = subpriv->dio.intr.asic;
670 return 1; /* not an interrupt
672 subpriv->dio.intr.enabled_mask = 0;
673 subpriv->dio.intr.active = 1;
674 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
675 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
677 for (n = 0; n < cmd->chanlist_len; n++) {
678 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
679 pol_bits |= (CR_AREF(cmd->chanlist[n])
681 chanlist[n]) ? 1U : 0U)
682 << CR_CHAN(cmd->chanlist[n]);
685 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
686 1) << subpriv->dio.intr.first_chan;
687 subpriv->dio.intr.enabled_mask = bits;
691 * the below code configures the board
692 * to use a specific IRQ from 0-15.
696 * set resource enable register
697 * to enable IRQ operation
699 outb(1 << 4, dev->iobase + 3);
700 /* set bits 0-3 of b to the irq number from 0-15 */
701 b = dev->irq & ((1 << 4) - 1);
702 outb(b, dev->iobase + 2);
703 /* done, we told the board what irq to use */
706 switch_page(dev, asic, PAGE_ENAB);
707 for (port = firstport; port < firstport + nports; ++port) {
709 bits >> (subpriv->dio.intr.first_chan + (port -
712 pol_bits >> (subpriv->dio.intr.first_chan +
713 (port - firstport) * 8) & 0xff;
714 /* set enab intrs for this subdev.. */
716 devpriv->asics[asic].iobase + REG_ENAB0 + port);
717 switch_page(dev, asic, PAGE_POL);
719 devpriv->asics[asic].iobase + REG_ENAB0 + port);
725 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
729 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
730 if (subpriv->dio.intr.active)
731 pcmmio_stop_intr(dev, s);
732 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
738 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
741 pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
742 unsigned int trignum)
750 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
751 s->async->inttrig = NULL;
752 if (subpriv->dio.intr.active)
753 event = pcmmio_start_intr(dev, s);
754 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
757 comedi_event(dev, s);
763 * 'do_cmd' function for an 'INTERRUPT' subdevice.
765 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
767 struct comedi_cmd *cmd = &s->async->cmd;
771 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
772 subpriv->dio.intr.active = 1;
774 /* Set up end of acquisition. */
775 switch (cmd->stop_src) {
777 subpriv->dio.intr.continuous = 0;
778 subpriv->dio.intr.stop_count = cmd->stop_arg;
782 subpriv->dio.intr.continuous = 1;
783 subpriv->dio.intr.stop_count = 0;
787 /* Set up start of acquisition. */
788 switch (cmd->start_src) {
790 s->async->inttrig = pcmmio_inttrig_start_intr;
794 event = pcmmio_start_intr(dev, s);
797 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
800 comedi_event(dev, s);
805 static int pcmmio_cmdtest(struct comedi_device *dev,
806 struct comedi_subdevice *s,
807 struct comedi_cmd *cmd)
811 /* Step 1 : check if triggers are trivially valid */
813 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
814 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
815 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
816 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
817 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
822 /* Step 2a : make sure trigger sources are unique */
824 err |= cfc_check_trigger_is_unique(cmd->start_src);
825 err |= cfc_check_trigger_is_unique(cmd->stop_src);
827 /* Step 2b : and mutually compatible */
832 /* Step 3: check if arguments are trivially valid */
834 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
835 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
836 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
837 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
839 switch (cmd->stop_src) {
841 /* any count allowed */
844 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
853 /* step 4: fix up any arguments */
855 /* if (err) return 4; */
860 static int adc_wait_ready(unsigned long iobase)
862 unsigned long retry = 100000;
864 if (inb(iobase + 3) & 0x80)
869 /* All this is for AI and AO */
870 static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
871 struct comedi_insn *insn, unsigned int *data)
874 unsigned long iobase = subpriv->iobase;
877 1. write the CMD byte (to BASE+2)
878 2. read junk lo byte (BASE+0)
879 3. read junk hi byte (BASE+1)
880 4. (mux settled so) write CMD byte again (BASE+2)
881 5. read valid lo byte(BASE+0)
882 6. read valid hi byte(BASE+1)
884 Additionally note that the BASE += 4 if the channel >= 8
887 /* convert n samples */
888 for (n = 0; n < insn->n; n++) {
889 unsigned chan = CR_CHAN(insn->chanspec), range =
890 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
891 unsigned char command_byte = 0;
892 unsigned iooffset = 0;
893 short sample, adc_adjust = 0;
896 chan -= 8, iooffset = 4; /*
897 * use the second dword
901 if (aref != AREF_DIFF) {
903 command_byte |= 1 << 7; /*
904 * set bit 7 to indicate
909 adc_adjust = 0x8000; /*
911 * (-5,5 .. -10,10 need to be
912 * adjusted -- that is.. they
913 * need to wrap around by
918 command_byte |= 1 << 6; /*
919 * odd-numbered channels
924 /* select the channel, bits 4-5 == chan/2 */
925 command_byte |= ((chan / 2) & 0x3) << 4;
927 /* set the range, bits 2-3 */
928 command_byte |= (range & 0x3) << 2;
930 /* need to do this twice to make sure mux settled */
931 /* chan/range/aref select */
932 outb(command_byte, iobase + iooffset + 2);
934 /* wait for the adc to say it finised the conversion */
935 adc_wait_ready(iobase + iooffset);
937 /* select the chan/range/aref AGAIN */
938 outb(command_byte, iobase + iooffset + 2);
940 adc_wait_ready(iobase + iooffset);
942 /* read data lo byte */
943 sample = inb(iobase + iooffset + 0);
945 /* read data hi byte */
946 sample |= inb(iobase + iooffset + 1) << 8;
947 sample += adc_adjust; /* adjustment .. munge data */
950 /* return the number of samples read/written */
954 static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
955 struct comedi_insn *insn, unsigned int *data)
958 for (n = 0; n < insn->n; n++) {
959 unsigned chan = CR_CHAN(insn->chanspec);
960 if (chan < s->n_chan)
961 data[n] = subpriv->ao.shadow_samples[chan];
966 static int wait_dac_ready(unsigned long iobase)
968 unsigned long retry = 100000L;
970 /* This may seem like an absurd way to handle waiting and violates the
971 "no busy waiting" policy. The fact is that the hardware is
972 normally so fast that we usually only need one time through the loop
973 anyway. The longer timeout is for rare occasions and for detecting
974 non-existent hardware. */
977 if (inb(iobase + 3) & 0x80)
984 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
985 struct comedi_insn *insn, unsigned int *data)
988 unsigned iobase = subpriv->iobase, iooffset = 0;
990 for (n = 0; n < insn->n; n++) {
991 unsigned chan = CR_CHAN(insn->chanspec), range =
992 CR_RANGE(insn->chanspec);
993 if (chan < s->n_chan) {
994 unsigned char command_byte = 0, range_byte =
995 range & ((1 << 4) - 1);
997 chan -= 4, iooffset += 4;
998 /* set the range.. */
999 outb(range_byte, iobase + iooffset + 0);
1000 outb(0, iobase + iooffset + 1);
1002 /* tell it to begin */
1003 command_byte = (chan << 1) | 0x60;
1004 outb(command_byte, iobase + iooffset + 2);
1006 wait_dac_ready(iobase + iooffset);
1008 /* low order byte */
1009 outb(data[n] & 0xff, iobase + iooffset + 0);
1011 /* high order byte */
1012 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
1015 * set bit 4 of command byte to indicate
1016 * data is loaded and trigger conversion
1018 command_byte = 0x70 | (chan << 1);
1019 /* trigger converion */
1020 outb(command_byte, iobase + iooffset + 2);
1022 wait_dac_ready(iobase + iooffset);
1024 /* save to shadow register for ao_rinsn */
1025 subpriv->ao.shadow_samples[chan] = data[n];
1031 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1033 struct pcmmio_private *devpriv;
1034 struct comedi_subdevice *s;
1035 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
1036 thisasic_chanct = 0;
1037 unsigned int irq[MAX_ASICS];
1040 irq[0] = it->options[1];
1042 ret = comedi_request_region(dev, it->options[0], 32);
1046 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
1049 dev->private = devpriv;
1051 for (asic = 0; asic < MAX_ASICS; ++asic) {
1052 devpriv->asics[asic].num = asic;
1053 devpriv->asics[asic].iobase =
1054 dev->iobase + 16 + asic * ASIC_IOSIZE;
1056 * this gets actually set at the end of this function when we
1059 devpriv->asics[asic].irq = 0;
1060 spin_lock_init(&devpriv->asics[asic].spinlock);
1063 chans_left = CHANS_PER_ASIC * 1;
1064 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
1065 n_subdevs = n_dio_subdevs + 2;
1067 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
1069 if (!devpriv->sprivs) {
1070 printk(KERN_ERR "comedi%d: cannot allocate subdevice private data structures\n",
1075 ret = comedi_alloc_subdevices(dev, n_subdevs);
1080 s = &dev->subdevices[0];
1081 s->private = &devpriv->sprivs[0];
1082 s->maxdata = 0xffff;
1083 s->range_table = &ranges_ai;
1084 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
1085 s->type = COMEDI_SUBD_AI;
1087 s->len_chanlist = s->n_chan;
1088 s->insn_read = ai_rinsn;
1089 subpriv->iobase = dev->iobase + 0;
1090 /* initialize the resource enable register by clearing it */
1091 outb(0, subpriv->iobase + 3);
1092 outb(0, subpriv->iobase + 4 + 3);
1095 s = &dev->subdevices[1];
1096 s->private = &devpriv->sprivs[1];
1097 s->maxdata = 0xffff;
1098 s->range_table = &ranges_ao;
1099 s->subdev_flags = SDF_READABLE;
1100 s->type = COMEDI_SUBD_AO;
1102 s->len_chanlist = s->n_chan;
1103 s->insn_read = ao_rinsn;
1104 s->insn_write = ao_winsn;
1105 subpriv->iobase = dev->iobase + 8;
1106 /* initialize the resource enable register by clearing it */
1107 outb(0, subpriv->iobase + 3);
1108 outb(0, subpriv->iobase + 4 + 3);
1112 for (sdev_no = 2; sdev_no < dev->n_subdevices; ++sdev_no) {
1115 s = &dev->subdevices[sdev_no];
1116 s->private = &devpriv->sprivs[sdev_no];
1118 s->range_table = &range_digital;
1119 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1120 s->type = COMEDI_SUBD_DIO;
1121 s->insn_bits = pcmmio_dio_insn_bits;
1122 s->insn_config = pcmmio_dio_insn_config;
1123 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
1124 subpriv->dio.intr.asic = -1;
1125 subpriv->dio.intr.first_chan = -1;
1126 subpriv->dio.intr.asic_chan = -1;
1127 subpriv->dio.intr.num_asic_chans = -1;
1128 subpriv->dio.intr.active = 0;
1129 s->len_chanlist = 1;
1131 /* save the ioport address for each 'port' of 8 channels in the
1133 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
1134 if (port >= PORTS_PER_ASIC) {
1137 thisasic_chanct = 0;
1139 subpriv->iobases[byte_no] =
1140 devpriv->asics[asic].iobase + port;
1142 if (thisasic_chanct <
1143 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
1144 && subpriv->dio.intr.asic < 0) {
1146 * this is an interrupt subdevice,
1147 * so setup the struct
1149 subpriv->dio.intr.asic = asic;
1150 subpriv->dio.intr.active = 0;
1151 subpriv->dio.intr.stop_count = 0;
1152 subpriv->dio.intr.first_chan = byte_no * 8;
1153 subpriv->dio.intr.asic_chan = thisasic_chanct;
1154 subpriv->dio.intr.num_asic_chans =
1155 s->n_chan - subpriv->dio.intr.first_chan;
1156 s->cancel = pcmmio_cancel;
1157 s->do_cmd = pcmmio_cmd;
1158 s->do_cmdtest = pcmmio_cmdtest;
1160 subpriv->dio.intr.num_asic_chans;
1162 thisasic_chanct += CHANS_PER_PORT;
1164 spin_lock_init(&subpriv->dio.intr.spinlock);
1166 chans_left -= s->n_chan;
1170 * reset the asic to our first asic,
1171 * to do intr subdevs
1179 init_asics(dev); /* clear out all the registers, basically */
1181 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
1183 && request_irq(irq[asic], interrupt_pcmmio,
1184 IRQF_SHARED, dev->board_name, dev)) {
1186 /* unroll the allocated irqs.. */
1187 for (i = asic - 1; i >= 0; --i) {
1188 free_irq(irq[i], dev);
1189 devpriv->asics[i].irq = irq[i] = 0;
1193 devpriv->asics[asic].irq = irq[asic];
1199 static void pcmmio_detach(struct comedi_device *dev)
1201 struct pcmmio_private *devpriv = dev->private;
1204 for (i = 0; i < MAX_ASICS; ++i) {
1205 if (devpriv && devpriv->asics[i].irq)
1206 free_irq(devpriv->asics[i].irq, dev);
1208 if (devpriv && devpriv->sprivs)
1209 kfree(devpriv->sprivs);
1210 comedi_legacy_detach(dev);
1213 static struct comedi_driver pcmmio_driver = {
1214 .driver_name = "pcmmio",
1215 .module = THIS_MODULE,
1216 .attach = pcmmio_attach,
1217 .detach = pcmmio_detach,
1219 module_comedi_driver(pcmmio_driver);
1221 MODULE_AUTHOR("Comedi http://www.comedi.org");
1222 MODULE_DESCRIPTION("Comedi low-level driver");
1223 MODULE_LICENSE("GPL");