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>
81 #include "../comedidev.h"
82 #include "pcm_common.h"
83 #include <linux/pci.h> /* for PCI devices */
85 /* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
86 #define CHANS_PER_PORT 8
87 #define PORTS_PER_ASIC 6
88 #define INTR_PORTS_PER_ASIC 3
89 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
90 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
91 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
92 #define INTR_CHANS_PER_ASIC 24
93 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
94 #define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
95 #define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
96 #define SDEV_NO ((int)(s - dev->subdevices))
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
149 * Board descriptions for two imaginary boards. Describing the
150 * boards in this way is optional, and completely driver-dependent.
151 * Some drivers use arrays such as this, other do not.
153 struct pcmmio_board {
155 const int dio_num_asics;
156 const int dio_num_ports;
157 const int total_iosize;
160 const int n_ai_chans;
161 const int n_ao_chans;
162 const struct comedi_lrange *ai_range_table, *ao_range_table;
163 int (*ai_rinsn) (struct comedi_device *dev,
164 struct comedi_subdevice *s,
165 struct comedi_insn *insn,
167 int (*ao_rinsn) (struct comedi_device *dev,
168 struct comedi_subdevice *s,
169 struct comedi_insn *insn,
171 int (*ao_winsn) (struct comedi_device *dev,
172 struct comedi_subdevice *s,
173 struct comedi_insn *insn,
177 static const struct comedi_lrange ranges_ai = {
178 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
181 static const struct comedi_lrange ranges_ao = {
182 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
183 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
186 /* this structure is for data unique to this subdevice. */
187 struct pcmmio_subdev_private {
190 /* for DIO: mapping of halfwords (bytes)
191 in port/chanarray to iobase */
192 unsigned long iobases[PORTS_PER_SUBDEV];
195 unsigned long iobase;
200 /* The below is only used for intr subdevices */
203 * if non-negative, this subdev has an
208 * if nonnegative, the first channel id for
213 * the number of asic channels in this subdev
214 * that have interrutps
218 * if nonnegative, the first channel id with
219 * respect to the asic that has interrupts
223 * subdev-relative channel mask for channels
224 * we are interested in
234 /* the last unsigned int data written */
235 unsigned int shadow_samples[8];
241 * this structure is for data unique to this hardware driver. If
242 * several hardware drivers keep similar information in this structure,
243 * feel free to suggest moving the variable to the struct comedi_device struct.
245 struct pcmmio_private {
248 unsigned char pagelock; /* current page and lock */
249 /* shadow of POLx registers */
250 unsigned char pol[NUM_PAGED_REGS];
251 /* shadow of ENABx registers */
252 unsigned char enab[NUM_PAGED_REGS];
254 unsigned long iobase;
258 struct pcmmio_subdev_private *sprivs;
262 * most drivers define the following macro to make it easy to
263 * access the private structure.
265 #define devpriv ((struct pcmmio_private *)dev->private)
266 #define subpriv ((struct pcmmio_subdev_private *)s->private)
268 /* DIO devices are slightly special. Although it is possible to
269 * implement the insn_read/insn_write interface, it is much more
270 * useful to applications if you implement the insn_bits interface.
271 * This allows packed reading/writing of the DIO channels. The
272 * comedi core can convert between insn_bits and insn_read/write */
273 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
274 struct comedi_subdevice *s,
275 struct comedi_insn *insn, unsigned int *data)
280 reading a 0 means this channel was high
281 writine a 0 sets the channel high
282 reading a 1 means this channel was low
283 writing a 1 means set this channel low
285 Therefore everything is always inverted. */
287 /* The insn data is a mask in data[0] and the new data
288 * in data[1], each channel cooresponding to a bit. */
290 #ifdef DAMMIT_ITS_BROKEN
292 printk(KERN_DEBUG "write mask: %08x data: %08x\n", data[0], data[1]);
297 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
298 /* address of 8-bit port */
299 unsigned long ioaddr = subpriv->iobases[byte_no],
300 /* bit offset of port in 32-bit doubleword */
301 offset = byte_no * 8;
302 /* this 8-bit port's data */
303 unsigned char byte = 0,
304 /* The write mask for this port (if any) */
305 write_mask_byte = (data[0] >> offset) & 0xff,
306 /* The data byte for this port */
307 data_byte = (data[1] >> offset) & 0xff;
309 byte = inb(ioaddr); /* read all 8-bits for this port */
311 #ifdef DAMMIT_ITS_BROKEN
314 (KERN_DEBUG "byte %d wmb %02x db %02x offset %02d io %04x,"
315 " data_in %02x ", byte_no, (unsigned)write_mask_byte,
316 (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
319 if (write_mask_byte) {
321 * this byte has some write_bits
322 * -- so set the output lines
324 /* clear bits for write mask */
325 byte &= ~write_mask_byte;
326 /* set to inverted data_byte */
327 byte |= ~data_byte & write_mask_byte;
328 /* Write out the new digital output state */
331 #ifdef DAMMIT_ITS_BROKEN
333 printk(KERN_DEBUG "data_out_byte %02x\n", (unsigned)byte);
335 /* save the digital input lines for this byte.. */
336 s->state |= ((unsigned int)byte) << offset;
339 /* now return the DIO lines to data[1] - note they came inverted! */
342 #ifdef DAMMIT_ITS_BROKEN
344 printk(KERN_DEBUG "s->state %08x data_out %08x\n", s->state, data[1]);
350 /* The input or output configuration of each digital line is
351 * configured by a special insn_config instruction. chanspec
352 * contains the channel to be changed, and data[0] contains the
353 * value COMEDI_INPUT or COMEDI_OUTPUT. */
354 static int pcmmio_dio_insn_config(struct comedi_device *dev,
355 struct comedi_subdevice *s,
356 struct comedi_insn *insn, unsigned int *data)
358 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
360 unsigned long ioaddr;
363 /* Compute ioaddr for this channel */
364 ioaddr = subpriv->iobases[byte_no];
367 writing a 0 an IO channel's bit sets the channel to INPUT
368 and pulls the line high as well
370 writing a 1 to an IO channel's bit pulls the line low
372 All channels are implicitly always in OUTPUT mode -- but when
373 they are high they can be considered to be in INPUT mode..
375 Thus, we only force channels low if the config request was INPUT,
376 otherwise we do nothing to the hardware. */
379 case INSN_CONFIG_DIO_OUTPUT:
380 /* save to io_bits -- don't actually do anything since
381 all input channels are also output channels... */
382 s->io_bits |= 1 << chan;
384 case INSN_CONFIG_DIO_INPUT:
385 /* write a 0 to the actual register representing the channel
386 to set it to 'input'. 0 means "float high". */
388 byte &= ~(1 << bit_no);
389 /**< set input channel to '0' */
392 * write out byte -- this is the only time we actually affect
393 * the hardware as all channels are implicitly output
394 * -- but input channels are set to float-high
398 /* save to io_bits */
399 s->io_bits &= ~(1 << chan);
402 case INSN_CONFIG_DIO_QUERY:
403 /* retrieve from shadow register */
405 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
417 static void switch_page(struct comedi_device *dev, int asic, int page)
419 const struct pcmmio_board *board = comedi_board(dev);
421 if (asic < 0 || asic >= board->dio_num_asics)
422 return; /* paranoia */
423 if (page < 0 || page >= NUM_PAGES)
424 return; /* more paranoia */
426 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
427 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
429 /* now write out the shadow register */
430 outb(devpriv->asics[asic].pagelock,
431 devpriv->asics[asic].iobase + REG_PAGELOCK);
434 static void init_asics(struct comedi_device *dev)
436 ASIC chip to defaults */
437 const struct pcmmio_board *board = comedi_board(dev);
440 for (asic = 0; asic < board->dio_num_asics; ++asic) {
442 unsigned long baseaddr = devpriv->asics[asic].iobase;
444 switch_page(dev, asic, 0); /* switch back to page 0 */
446 /* first, clear all the DIO port bits */
447 for (port = 0; port < PORTS_PER_ASIC; ++port)
448 outb(0, baseaddr + REG_PORT0 + port);
450 /* Next, clear all the paged registers for each page */
451 for (page = 1; page < NUM_PAGES; ++page) {
453 /* now clear all the paged registers */
454 switch_page(dev, asic, page);
455 for (reg = FIRST_PAGED_REG;
456 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
457 outb(0, baseaddr + reg);
460 /* DEBUG set rising edge interrupts on port0 of both asics */
461 /*switch_page(dev, asic, PAGE_POL);
462 outb(0xff, baseaddr + REG_POL0);
463 switch_page(dev, asic, PAGE_ENAB);
464 outb(0xff, baseaddr + REG_ENAB0); */
467 /* switch back to default page 0 */
468 switch_page(dev, asic, 0);
473 static void lock_port(struct comedi_device *dev, int asic, int port)
475 const struct pcmmio_board *board = comedi_board(dev);
477 if (asic < 0 || asic >= board->dio_num_asics)
478 return; /* paranoia */
479 if (port < 0 || port >= PORTS_PER_ASIC)
480 return; /* more paranoia */
482 devpriv->asics[asic].pagelock |= 0x1 << port;
483 /* now write out the shadow register */
484 outb(devpriv->asics[asic].pagelock,
485 devpriv->asics[asic].iobase + REG_PAGELOCK);
489 static void unlock_port(struct comedi_device *dev, int asic, int port)
491 const struct pcmmio_board *board = comedi_board(dev);
493 if (asic < 0 || asic >= board->dio_num_asics)
494 return; /* paranoia */
495 if (port < 0 || port >= PORTS_PER_ASIC)
496 return; /* more paranoia */
497 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
498 /* now write out the shadow register */
499 outb(devpriv->asics[asic].pagelock,
500 devpriv->asics[asic].iobase + REG_PAGELOCK);
504 static void pcmmio_stop_intr(struct comedi_device *dev,
505 struct comedi_subdevice *s)
507 int nports, firstport, asic, port;
509 asic = subpriv->dio.intr.asic;
511 return; /* not an interrupt subdev */
513 subpriv->dio.intr.enabled_mask = 0;
514 subpriv->dio.intr.active = 0;
515 s->async->inttrig = NULL;
516 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
517 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
518 switch_page(dev, asic, PAGE_ENAB);
519 for (port = firstport; port < firstport + nports; ++port) {
520 /* disable all intrs for this subdev.. */
521 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
525 static irqreturn_t interrupt_pcmmio(int irq, void *d)
528 struct comedi_device *dev = (struct comedi_device *)d;
530 for (asic = 0; asic < MAX_ASICS; ++asic) {
531 if (irq == devpriv->asics[asic].irq) {
533 unsigned triggered = 0;
534 unsigned long iobase = devpriv->asics[asic].iobase;
535 /* it is an interrupt for ASIC #asic */
536 unsigned char int_pend;
538 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
541 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
545 for (port = 0; port < INTR_PORTS_PER_ASIC;
547 if (int_pend & (0x1 << port)) {
549 io_lines_with_edges = 0;
550 switch_page(dev, asic,
552 io_lines_with_edges =
556 if (io_lines_with_edges)
566 io_lines_with_edges <<
574 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
578 struct comedi_subdevice *s;
580 * TODO here: dispatch io lines to subdevs
584 (KERN_DEBUG "got edge detect interrupt %d asic %d which_chans: %06x\n",
585 irq, asic, triggered);
586 for (s = dev->subdevices + 2;
587 s < dev->subdevices + dev->n_subdevices;
590 * this is an interrupt subdev,
591 * and it matches this asic!
593 if (subpriv->dio.intr.asic == asic) {
597 spin_lock_irqsave(&subpriv->dio.
601 oldevents = s->async->events;
603 if (subpriv->dio.intr.active) {
606 subpriv->dio.intr.asic_chan)
623 async->cmd.chanlist_len;
627 ch = CR_CHAN(s->async->cmd.chanlist[n]);
628 if (mytrig & (1U << ch))
631 /* Write the scan to the buffer. */
632 if (comedi_buf_put(s->async, ((short *)&val)[0])
638 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
640 /* Overflow! Stop acquisition!! */
641 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
647 /* Check for end of acquisition. */
648 if (!subpriv->dio.intr.continuous) {
649 /* stop_src == TRIG_COUNT */
650 if (subpriv->dio.intr.stop_count > 0) {
651 subpriv->dio.intr.stop_count--;
652 if (subpriv->dio.intr.stop_count == 0) {
653 s->async->events |= COMEDI_CB_EOA;
654 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
664 spin_unlock_irqrestore
670 comedi_event(dev, s);
681 return IRQ_NONE; /* interrupt from other source */
685 static int pcmmio_start_intr(struct comedi_device *dev,
686 struct comedi_subdevice *s)
688 if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
689 /* An empty acquisition! */
690 s->async->events |= COMEDI_CB_EOA;
691 subpriv->dio.intr.active = 0;
694 unsigned bits = 0, pol_bits = 0, n;
695 int nports, firstport, asic, port;
696 struct comedi_cmd *cmd = &s->async->cmd;
698 asic = subpriv->dio.intr.asic;
700 return 1; /* not an interrupt
702 subpriv->dio.intr.enabled_mask = 0;
703 subpriv->dio.intr.active = 1;
704 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
705 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
707 for (n = 0; n < cmd->chanlist_len; n++) {
708 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
709 pol_bits |= (CR_AREF(cmd->chanlist[n])
711 chanlist[n]) ? 1U : 0U)
712 << CR_CHAN(cmd->chanlist[n]);
715 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
716 1) << subpriv->dio.intr.first_chan;
717 subpriv->dio.intr.enabled_mask = bits;
721 * the below code configures the board
722 * to use a specific IRQ from 0-15.
726 * set resource enable register
727 * to enable IRQ operation
729 outb(1 << 4, dev->iobase + 3);
730 /* set bits 0-3 of b to the irq number from 0-15 */
731 b = dev->irq & ((1 << 4) - 1);
732 outb(b, dev->iobase + 2);
733 /* done, we told the board what irq to use */
736 switch_page(dev, asic, PAGE_ENAB);
737 for (port = firstport; port < firstport + nports; ++port) {
739 bits >> (subpriv->dio.intr.first_chan + (port -
742 pol_bits >> (subpriv->dio.intr.first_chan +
743 (port - firstport) * 8) & 0xff;
744 /* set enab intrs for this subdev.. */
746 devpriv->asics[asic].iobase + REG_ENAB0 + port);
747 switch_page(dev, asic, PAGE_POL);
749 devpriv->asics[asic].iobase + REG_ENAB0 + port);
755 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
759 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
760 if (subpriv->dio.intr.active)
761 pcmmio_stop_intr(dev, s);
762 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
768 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
771 pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
772 unsigned int trignum)
780 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
781 s->async->inttrig = NULL;
782 if (subpriv->dio.intr.active)
783 event = pcmmio_start_intr(dev, s);
784 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
787 comedi_event(dev, s);
793 * 'do_cmd' function for an 'INTERRUPT' subdevice.
795 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
797 struct comedi_cmd *cmd = &s->async->cmd;
801 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
802 subpriv->dio.intr.active = 1;
804 /* Set up end of acquisition. */
805 switch (cmd->stop_src) {
807 subpriv->dio.intr.continuous = 0;
808 subpriv->dio.intr.stop_count = cmd->stop_arg;
812 subpriv->dio.intr.continuous = 1;
813 subpriv->dio.intr.stop_count = 0;
817 /* Set up start of acquisition. */
818 switch (cmd->start_src) {
820 s->async->inttrig = pcmmio_inttrig_start_intr;
824 event = pcmmio_start_intr(dev, s);
827 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
830 comedi_event(dev, s);
836 pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
837 struct comedi_cmd *cmd)
839 return comedi_pcm_cmdtest(dev, s, cmd);
842 static int adc_wait_ready(unsigned long iobase)
844 unsigned long retry = 100000;
846 if (inb(iobase + 3) & 0x80)
851 /* All this is for AI and AO */
852 static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
853 struct comedi_insn *insn, unsigned int *data)
856 unsigned long iobase = subpriv->iobase;
859 1. write the CMD byte (to BASE+2)
860 2. read junk lo byte (BASE+0)
861 3. read junk hi byte (BASE+1)
862 4. (mux settled so) write CMD byte again (BASE+2)
863 5. read valid lo byte(BASE+0)
864 6. read valid hi byte(BASE+1)
866 Additionally note that the BASE += 4 if the channel >= 8
869 /* convert n samples */
870 for (n = 0; n < insn->n; n++) {
871 unsigned chan = CR_CHAN(insn->chanspec), range =
872 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
873 unsigned char command_byte = 0;
874 unsigned iooffset = 0;
875 short sample, adc_adjust = 0;
878 chan -= 8, iooffset = 4; /*
879 * use the second dword
883 if (aref != AREF_DIFF) {
885 command_byte |= 1 << 7; /*
886 * set bit 7 to indicate
891 adc_adjust = 0x8000; /*
893 * (-5,5 .. -10,10 need to be
894 * adjusted -- that is.. they
895 * need to wrap around by
900 command_byte |= 1 << 6; /*
901 * odd-numbered channels
906 /* select the channel, bits 4-5 == chan/2 */
907 command_byte |= ((chan / 2) & 0x3) << 4;
909 /* set the range, bits 2-3 */
910 command_byte |= (range & 0x3) << 2;
912 /* need to do this twice to make sure mux settled */
913 /* chan/range/aref select */
914 outb(command_byte, iobase + iooffset + 2);
916 /* wait for the adc to say it finised the conversion */
917 adc_wait_ready(iobase + iooffset);
919 /* select the chan/range/aref AGAIN */
920 outb(command_byte, iobase + iooffset + 2);
922 adc_wait_ready(iobase + iooffset);
924 /* read data lo byte */
925 sample = inb(iobase + iooffset + 0);
927 /* read data hi byte */
928 sample |= inb(iobase + iooffset + 1) << 8;
929 sample += adc_adjust; /* adjustment .. munge data */
932 /* return the number of samples read/written */
936 static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
937 struct comedi_insn *insn, unsigned int *data)
940 for (n = 0; n < insn->n; n++) {
941 unsigned chan = CR_CHAN(insn->chanspec);
942 if (chan < s->n_chan)
943 data[n] = subpriv->ao.shadow_samples[chan];
948 static int wait_dac_ready(unsigned long iobase)
950 unsigned long retry = 100000L;
952 /* This may seem like an absurd way to handle waiting and violates the
953 "no busy waiting" policy. The fact is that the hardware is
954 normally so fast that we usually only need one time through the loop
955 anyway. The longer timeout is for rare occasions and for detecting
956 non-existent hardware. */
959 if (inb(iobase + 3) & 0x80)
966 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
967 struct comedi_insn *insn, unsigned int *data)
970 unsigned iobase = subpriv->iobase, iooffset = 0;
972 for (n = 0; n < insn->n; n++) {
973 unsigned chan = CR_CHAN(insn->chanspec), range =
974 CR_RANGE(insn->chanspec);
975 if (chan < s->n_chan) {
976 unsigned char command_byte = 0, range_byte =
977 range & ((1 << 4) - 1);
979 chan -= 4, iooffset += 4;
980 /* set the range.. */
981 outb(range_byte, iobase + iooffset + 0);
982 outb(0, iobase + iooffset + 1);
984 /* tell it to begin */
985 command_byte = (chan << 1) | 0x60;
986 outb(command_byte, iobase + iooffset + 2);
988 wait_dac_ready(iobase + iooffset);
991 outb(data[n] & 0xff, iobase + iooffset + 0);
993 /* high order byte */
994 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
997 * set bit 4 of command byte to indicate
998 * data is loaded and trigger conversion
1000 command_byte = 0x70 | (chan << 1);
1001 /* trigger converion */
1002 outb(command_byte, iobase + iooffset + 2);
1004 wait_dac_ready(iobase + iooffset);
1006 /* save to shadow register for ao_rinsn */
1007 subpriv->ao.shadow_samples[chan] = data[n];
1013 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1015 const struct pcmmio_board *board = comedi_board(dev);
1016 struct comedi_subdevice *s;
1017 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
1018 thisasic_chanct = 0;
1019 unsigned long iobase;
1020 unsigned int irq[MAX_ASICS];
1023 iobase = it->options[0];
1024 irq[0] = it->options[1];
1026 printk(KERN_INFO "comedi%d: %s: io: %lx attaching...\n", dev->minor,
1027 dev->driver->driver_name, iobase);
1029 dev->iobase = iobase;
1031 if (!iobase || !request_region(iobase,
1032 board->total_iosize,
1033 dev->driver->driver_name)) {
1034 printk(KERN_ERR "comedi%d: I/O port conflict\n", dev->minor);
1038 dev->board_name = board->name;
1041 * Allocate the private structure area. alloc_private() is a
1042 * convenient macro defined in comedidev.h.
1044 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
1045 printk(KERN_ERR "comedi%d: cannot allocate private data structure\n",
1050 for (asic = 0; asic < MAX_ASICS; ++asic) {
1051 devpriv->asics[asic].num = asic;
1052 devpriv->asics[asic].iobase =
1053 dev->iobase + 16 + asic * ASIC_IOSIZE;
1055 * this gets actually set at the end of this function when we
1058 devpriv->asics[asic].irq = 0;
1059 spin_lock_init(&devpriv->asics[asic].spinlock);
1062 chans_left = CHANS_PER_ASIC * board->dio_num_asics;
1063 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
1064 n_subdevs = n_dio_subdevs + 2;
1066 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
1068 if (!devpriv->sprivs) {
1069 printk(KERN_ERR "comedi%d: cannot allocate subdevice private data structures\n",
1074 ret = comedi_alloc_subdevices(dev, n_subdevs);
1080 s = dev->subdevices + sdev_no;
1081 s->private = devpriv->sprivs + sdev_no;
1082 s->maxdata = (1 << board->ai_bits) - 1;
1083 s->range_table = board->ai_range_table;
1084 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
1085 s->type = COMEDI_SUBD_AI;
1086 s->n_chan = board->n_ai_chans;
1087 s->len_chanlist = s->n_chan;
1088 s->insn_read = board->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);
1096 s = dev->subdevices + sdev_no;
1097 s->private = devpriv->sprivs + sdev_no;
1098 s->maxdata = (1 << board->ao_bits) - 1;
1099 s->range_table = board->ao_range_table;
1100 s->subdev_flags = SDF_READABLE;
1101 s->type = COMEDI_SUBD_AO;
1102 s->n_chan = board->n_ao_chans;
1103 s->len_chanlist = s->n_chan;
1104 s->insn_read = board->ao_rinsn;
1105 s->insn_write = board->ao_winsn;
1106 subpriv->iobase = dev->iobase + 8;
1107 /* initialize the resource enable register by clearing it */
1108 outb(0, subpriv->iobase + 3);
1109 outb(0, subpriv->iobase + 4 + 3);
1114 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
1117 s = dev->subdevices + sdev_no;
1118 s->private = devpriv->sprivs + sdev_no;
1120 s->range_table = &range_digital;
1121 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1122 s->type = COMEDI_SUBD_DIO;
1123 s->insn_bits = pcmmio_dio_insn_bits;
1124 s->insn_config = pcmmio_dio_insn_config;
1125 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
1126 subpriv->dio.intr.asic = -1;
1127 subpriv->dio.intr.first_chan = -1;
1128 subpriv->dio.intr.asic_chan = -1;
1129 subpriv->dio.intr.num_asic_chans = -1;
1130 subpriv->dio.intr.active = 0;
1131 s->len_chanlist = 1;
1133 /* save the ioport address for each 'port' of 8 channels in the
1135 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
1136 if (port >= PORTS_PER_ASIC) {
1139 thisasic_chanct = 0;
1141 subpriv->iobases[byte_no] =
1142 devpriv->asics[asic].iobase + port;
1144 if (thisasic_chanct <
1145 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
1146 && subpriv->dio.intr.asic < 0) {
1148 * this is an interrupt subdevice,
1149 * so setup the struct
1151 subpriv->dio.intr.asic = asic;
1152 subpriv->dio.intr.active = 0;
1153 subpriv->dio.intr.stop_count = 0;
1154 subpriv->dio.intr.first_chan = byte_no * 8;
1155 subpriv->dio.intr.asic_chan = thisasic_chanct;
1156 subpriv->dio.intr.num_asic_chans =
1157 s->n_chan - subpriv->dio.intr.first_chan;
1158 s->cancel = pcmmio_cancel;
1159 s->do_cmd = pcmmio_cmd;
1160 s->do_cmdtest = pcmmio_cmdtest;
1162 subpriv->dio.intr.num_asic_chans;
1164 thisasic_chanct += CHANS_PER_PORT;
1166 spin_lock_init(&subpriv->dio.intr.spinlock);
1168 chans_left -= s->n_chan;
1172 * reset the asic to our first asic,
1173 * to do intr subdevs
1181 init_asics(dev); /* clear out all the registers, basically */
1183 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
1185 && request_irq(irq[asic], interrupt_pcmmio,
1186 IRQF_SHARED, board->name, dev)) {
1188 /* unroll the allocated irqs.. */
1189 for (i = asic - 1; i >= 0; --i) {
1190 free_irq(irq[i], dev);
1191 devpriv->asics[i].irq = irq[i] = 0;
1195 devpriv->asics[asic].irq = irq[asic];
1198 dev->irq = irq[0]; /*
1199 * grr.. wish comedi dev struct supported
1204 printk(KERN_DEBUG "comedi%d: irq: %u\n", dev->minor, irq[0]);
1205 if (board->dio_num_asics == 2 && irq[1])
1206 printk(KERN_DEBUG "comedi%d: second ASIC irq: %u\n",
1207 dev->minor, irq[1]);
1209 printk(KERN_INFO "comedi%d: (IRQ mode disabled)\n", dev->minor);
1212 printk(KERN_INFO "comedi%d: attached\n", dev->minor);
1217 static void pcmmio_detach(struct comedi_device *dev)
1219 const struct pcmmio_board *board = comedi_board(dev);
1223 release_region(dev->iobase, board->total_iosize);
1224 for (i = 0; i < MAX_ASICS; ++i) {
1225 if (devpriv && devpriv->asics[i].irq)
1226 free_irq(devpriv->asics[i].irq, dev);
1228 if (devpriv && devpriv->sprivs)
1229 kfree(devpriv->sprivs);
1232 static const struct pcmmio_board pcmmio_boards[] = {
1242 .ai_range_table = &ranges_ai,
1243 .ao_range_table = &ranges_ao,
1244 .ai_rinsn = ai_rinsn,
1245 .ao_rinsn = ao_rinsn,
1246 .ao_winsn = ao_winsn
1250 static struct comedi_driver pcmmio_driver = {
1251 .driver_name = "pcmmio",
1252 .module = THIS_MODULE,
1253 .attach = pcmmio_attach,
1254 .detach = pcmmio_detach,
1255 .board_name = &pcmmio_boards[0].name,
1256 .offset = sizeof(struct pcmmio_board),
1257 .num_names = ARRAY_SIZE(pcmmio_boards),
1259 module_comedi_driver(pcmmio_driver);
1261 MODULE_AUTHOR("Comedi http://www.comedi.org");
1262 MODULE_DESCRIPTION("Comedi low-level driver");
1263 MODULE_LICENSE("GPL");