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
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);
806 pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
807 struct comedi_cmd *cmd)
809 return comedi_pcm_cmdtest(dev, s, cmd);
812 static int adc_wait_ready(unsigned long iobase)
814 unsigned long retry = 100000;
816 if (inb(iobase + 3) & 0x80)
821 /* All this is for AI and AO */
822 static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
823 struct comedi_insn *insn, unsigned int *data)
826 unsigned long iobase = subpriv->iobase;
829 1. write the CMD byte (to BASE+2)
830 2. read junk lo byte (BASE+0)
831 3. read junk hi byte (BASE+1)
832 4. (mux settled so) write CMD byte again (BASE+2)
833 5. read valid lo byte(BASE+0)
834 6. read valid hi byte(BASE+1)
836 Additionally note that the BASE += 4 if the channel >= 8
839 /* convert n samples */
840 for (n = 0; n < insn->n; n++) {
841 unsigned chan = CR_CHAN(insn->chanspec), range =
842 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
843 unsigned char command_byte = 0;
844 unsigned iooffset = 0;
845 short sample, adc_adjust = 0;
848 chan -= 8, iooffset = 4; /*
849 * use the second dword
853 if (aref != AREF_DIFF) {
855 command_byte |= 1 << 7; /*
856 * set bit 7 to indicate
861 adc_adjust = 0x8000; /*
863 * (-5,5 .. -10,10 need to be
864 * adjusted -- that is.. they
865 * need to wrap around by
870 command_byte |= 1 << 6; /*
871 * odd-numbered channels
876 /* select the channel, bits 4-5 == chan/2 */
877 command_byte |= ((chan / 2) & 0x3) << 4;
879 /* set the range, bits 2-3 */
880 command_byte |= (range & 0x3) << 2;
882 /* need to do this twice to make sure mux settled */
883 /* chan/range/aref select */
884 outb(command_byte, iobase + iooffset + 2);
886 /* wait for the adc to say it finised the conversion */
887 adc_wait_ready(iobase + iooffset);
889 /* select the chan/range/aref AGAIN */
890 outb(command_byte, iobase + iooffset + 2);
892 adc_wait_ready(iobase + iooffset);
894 /* read data lo byte */
895 sample = inb(iobase + iooffset + 0);
897 /* read data hi byte */
898 sample |= inb(iobase + iooffset + 1) << 8;
899 sample += adc_adjust; /* adjustment .. munge data */
902 /* return the number of samples read/written */
906 static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
907 struct comedi_insn *insn, unsigned int *data)
910 for (n = 0; n < insn->n; n++) {
911 unsigned chan = CR_CHAN(insn->chanspec);
912 if (chan < s->n_chan)
913 data[n] = subpriv->ao.shadow_samples[chan];
918 static int wait_dac_ready(unsigned long iobase)
920 unsigned long retry = 100000L;
922 /* This may seem like an absurd way to handle waiting and violates the
923 "no busy waiting" policy. The fact is that the hardware is
924 normally so fast that we usually only need one time through the loop
925 anyway. The longer timeout is for rare occasions and for detecting
926 non-existent hardware. */
929 if (inb(iobase + 3) & 0x80)
936 static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
937 struct comedi_insn *insn, unsigned int *data)
940 unsigned iobase = subpriv->iobase, iooffset = 0;
942 for (n = 0; n < insn->n; n++) {
943 unsigned chan = CR_CHAN(insn->chanspec), range =
944 CR_RANGE(insn->chanspec);
945 if (chan < s->n_chan) {
946 unsigned char command_byte = 0, range_byte =
947 range & ((1 << 4) - 1);
949 chan -= 4, iooffset += 4;
950 /* set the range.. */
951 outb(range_byte, iobase + iooffset + 0);
952 outb(0, iobase + iooffset + 1);
954 /* tell it to begin */
955 command_byte = (chan << 1) | 0x60;
956 outb(command_byte, iobase + iooffset + 2);
958 wait_dac_ready(iobase + iooffset);
961 outb(data[n] & 0xff, iobase + iooffset + 0);
963 /* high order byte */
964 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
967 * set bit 4 of command byte to indicate
968 * data is loaded and trigger conversion
970 command_byte = 0x70 | (chan << 1);
971 /* trigger converion */
972 outb(command_byte, iobase + iooffset + 2);
974 wait_dac_ready(iobase + iooffset);
976 /* save to shadow register for ao_rinsn */
977 subpriv->ao.shadow_samples[chan] = data[n];
983 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
985 struct pcmmio_private *devpriv;
986 struct comedi_subdevice *s;
987 int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
989 unsigned long iobase;
990 unsigned int irq[MAX_ASICS];
993 dev->board_name = dev->driver->driver_name;
995 iobase = it->options[0];
996 irq[0] = it->options[1];
998 printk(KERN_INFO "comedi%d: %s: io: %lx attaching...\n", dev->minor,
999 dev->board_name, iobase);
1001 dev->iobase = iobase;
1003 if (!iobase || !request_region(iobase, 32, dev->board_name)) {
1004 printk(KERN_ERR "comedi%d: I/O port conflict\n", dev->minor);
1008 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
1011 dev->private = devpriv;
1013 for (asic = 0; asic < MAX_ASICS; ++asic) {
1014 devpriv->asics[asic].num = asic;
1015 devpriv->asics[asic].iobase =
1016 dev->iobase + 16 + asic * ASIC_IOSIZE;
1018 * this gets actually set at the end of this function when we
1021 devpriv->asics[asic].irq = 0;
1022 spin_lock_init(&devpriv->asics[asic].spinlock);
1025 chans_left = CHANS_PER_ASIC * 1;
1026 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
1027 n_subdevs = n_dio_subdevs + 2;
1029 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
1031 if (!devpriv->sprivs) {
1032 printk(KERN_ERR "comedi%d: cannot allocate subdevice private data structures\n",
1037 ret = comedi_alloc_subdevices(dev, n_subdevs);
1042 s = &dev->subdevices[0];
1043 s->private = &devpriv->sprivs[0];
1044 s->maxdata = 0xffff;
1045 s->range_table = &ranges_ai;
1046 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
1047 s->type = COMEDI_SUBD_AI;
1049 s->len_chanlist = s->n_chan;
1050 s->insn_read = ai_rinsn;
1051 subpriv->iobase = dev->iobase + 0;
1052 /* initialize the resource enable register by clearing it */
1053 outb(0, subpriv->iobase + 3);
1054 outb(0, subpriv->iobase + 4 + 3);
1057 s = &dev->subdevices[1];
1058 s->private = &devpriv->sprivs[1];
1059 s->maxdata = 0xffff;
1060 s->range_table = &ranges_ao;
1061 s->subdev_flags = SDF_READABLE;
1062 s->type = COMEDI_SUBD_AO;
1064 s->len_chanlist = s->n_chan;
1065 s->insn_read = ao_rinsn;
1066 s->insn_write = ao_winsn;
1067 subpriv->iobase = dev->iobase + 8;
1068 /* initialize the resource enable register by clearing it */
1069 outb(0, subpriv->iobase + 3);
1070 outb(0, subpriv->iobase + 4 + 3);
1074 for (sdev_no = 2; sdev_no < dev->n_subdevices; ++sdev_no) {
1077 s = &dev->subdevices[sdev_no];
1078 s->private = &devpriv->sprivs[sdev_no];
1080 s->range_table = &range_digital;
1081 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1082 s->type = COMEDI_SUBD_DIO;
1083 s->insn_bits = pcmmio_dio_insn_bits;
1084 s->insn_config = pcmmio_dio_insn_config;
1085 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
1086 subpriv->dio.intr.asic = -1;
1087 subpriv->dio.intr.first_chan = -1;
1088 subpriv->dio.intr.asic_chan = -1;
1089 subpriv->dio.intr.num_asic_chans = -1;
1090 subpriv->dio.intr.active = 0;
1091 s->len_chanlist = 1;
1093 /* save the ioport address for each 'port' of 8 channels in the
1095 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
1096 if (port >= PORTS_PER_ASIC) {
1099 thisasic_chanct = 0;
1101 subpriv->iobases[byte_no] =
1102 devpriv->asics[asic].iobase + port;
1104 if (thisasic_chanct <
1105 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
1106 && subpriv->dio.intr.asic < 0) {
1108 * this is an interrupt subdevice,
1109 * so setup the struct
1111 subpriv->dio.intr.asic = asic;
1112 subpriv->dio.intr.active = 0;
1113 subpriv->dio.intr.stop_count = 0;
1114 subpriv->dio.intr.first_chan = byte_no * 8;
1115 subpriv->dio.intr.asic_chan = thisasic_chanct;
1116 subpriv->dio.intr.num_asic_chans =
1117 s->n_chan - subpriv->dio.intr.first_chan;
1118 s->cancel = pcmmio_cancel;
1119 s->do_cmd = pcmmio_cmd;
1120 s->do_cmdtest = pcmmio_cmdtest;
1122 subpriv->dio.intr.num_asic_chans;
1124 thisasic_chanct += CHANS_PER_PORT;
1126 spin_lock_init(&subpriv->dio.intr.spinlock);
1128 chans_left -= s->n_chan;
1132 * reset the asic to our first asic,
1133 * to do intr subdevs
1141 init_asics(dev); /* clear out all the registers, basically */
1143 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
1145 && request_irq(irq[asic], interrupt_pcmmio,
1146 IRQF_SHARED, dev->board_name, dev)) {
1148 /* unroll the allocated irqs.. */
1149 for (i = asic - 1; i >= 0; --i) {
1150 free_irq(irq[i], dev);
1151 devpriv->asics[i].irq = irq[i] = 0;
1155 devpriv->asics[asic].irq = irq[asic];
1158 dev->irq = irq[0]; /*
1159 * grr.. wish comedi dev struct supported
1163 printk(KERN_INFO "comedi%d: attached\n", dev->minor);
1168 static void pcmmio_detach(struct comedi_device *dev)
1170 struct pcmmio_private *devpriv = dev->private;
1174 release_region(dev->iobase, 32);
1175 for (i = 0; i < MAX_ASICS; ++i) {
1176 if (devpriv && devpriv->asics[i].irq)
1177 free_irq(devpriv->asics[i].irq, dev);
1179 if (devpriv && devpriv->sprivs)
1180 kfree(devpriv->sprivs);
1183 static struct comedi_driver pcmmio_driver = {
1184 .driver_name = "pcmmio",
1185 .module = THIS_MODULE,
1186 .attach = pcmmio_attach,
1187 .detach = pcmmio_detach,
1189 module_comedi_driver(pcmmio_driver);
1191 MODULE_AUTHOR("Comedi http://www.comedi.org");
1192 MODULE_DESCRIPTION("Comedi low-level driver");
1193 MODULE_LICENSE("GPL");