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.
20 Description: A driver for the PCM-MIO multifunction board
21 Devices: [Winsystems] PCM-MIO (pcmmio)
22 Author: Calin Culianu <calin@ajvar.org>
23 Updated: Wed, May 16 2007 16:21:10 -0500
26 A driver for the relatively new PCM-MIO multifunction board from
27 Winsystems. This board is a PC-104 based I/O board. It contains
29 subdevice 0 - 16 channels of 16-bit AI
30 subdevice 1 - 8 channels of 16-bit AO
31 subdevice 2 - first 24 channels of the 48 channel of DIO
32 (with edge-triggered interrupt support)
33 subdevice 3 - last 24 channels of the 48 channel DIO
34 (no interrupt support for this bank of channels)
38 Synchronous reads and writes are the only things implemented for AI and AO,
39 even though the hardware itself can do streaming acquisition, etc. Anyone
40 want to add asynchronous I/O for AI/AO as a feature? Be my guest...
42 Asynchronous I/O for the DIO subdevices *is* implemented, however! They are
43 basically edge-triggered interrupts for any configuration of the first
46 Also note that this interrupt support is untested.
48 A few words about edge-detection IRQ support (commands on DIO):
50 * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
51 of the board to the comedi_config command. The board IRQ is not jumpered
52 but rather configured through software, so any IRQ from 1-15 is OK.
54 * Due to the genericity of the comedi API, you need to create a special
55 comedi_command in order to use edge-triggered interrupts for DIO.
57 * Use comedi_commands with TRIG_NOW. Your callback will be called each
58 time an edge is detected on the specified DIO line(s), and the data
59 values will be two sample_t's, which should be concatenated to form
60 one 32-bit unsigned int. This value is the mask of channels that had
61 edges detected from your channel list. Note that the bits positions
62 in the mask correspond to positions in your chanlist when you
63 specified the command and *not* channel id's!
65 * To set the polarity of the edge-detection interrupts pass a nonzero value
66 for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
67 value for both CR_RANGE and CR_AREF if you want edge-down polarity.
69 Configuration Options:
70 [0] - I/O port base address
71 [1] - IRQ (optional -- for edge-detect interrupt support only,
72 leave out if you don't need this feature)
75 #include <linux/module.h>
76 #include <linux/interrupt.h>
77 #include <linux/slab.h>
79 #include "../comedidev.h"
81 #include "comedi_fc.h"
83 /* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
84 #define CHANS_PER_PORT 8
85 #define PORTS_PER_ASIC 6
86 #define INTR_PORTS_PER_ASIC 3
87 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
88 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
89 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
90 #define INTR_CHANS_PER_ASIC 24
91 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
92 #define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
93 #define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
94 #define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
96 #define ASIC_IOSIZE (0x0B)
97 #define PCMMIO48_IOSIZE ASIC_IOSIZE
99 /* Some offsets - these are all in the 16byte IO memory offset from
100 the base address. Note that there is a paging scheme to swap out
101 offsets 0x8-0xA using the PAGELOCK register. See the table below.
103 Register(s) Pages R/W? Description
104 --------------------------------------------------------------
105 REG_PORTx All R/W Read/Write/Configure IO
106 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
107 REG_PAGELOCK All WriteOnly Select a page
108 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
109 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
110 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
112 #define REG_PORT0 0x0
113 #define REG_PORT1 0x1
114 #define REG_PORT2 0x2
115 #define REG_PORT3 0x3
116 #define REG_PORT4 0x4
117 #define REG_PORT5 0x5
118 #define REG_INT_PENDING 0x6
119 #define REG_PAGELOCK 0x7 /*
120 * page selector register, upper 2 bits select
121 * a page and bits 0-5 are used to 'lock down'
122 * a particular port above to make it readonly.
127 #define REG_ENAB0 0x8
128 #define REG_ENAB1 0x9
129 #define REG_ENAB2 0xA
130 #define REG_INT_ID0 0x8
131 #define REG_INT_ID1 0x9
132 #define REG_INT_ID2 0xA
134 #define NUM_PAGED_REGS 3
136 #define FIRST_PAGED_REG 0x8
137 #define REG_PAGE_BITOFFSET 6
138 #define REG_LOCK_BITOFFSET 0
139 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
140 #define REG_LOCK_MASK (~(REG_PAGE_MASK))
143 #define PAGE_INT_ID 3
145 static const struct comedi_lrange ranges_ai = {
146 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
149 static const struct comedi_lrange ranges_ao = {
150 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
151 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
154 /* this structure is for data unique to this subdevice. */
155 struct pcmmio_subdev_private {
158 /* for DIO: mapping of halfwords (bytes)
159 in port/chanarray to iobase */
160 unsigned long iobases[PORTS_PER_SUBDEV];
163 unsigned long iobase;
168 /* The below is only used for intr subdevices */
171 * if non-negative, this subdev has an
176 * if nonnegative, the first channel id for
181 * the number of asic channels in this subdev
182 * that have interrutps
186 * if nonnegative, the first channel id with
187 * respect to the asic that has interrupts
191 * subdev-relative channel mask for channels
192 * we are interested in
202 /* the last unsigned int data written */
203 unsigned int shadow_samples[8];
209 * this structure is for data unique to this hardware driver. If
210 * several hardware drivers keep similar information in this structure,
211 * feel free to suggest moving the variable to the struct comedi_device struct.
213 struct pcmmio_private {
216 unsigned char pagelock; /* current page and lock */
217 /* shadow of POLx registers */
218 unsigned char pol[NUM_PAGED_REGS];
219 /* shadow of ENABx registers */
220 unsigned char enab[NUM_PAGED_REGS];
222 unsigned long iobase;
226 struct pcmmio_subdev_private *sprivs;
229 #define subpriv ((struct pcmmio_subdev_private *)s->private)
231 /* DIO devices are slightly special. Although it is possible to
232 * implement the insn_read/insn_write interface, it is much more
233 * useful to applications if you implement the insn_bits interface.
234 * This allows packed reading/writing of the DIO channels. The
235 * comedi core can convert between insn_bits and insn_read/write */
236 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
237 struct comedi_subdevice *s,
238 struct comedi_insn *insn, unsigned int *data)
243 reading a 0 means this channel was high
244 writine a 0 sets the channel high
245 reading a 1 means this channel was low
246 writing a 1 means set this channel low
248 Therefore everything is always inverted. */
250 /* The insn data is a mask in data[0] and the new data
251 * in data[1], each channel cooresponding to a bit. */
253 #ifdef DAMMIT_ITS_BROKEN
255 printk(KERN_DEBUG "write mask: %08x data: %08x\n", data[0], data[1]);
260 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
261 /* address of 8-bit port */
262 unsigned long ioaddr = subpriv->iobases[byte_no],
263 /* bit offset of port in 32-bit doubleword */
264 offset = byte_no * 8;
265 /* this 8-bit port's data */
266 unsigned char byte = 0,
267 /* The write mask for this port (if any) */
268 write_mask_byte = (data[0] >> offset) & 0xff,
269 /* The data byte for this port */
270 data_byte = (data[1] >> offset) & 0xff;
272 byte = inb(ioaddr); /* read all 8-bits for this port */
274 #ifdef DAMMIT_ITS_BROKEN
277 (KERN_DEBUG "byte %d wmb %02x db %02x offset %02d io %04x,"
278 " data_in %02x ", byte_no, (unsigned)write_mask_byte,
279 (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
282 if (write_mask_byte) {
284 * this byte has some write_bits
285 * -- so set the output lines
287 /* clear bits for write mask */
288 byte &= ~write_mask_byte;
289 /* set to inverted data_byte */
290 byte |= ~data_byte & write_mask_byte;
291 /* Write out the new digital output state */
294 #ifdef DAMMIT_ITS_BROKEN
296 printk(KERN_DEBUG "data_out_byte %02x\n", (unsigned)byte);
298 /* save the digital input lines for this byte.. */
299 s->state |= ((unsigned int)byte) << offset;
302 /* now return the DIO lines to data[1] - note they came inverted! */
305 #ifdef DAMMIT_ITS_BROKEN
307 printk(KERN_DEBUG "s->state %08x data_out %08x\n", s->state, data[1]);
313 static int pcmmio_dio_insn_config(struct comedi_device *dev,
314 struct comedi_subdevice *s,
315 struct comedi_insn *insn,
318 unsigned int chan = CR_CHAN(insn->chanspec);
319 int byte_no = chan / 8;
320 int bit_no = chan % 8;
323 ret = comedi_dio_insn_config(dev, s, insn, data, 0);
327 if (data[0] == INSN_CONFIG_DIO_INPUT) {
328 unsigned long ioaddr = subpriv->iobases[byte_no];
332 val &= ~(1 << bit_no);
339 static void switch_page(struct comedi_device *dev, int asic, int page)
341 struct pcmmio_private *devpriv = dev->private;
343 if (asic < 0 || asic >= 1)
344 return; /* paranoia */
345 if (page < 0 || page >= NUM_PAGES)
346 return; /* more paranoia */
348 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
349 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
351 /* now write out the shadow register */
352 outb(devpriv->asics[asic].pagelock,
353 devpriv->asics[asic].iobase + REG_PAGELOCK);
356 static void init_asics(struct comedi_device *dev)
358 ASIC chip to defaults */
359 struct pcmmio_private *devpriv = dev->private;
362 for (asic = 0; asic < 1; ++asic) {
364 unsigned long baseaddr = devpriv->asics[asic].iobase;
366 switch_page(dev, asic, 0); /* switch back to page 0 */
368 /* first, clear all the DIO port bits */
369 for (port = 0; port < PORTS_PER_ASIC; ++port)
370 outb(0, baseaddr + REG_PORT0 + port);
372 /* Next, clear all the paged registers for each page */
373 for (page = 1; page < NUM_PAGES; ++page) {
375 /* now clear all the paged registers */
376 switch_page(dev, asic, page);
377 for (reg = FIRST_PAGED_REG;
378 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
379 outb(0, baseaddr + reg);
382 /* DEBUG set rising edge interrupts on port0 of both asics */
383 /*switch_page(dev, asic, PAGE_POL);
384 outb(0xff, baseaddr + REG_POL0);
385 switch_page(dev, asic, PAGE_ENAB);
386 outb(0xff, baseaddr + REG_ENAB0); */
389 /* switch back to default page 0 */
390 switch_page(dev, asic, 0);
395 static void lock_port(struct comedi_device *dev, int asic, int port)
397 struct pcmmio_private *devpriv = dev->private;
399 if (asic < 0 || asic >= 1)
400 return; /* paranoia */
401 if (port < 0 || port >= PORTS_PER_ASIC)
402 return; /* more paranoia */
404 devpriv->asics[asic].pagelock |= 0x1 << port;
405 /* now write out the shadow register */
406 outb(devpriv->asics[asic].pagelock,
407 devpriv->asics[asic].iobase + REG_PAGELOCK);
411 static void unlock_port(struct comedi_device *dev, int asic, int port)
413 struct pcmmio_private *devpriv = dev->private;
415 if (asic < 0 || asic >= 1)
416 return; /* paranoia */
417 if (port < 0 || port >= PORTS_PER_ASIC)
418 return; /* more paranoia */
419 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
420 /* now write out the shadow register */
421 outb(devpriv->asics[asic].pagelock,
422 devpriv->asics[asic].iobase + REG_PAGELOCK);
426 static void pcmmio_stop_intr(struct comedi_device *dev,
427 struct comedi_subdevice *s)
429 struct pcmmio_private *devpriv = dev->private;
430 int nports, firstport, asic, port;
432 asic = subpriv->dio.intr.asic;
434 return; /* not an interrupt subdev */
436 subpriv->dio.intr.enabled_mask = 0;
437 subpriv->dio.intr.active = 0;
438 s->async->inttrig = NULL;
439 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
440 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
441 switch_page(dev, asic, PAGE_ENAB);
442 for (port = firstport; port < firstport + nports; ++port) {
443 /* disable all intrs for this subdev.. */
444 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
448 static irqreturn_t interrupt_pcmmio(int irq, void *d)
451 struct comedi_device *dev = (struct comedi_device *)d;
452 struct pcmmio_private *devpriv = dev->private;
455 for (asic = 0; asic < MAX_ASICS; ++asic) {
456 if (irq == devpriv->asics[asic].irq) {
458 unsigned triggered = 0;
459 unsigned long iobase = devpriv->asics[asic].iobase;
460 /* it is an interrupt for ASIC #asic */
461 unsigned char int_pend;
463 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
466 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
470 for (port = 0; port < INTR_PORTS_PER_ASIC;
472 if (int_pend & (0x1 << port)) {
474 io_lines_with_edges = 0;
475 switch_page(dev, asic,
477 io_lines_with_edges =
481 if (io_lines_with_edges)
491 io_lines_with_edges <<
499 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
503 struct comedi_subdevice *s;
505 * TODO here: dispatch io lines to subdevs
509 (KERN_DEBUG "got edge detect interrupt %d asic %d which_chans: %06x\n",
510 irq, asic, triggered);
511 for (i = 2; i < dev->n_subdevices; i++) {
512 s = &dev->subdevices[i];
514 * this is an interrupt subdev,
515 * and it matches this asic!
517 if (subpriv->dio.intr.asic == asic) {
521 spin_lock_irqsave(&subpriv->dio.
525 oldevents = s->async->events;
527 if (subpriv->dio.intr.active) {
530 subpriv->dio.intr.asic_chan)
547 async->cmd.chanlist_len;
551 ch = CR_CHAN(s->async->cmd.chanlist[n]);
552 if (mytrig & (1U << ch))
555 /* Write the scan to the buffer. */
556 if (comedi_buf_put(s->async, ((short *)&val)[0])
562 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
564 /* Overflow! Stop acquisition!! */
565 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
571 /* Check for end of acquisition. */
572 if (!subpriv->dio.intr.continuous) {
573 /* stop_src == TRIG_COUNT */
574 if (subpriv->dio.intr.stop_count > 0) {
575 subpriv->dio.intr.stop_count--;
576 if (subpriv->dio.intr.stop_count == 0) {
577 s->async->events |= COMEDI_CB_EOA;
578 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
588 spin_unlock_irqrestore
594 comedi_event(dev, s);
605 return IRQ_NONE; /* interrupt from other source */
609 static int pcmmio_start_intr(struct comedi_device *dev,
610 struct comedi_subdevice *s)
612 struct pcmmio_private *devpriv = dev->private;
614 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
615 /* An empty acquisition! */
616 s->async->events |= COMEDI_CB_EOA;
617 subpriv->dio.intr.active = 0;
620 unsigned bits = 0, pol_bits = 0, n;
621 int nports, firstport, asic, port;
622 struct comedi_cmd *cmd = &s->async->cmd;
624 asic = subpriv->dio.intr.asic;
626 return 1; /* not an interrupt
628 subpriv->dio.intr.enabled_mask = 0;
629 subpriv->dio.intr.active = 1;
630 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
631 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
633 for (n = 0; n < cmd->chanlist_len; n++) {
634 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
635 pol_bits |= (CR_AREF(cmd->chanlist[n])
637 chanlist[n]) ? 1U : 0U)
638 << CR_CHAN(cmd->chanlist[n]);
641 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
642 1) << subpriv->dio.intr.first_chan;
643 subpriv->dio.intr.enabled_mask = bits;
647 * the below code configures the board
648 * to use a specific IRQ from 0-15.
652 * set resource enable register
653 * to enable IRQ operation
655 outb(1 << 4, dev->iobase + 3);
656 /* set bits 0-3 of b to the irq number from 0-15 */
657 b = dev->irq & ((1 << 4) - 1);
658 outb(b, dev->iobase + 2);
659 /* done, we told the board what irq to use */
662 switch_page(dev, asic, PAGE_ENAB);
663 for (port = firstport; port < firstport + nports; ++port) {
665 bits >> (subpriv->dio.intr.first_chan + (port -
668 pol_bits >> (subpriv->dio.intr.first_chan +
669 (port - firstport) * 8) & 0xff;
670 /* set enab intrs for this subdev.. */
672 devpriv->asics[asic].iobase + REG_ENAB0 + port);
673 switch_page(dev, asic, PAGE_POL);
675 devpriv->asics[asic].iobase + REG_ENAB0 + port);
681 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
685 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
686 if (subpriv->dio.intr.active)
687 pcmmio_stop_intr(dev, s);
688 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
694 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
697 pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
698 unsigned int trignum)
706 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
707 s->async->inttrig = NULL;
708 if (subpriv->dio.intr.active)
709 event = pcmmio_start_intr(dev, s);
710 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
713 comedi_event(dev, s);
719 * 'do_cmd' function for an 'INTERRUPT' subdevice.
721 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
723 struct comedi_cmd *cmd = &s->async->cmd;
727 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
728 subpriv->dio.intr.active = 1;
730 /* Set up end of acquisition. */
731 switch (cmd->stop_src) {
733 subpriv->dio.intr.continuous = 0;
734 subpriv->dio.intr.stop_count = cmd->stop_arg;
738 subpriv->dio.intr.continuous = 1;
739 subpriv->dio.intr.stop_count = 0;
743 /* Set up start of acquisition. */
744 switch (cmd->start_src) {
746 s->async->inttrig = pcmmio_inttrig_start_intr;
750 event = pcmmio_start_intr(dev, s);
753 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
756 comedi_event(dev, s);
761 static int pcmmio_cmdtest(struct comedi_device *dev,
762 struct comedi_subdevice *s,
763 struct comedi_cmd *cmd)
767 /* Step 1 : check if triggers are trivially valid */
769 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
770 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
771 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
772 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
773 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
778 /* Step 2a : make sure trigger sources are unique */
780 err |= cfc_check_trigger_is_unique(cmd->start_src);
781 err |= cfc_check_trigger_is_unique(cmd->stop_src);
783 /* Step 2b : and mutually compatible */
788 /* Step 3: check if arguments are trivially valid */
790 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
791 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
792 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
793 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
795 switch (cmd->stop_src) {
797 /* any count allowed */
800 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
809 /* step 4: fix up any arguments */
811 /* if (err) return 4; */
816 static int adc_wait_ready(unsigned long iobase)
818 unsigned long retry = 100000;
820 if (inb(iobase + 3) & 0x80)
825 /* All this is for AI and AO */
826 static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
827 struct comedi_insn *insn, unsigned int *data)
830 unsigned long iobase = subpriv->iobase;
833 1. write the CMD byte (to BASE+2)
834 2. read junk lo byte (BASE+0)
835 3. read junk hi byte (BASE+1)
836 4. (mux settled so) write CMD byte again (BASE+2)
837 5. read valid lo byte(BASE+0)
838 6. read valid hi byte(BASE+1)
840 Additionally note that the BASE += 4 if the channel >= 8
843 /* convert n samples */
844 for (n = 0; n < insn->n; n++) {
845 unsigned chan = CR_CHAN(insn->chanspec), range =
846 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
847 unsigned char command_byte = 0;
848 unsigned iooffset = 0;
849 short sample, adc_adjust = 0;
852 chan -= 8, iooffset = 4; /*
853 * use the second dword
857 if (aref != AREF_DIFF) {
859 command_byte |= 1 << 7; /*
860 * set bit 7 to indicate
865 adc_adjust = 0x8000; /*
867 * (-5,5 .. -10,10 need to be
868 * adjusted -- that is.. they
869 * need to wrap around by
874 command_byte |= 1 << 6; /*
875 * odd-numbered channels
880 /* select the channel, bits 4-5 == chan/2 */
881 command_byte |= ((chan / 2) & 0x3) << 4;
883 /* set the range, bits 2-3 */
884 command_byte |= (range & 0x3) << 2;
886 /* need to do this twice to make sure mux settled */
887 /* chan/range/aref select */
888 outb(command_byte, iobase + iooffset + 2);
890 /* wait for the adc to say it finised the conversion */
891 adc_wait_ready(iobase + iooffset);
893 /* select the chan/range/aref AGAIN */
894 outb(command_byte, iobase + iooffset + 2);
896 adc_wait_ready(iobase + iooffset);
898 /* read data lo byte */
899 sample = inb(iobase + iooffset + 0);
901 /* read data hi byte */
902 sample |= inb(iobase + iooffset + 1) << 8;
903 sample += adc_adjust; /* adjustment .. munge data */
906 /* return the number of samples read/written */
910 static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
911 struct comedi_insn *insn, unsigned int *data)
914 for (n = 0; n < insn->n; n++) {
915 unsigned chan = CR_CHAN(insn->chanspec);
916 if (chan < s->n_chan)
917 data[n] = subpriv->ao.shadow_samples[chan];
922 static int wait_dac_ready(unsigned long iobase)
924 unsigned long retry = 100000L;
926 /* This may seem like an absurd way to handle waiting and violates the
927 "no busy waiting" policy. The fact is that the hardware is
928 normally so fast that we usually only need one time through the loop
929 anyway. The longer timeout is for rare occasions and for detecting
930 non-existent hardware. */
933 if (inb(iobase + 3) & 0x80)
940 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
941 struct comedi_insn *insn, unsigned int *data)
944 unsigned iobase = subpriv->iobase, iooffset = 0;
946 for (n = 0; n < insn->n; n++) {
947 unsigned chan = CR_CHAN(insn->chanspec), range =
948 CR_RANGE(insn->chanspec);
949 if (chan < s->n_chan) {
950 unsigned char command_byte = 0, range_byte =
951 range & ((1 << 4) - 1);
953 chan -= 4, iooffset += 4;
954 /* set the range.. */
955 outb(range_byte, iobase + iooffset + 0);
956 outb(0, iobase + iooffset + 1);
958 /* tell it to begin */
959 command_byte = (chan << 1) | 0x60;
960 outb(command_byte, iobase + iooffset + 2);
962 wait_dac_ready(iobase + iooffset);
965 outb(data[n] & 0xff, iobase + iooffset + 0);
967 /* high order byte */
968 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
971 * set bit 4 of command byte to indicate
972 * data is loaded and trigger conversion
974 command_byte = 0x70 | (chan << 1);
975 /* trigger converion */
976 outb(command_byte, iobase + iooffset + 2);
978 wait_dac_ready(iobase + iooffset);
980 /* save to shadow register for ao_rinsn */
981 subpriv->ao.shadow_samples[chan] = data[n];
987 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
989 struct pcmmio_private *devpriv;
990 struct comedi_subdevice *s;
991 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
993 unsigned int irq[MAX_ASICS];
996 irq[0] = it->options[1];
998 ret = comedi_request_region(dev, it->options[0], 32);
1002 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1006 for (asic = 0; asic < MAX_ASICS; ++asic) {
1007 devpriv->asics[asic].num = asic;
1008 devpriv->asics[asic].iobase =
1009 dev->iobase + 16 + asic * ASIC_IOSIZE;
1011 * this gets actually set at the end of this function when we
1014 devpriv->asics[asic].irq = 0;
1015 spin_lock_init(&devpriv->asics[asic].spinlock);
1018 chans_left = CHANS_PER_ASIC * 1;
1019 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
1020 n_subdevs = n_dio_subdevs + 2;
1022 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
1024 if (!devpriv->sprivs) {
1025 printk(KERN_ERR "comedi%d: cannot allocate subdevice private data structures\n",
1030 ret = comedi_alloc_subdevices(dev, n_subdevs);
1035 s = &dev->subdevices[0];
1036 s->private = &devpriv->sprivs[0];
1037 s->maxdata = 0xffff;
1038 s->range_table = &ranges_ai;
1039 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
1040 s->type = COMEDI_SUBD_AI;
1042 s->len_chanlist = s->n_chan;
1043 s->insn_read = ai_rinsn;
1044 subpriv->iobase = dev->iobase + 0;
1045 /* initialize the resource enable register by clearing it */
1046 outb(0, subpriv->iobase + 3);
1047 outb(0, subpriv->iobase + 4 + 3);
1050 s = &dev->subdevices[1];
1051 s->private = &devpriv->sprivs[1];
1052 s->maxdata = 0xffff;
1053 s->range_table = &ranges_ao;
1054 s->subdev_flags = SDF_READABLE;
1055 s->type = COMEDI_SUBD_AO;
1057 s->len_chanlist = s->n_chan;
1058 s->insn_read = ao_rinsn;
1059 s->insn_write = ao_winsn;
1060 subpriv->iobase = dev->iobase + 8;
1061 /* initialize the resource enable register by clearing it */
1062 outb(0, subpriv->iobase + 3);
1063 outb(0, subpriv->iobase + 4 + 3);
1067 for (sdev_no = 2; sdev_no < dev->n_subdevices; ++sdev_no) {
1070 s = &dev->subdevices[sdev_no];
1071 s->private = &devpriv->sprivs[sdev_no];
1073 s->range_table = &range_digital;
1074 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1075 s->type = COMEDI_SUBD_DIO;
1076 s->insn_bits = pcmmio_dio_insn_bits;
1077 s->insn_config = pcmmio_dio_insn_config;
1078 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
1079 subpriv->dio.intr.asic = -1;
1080 subpriv->dio.intr.first_chan = -1;
1081 subpriv->dio.intr.asic_chan = -1;
1082 subpriv->dio.intr.num_asic_chans = -1;
1083 subpriv->dio.intr.active = 0;
1084 s->len_chanlist = 1;
1086 /* save the ioport address for each 'port' of 8 channels in the
1088 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
1089 if (port >= PORTS_PER_ASIC) {
1092 thisasic_chanct = 0;
1094 subpriv->iobases[byte_no] =
1095 devpriv->asics[asic].iobase + port;
1097 if (thisasic_chanct <
1098 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
1099 && subpriv->dio.intr.asic < 0) {
1101 * this is an interrupt subdevice,
1102 * so setup the struct
1104 subpriv->dio.intr.asic = asic;
1105 subpriv->dio.intr.active = 0;
1106 subpriv->dio.intr.stop_count = 0;
1107 subpriv->dio.intr.first_chan = byte_no * 8;
1108 subpriv->dio.intr.asic_chan = thisasic_chanct;
1109 subpriv->dio.intr.num_asic_chans =
1110 s->n_chan - subpriv->dio.intr.first_chan;
1111 s->cancel = pcmmio_cancel;
1112 s->do_cmd = pcmmio_cmd;
1113 s->do_cmdtest = pcmmio_cmdtest;
1115 subpriv->dio.intr.num_asic_chans;
1117 thisasic_chanct += CHANS_PER_PORT;
1119 spin_lock_init(&subpriv->dio.intr.spinlock);
1121 chans_left -= s->n_chan;
1125 * reset the asic to our first asic,
1126 * to do intr subdevs
1134 init_asics(dev); /* clear out all the registers, basically */
1136 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
1138 && request_irq(irq[asic], interrupt_pcmmio,
1139 IRQF_SHARED, dev->board_name, dev)) {
1141 /* unroll the allocated irqs.. */
1142 for (i = asic - 1; i >= 0; --i) {
1143 free_irq(irq[i], dev);
1144 devpriv->asics[i].irq = irq[i] = 0;
1148 devpriv->asics[asic].irq = irq[asic];
1154 static void pcmmio_detach(struct comedi_device *dev)
1156 struct pcmmio_private *devpriv = dev->private;
1160 for (i = 0; i < MAX_ASICS; ++i) {
1161 if (devpriv->asics[i].irq)
1162 free_irq(devpriv->asics[i].irq, dev);
1164 kfree(devpriv->sprivs);
1166 comedi_legacy_detach(dev);
1169 static struct comedi_driver pcmmio_driver = {
1170 .driver_name = "pcmmio",
1171 .module = THIS_MODULE,
1172 .attach = pcmmio_attach,
1173 .detach = pcmmio_detach,
1175 module_comedi_driver(pcmmio_driver);
1177 MODULE_AUTHOR("Comedi http://www.comedi.org");
1178 MODULE_DESCRIPTION("Comedi low-level driver");
1179 MODULE_LICENSE("GPL");