GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / drivers / staging / comedi / drivers / pcmuio.c
blob7a9287433b2e69c494b3a0f52a32a09db95a71aa
1 /*
2 comedi/drivers/pcmuio.c
3 Driver for Winsystems PC-104 based 48-channel and 96-channel DIO boards.
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2006 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.
23 Driver: pcmuio
24 Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
25 Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
26 Author: Calin Culianu <calin@ajvar.org>
27 Updated: Fri, 13 Jan 2006 12:01:01 -0500
28 Status: works
30 A driver for the relatively straightforward-to-program PCM-UIO48A and
31 PCM-UIO96A boards from Winsystems. These boards use either one or two
32 (in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
33 This chip is interesting in that each I/O line is individually
34 programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
35 on a per-channel basis). Also, each chip supports edge-triggered
36 interrupts for the first 24 I/O lines. Of course, since the
37 96-channel version of the board has two ASICs, it can detect polarity
38 changes on up to 48 I/O lines. Since this is essentially an (non-PnP)
39 ISA board, I/O Address and IRQ selection are done through jumpers on
40 the board. You need to pass that information to this driver as the
41 first and second comedi_config option, respectively. Note that the
42 48-channel version uses 16 bytes of IO memory and the 96-channel
43 version uses 32-bytes (in case you are worried about conflicts). The
44 48-channel board is split into two 24-channel comedi subdevices.
45 The 96-channel board is split into 4 24-channel DIO subdevices.
47 Note that IRQ support has been added, but it is untested.
49 To use edge-detection IRQ support, pass the IRQs of both ASICS
50 (for the 96 channel version) or just 1 ASIC (for 48-channel version).
51 Then, use use comedi_commands with TRIG_NOW.
52 Your callback will be called each time an edge is triggered, and the data
53 values will be two sample_t's, which should be concatenated to form one
54 32-bit unsigned int. This value is the mask of channels that had
55 edges detected from your channel list. Note that the bits positions
56 in the mask correspond to positions in your chanlist when you specified
57 the command and *not* channel id's!
59 To set the polarity of the edge-detection interrupts pass a nonzero value for
60 either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
61 CR_RANGE and CR_AREF if you want edge-down polarity.
63 In the 48-channel version:
65 On subdev 0, the first 24 channels channels are edge-detect channels.
67 In the 96-channel board you have the collowing channels that can do edge detection:
69 subdev 0, channels 0-24 (first 24 channels of 1st ASIC)
70 subdev 2, channels 0-24 (first 24 channels of 2nd ASIC)
72 Configuration Options:
73 [0] - I/O port base address
74 [1] - IRQ (for first ASIC, or first 24 channels)
75 [2] - IRQ for second ASIC (pcmuio96 only - IRQ for chans 48-72 .. can be the same as first irq!)
78 #include <linux/interrupt.h>
79 #include <linux/slab.h>
80 #include "../comedidev.h"
81 #include "pcm_common.h"
83 #include <linux/pci.h> /* for PCI devices */
85 #define CHANS_PER_PORT 8
86 #define PORTS_PER_ASIC 6
87 #define INTR_PORTS_PER_ASIC 3
88 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
89 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
90 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
91 #define INTR_CHANS_PER_ASIC 24
92 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
93 #define MAX_DIO_CHANS (PORTS_PER_ASIC*2*CHANS_PER_PORT)
94 #define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
95 #define SDEV_NO ((int)(s - dev->subdevices))
96 #define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
97 /* IO Memory sizes */
98 #define ASIC_IOSIZE (0x10)
99 #define PCMUIO48_IOSIZE ASIC_IOSIZE
100 #define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
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 /* page selector register, upper 2 bits select a page
123 and bits 0-5 are used to 'lock down' a particular
124 port above to make it readonly. */
125 #define REG_POL0 0x8
126 #define REG_POL1 0x9
127 #define REG_POL2 0xA
128 #define REG_ENAB0 0x8
129 #define REG_ENAB1 0x9
130 #define REG_ENAB2 0xA
131 #define REG_INT_ID0 0x8
132 #define REG_INT_ID1 0x9
133 #define REG_INT_ID2 0xA
135 #define NUM_PAGED_REGS 3
136 #define NUM_PAGES 4
137 #define FIRST_PAGED_REG 0x8
138 #define REG_PAGE_BITOFFSET 6
139 #define REG_LOCK_BITOFFSET 0
140 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
141 #define REG_LOCK_MASK ~(REG_PAGE_MASK)
142 #define PAGE_POL 1
143 #define PAGE_ENAB 2
144 #define PAGE_INT_ID 3
147 * Board descriptions for two imaginary boards. Describing the
148 * boards in this way is optional, and completely driver-dependent.
149 * Some drivers use arrays such as this, other do not.
151 struct pcmuio_board {
152 const char *name;
153 const int num_asics;
154 const int num_channels_per_port;
155 const int num_ports;
158 static const struct pcmuio_board pcmuio_boards[] = {
160 .name = "pcmuio48",
161 .num_asics = 1,
162 .num_ports = 6,
165 .name = "pcmuio96",
166 .num_asics = 2,
167 .num_ports = 12,
172 * Useful for shorthand access to the particular board structure
174 #define thisboard ((const struct pcmuio_board *)dev->board_ptr)
176 /* this structure is for data unique to this subdevice. */
177 struct pcmuio_subdev_private {
178 /* mapping of halfwords (bytes) in port/chanarray to iobase */
179 unsigned long iobases[PORTS_PER_SUBDEV];
181 /* The below is only used for intr subdevices */
182 struct {
183 int asic; /* if non-negative, this subdev has an interrupt asic */
184 int first_chan; /* if nonnegative, the first channel id for
185 interrupts. */
186 int num_asic_chans; /* the number of asic channels in this subdev
187 that have interrutps */
188 int asic_chan; /* if nonnegative, the first channel id with
189 respect to the asic that has interrupts */
190 int enabled_mask; /* subdev-relative channel mask for channels
191 we are interested in */
192 int active;
193 int stop_count;
194 int continuous;
195 spinlock_t spinlock;
196 } intr;
199 /* this structure is for data unique to this hardware driver. If
200 several hardware drivers keep similar information in this structure,
201 feel free to suggest moving the variable to the struct comedi_device struct. */
202 struct pcmuio_private {
203 struct {
204 unsigned char pagelock; /* current page and lock */
205 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
206 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
207 int num;
208 unsigned long iobase;
209 unsigned int irq;
210 spinlock_t spinlock;
211 } asics[MAX_ASICS];
212 struct pcmuio_subdev_private *sprivs;
216 * most drivers define the following macro to make it easy to
217 * access the private structure.
219 #define devpriv ((struct pcmuio_private *)dev->private)
220 #define subpriv ((struct pcmuio_subdev_private *)s->private)
222 * The struct comedi_driver structure tells the Comedi core module
223 * which functions to call to configure/deconfigure (attach/detach)
224 * the board, and also about the kernel module that contains
225 * the device code.
227 static int pcmuio_attach(struct comedi_device *dev,
228 struct comedi_devconfig *it);
229 static int pcmuio_detach(struct comedi_device *dev);
231 static struct comedi_driver driver = {
232 .driver_name = "pcmuio",
233 .module = THIS_MODULE,
234 .attach = pcmuio_attach,
235 .detach = pcmuio_detach,
236 /* It is not necessary to implement the following members if you are
237 * writing a driver for a ISA PnP or PCI card */
238 /* Most drivers will support multiple types of boards by
239 * having an array of board structures. These were defined
240 * in pcmuio_boards[] above. Note that the element 'name'
241 * was first in the structure -- Comedi uses this fact to
242 * extract the name of the board without knowing any details
243 * about the structure except for its length.
244 * When a device is attached (by comedi_config), the name
245 * of the device is given to Comedi, and Comedi tries to
246 * match it by going through the list of board names. If
247 * there is a match, the address of the pointer is put
248 * into dev->board_ptr and driver->attach() is called.
250 * Note that these are not necessary if you can determine
251 * the type of board in software. ISA PnP, PCI, and PCMCIA
252 * devices are such boards.
254 .board_name = &pcmuio_boards[0].name,
255 .offset = sizeof(struct pcmuio_board),
256 .num_names = ARRAY_SIZE(pcmuio_boards),
259 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
260 struct comedi_subdevice *s,
261 struct comedi_insn *insn, unsigned int *data);
262 static int pcmuio_dio_insn_config(struct comedi_device *dev,
263 struct comedi_subdevice *s,
264 struct comedi_insn *insn, unsigned int *data);
266 static irqreturn_t interrupt_pcmuio(int irq, void *d);
267 static void pcmuio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
268 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
269 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
270 static int pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
271 struct comedi_cmd *cmd);
273 /* some helper functions to deal with specifics of this device's registers */
274 static void init_asics(struct comedi_device *dev); /* sets up/clears ASIC chips to defaults */
275 static void switch_page(struct comedi_device *dev, int asic, int page);
276 #ifdef notused
277 static void lock_port(struct comedi_device *dev, int asic, int port);
278 static void unlock_port(struct comedi_device *dev, int asic, int port);
279 #endif
282 * Attach is called by the Comedi core to configure the driver
283 * for a particular board. If you specified a board_name array
284 * in the driver structure, dev->board_ptr contains that
285 * address.
287 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
289 struct comedi_subdevice *s;
290 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
291 unsigned long iobase;
292 unsigned int irq[MAX_ASICS];
294 iobase = it->options[0];
295 irq[0] = it->options[1];
296 irq[1] = it->options[2];
298 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
299 iobase);
301 dev->iobase = iobase;
303 if (!iobase || !request_region(iobase,
304 thisboard->num_asics * ASIC_IOSIZE,
305 driver.driver_name)) {
306 printk("I/O port conflict\n");
307 return -EIO;
311 * Initialize dev->board_name. Note that we can use the "thisboard"
312 * macro now, since we just initialized it in the last line.
314 dev->board_name = thisboard->name;
317 * Allocate the private structure area. alloc_private() is a
318 * convenient macro defined in comedidev.h.
320 if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
321 printk("cannot allocate private data structure\n");
322 return -ENOMEM;
325 for (asic = 0; asic < MAX_ASICS; ++asic) {
326 devpriv->asics[asic].num = asic;
327 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
328 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
329 this function when we
330 request_irqs */
331 spin_lock_init(&devpriv->asics[asic].spinlock);
334 chans_left = CHANS_PER_ASIC * thisboard->num_asics;
335 n_subdevs = CALC_N_SUBDEVS(chans_left);
336 devpriv->sprivs =
337 kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
338 GFP_KERNEL);
339 if (!devpriv->sprivs) {
340 printk("cannot allocate subdevice private data structures\n");
341 return -ENOMEM;
344 * Allocate the subdevice structures. alloc_subdevice() is a
345 * convenient macro defined in comedidev.h.
347 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
348 * 96-channel version of the board.
350 if (alloc_subdevices(dev, n_subdevs) < 0) {
351 printk("cannot allocate subdevice data structures\n");
352 return -ENOMEM;
355 port = 0;
356 asic = 0;
357 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
358 int byte_no;
360 s = dev->subdevices + sdev_no;
361 s->private = devpriv->sprivs + sdev_no;
362 s->maxdata = 1;
363 s->range_table = &range_digital;
364 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
365 s->type = COMEDI_SUBD_DIO;
366 s->insn_bits = pcmuio_dio_insn_bits;
367 s->insn_config = pcmuio_dio_insn_config;
368 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
369 subpriv->intr.asic = -1;
370 subpriv->intr.first_chan = -1;
371 subpriv->intr.asic_chan = -1;
372 subpriv->intr.num_asic_chans = -1;
373 subpriv->intr.active = 0;
374 s->len_chanlist = 1;
376 /* save the ioport address for each 'port' of 8 channels in the
377 subdevice */
378 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
379 if (port >= PORTS_PER_ASIC) {
380 port = 0;
381 ++asic;
382 thisasic_chanct = 0;
384 subpriv->iobases[byte_no] =
385 devpriv->asics[asic].iobase + port;
387 if (thisasic_chanct <
388 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
389 && subpriv->intr.asic < 0) {
390 /* this is an interrupt subdevice, so setup the struct */
391 subpriv->intr.asic = asic;
392 subpriv->intr.active = 0;
393 subpriv->intr.stop_count = 0;
394 subpriv->intr.first_chan = byte_no * 8;
395 subpriv->intr.asic_chan = thisasic_chanct;
396 subpriv->intr.num_asic_chans =
397 s->n_chan - subpriv->intr.first_chan;
398 dev->read_subdev = s;
399 s->subdev_flags |= SDF_CMD_READ;
400 s->cancel = pcmuio_cancel;
401 s->do_cmd = pcmuio_cmd;
402 s->do_cmdtest = pcmuio_cmdtest;
403 s->len_chanlist = subpriv->intr.num_asic_chans;
405 thisasic_chanct += CHANS_PER_PORT;
407 spin_lock_init(&subpriv->intr.spinlock);
409 chans_left -= s->n_chan;
411 if (!chans_left) {
412 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
413 port = 0;
418 init_asics(dev); /* clear out all the registers, basically */
420 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
421 if (irq[asic]
422 && request_irq(irq[asic], interrupt_pcmuio,
423 IRQF_SHARED, thisboard->name, dev)) {
424 int i;
425 /* unroll the allocated irqs.. */
426 for (i = asic - 1; i >= 0; --i) {
427 free_irq(irq[i], dev);
428 devpriv->asics[i].irq = irq[i] = 0;
430 irq[asic] = 0;
432 devpriv->asics[asic].irq = irq[asic];
435 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
436 irqs.. */
438 if (irq[0]) {
439 printk("irq: %u ", irq[0]);
440 if (irq[1] && thisboard->num_asics == 2)
441 printk("second ASIC irq: %u ", irq[1]);
442 } else {
443 printk("(IRQ mode disabled) ");
446 printk("attached\n");
448 return 1;
452 * _detach is called to deconfigure a device. It should deallocate
453 * resources.
454 * This function is also called when _attach() fails, so it should be
455 * careful not to release resources that were not necessarily
456 * allocated by _attach(). dev->private and dev->subdevices are
457 * deallocated automatically by the core.
459 static int pcmuio_detach(struct comedi_device *dev)
461 int i;
463 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
464 if (dev->iobase)
465 release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics);
467 for (i = 0; i < MAX_ASICS; ++i) {
468 if (devpriv->asics[i].irq)
469 free_irq(devpriv->asics[i].irq, dev);
472 if (devpriv && devpriv->sprivs)
473 kfree(devpriv->sprivs);
475 return 0;
478 /* DIO devices are slightly special. Although it is possible to
479 * implement the insn_read/insn_write interface, it is much more
480 * useful to applications if you implement the insn_bits interface.
481 * This allows packed reading/writing of the DIO channels. The
482 * comedi core can convert between insn_bits and insn_read/write */
483 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
484 struct comedi_subdevice *s,
485 struct comedi_insn *insn, unsigned int *data)
487 int byte_no;
488 if (insn->n != 2)
489 return -EINVAL;
491 /* NOTE:
492 reading a 0 means this channel was high
493 writine a 0 sets the channel high
494 reading a 1 means this channel was low
495 writing a 1 means set this channel low
497 Therefore everything is always inverted. */
499 /* The insn data is a mask in data[0] and the new data
500 * in data[1], each channel cooresponding to a bit. */
502 #ifdef DAMMIT_ITS_BROKEN
503 /* DEBUG */
504 printk("write mask: %08x data: %08x\n", data[0], data[1]);
505 #endif
507 s->state = 0;
509 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
510 /* address of 8-bit port */
511 unsigned long ioaddr = subpriv->iobases[byte_no],
512 /* bit offset of port in 32-bit doubleword */
513 offset = byte_no * 8;
514 /* this 8-bit port's data */
515 unsigned char byte = 0,
516 /* The write mask for this port (if any) */
517 write_mask_byte = (data[0] >> offset) & 0xff,
518 /* The data byte for this port */
519 data_byte = (data[1] >> offset) & 0xff;
521 byte = inb(ioaddr); /* read all 8-bits for this port */
523 #ifdef DAMMIT_ITS_BROKEN
524 /* DEBUG */
525 printk
526 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
527 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
528 offset, ioaddr, (unsigned)byte);
529 #endif
531 if (write_mask_byte) {
532 /* this byte has some write_bits -- so set the output lines */
533 byte &= ~write_mask_byte; /* clear bits for write mask */
534 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
535 /* Write out the new digital output state */
536 outb(byte, ioaddr);
538 #ifdef DAMMIT_ITS_BROKEN
539 /* DEBUG */
540 printk("data_out_byte %02x\n", (unsigned)byte);
541 #endif
542 /* save the digital input lines for this byte.. */
543 s->state |= ((unsigned int)byte) << offset;
546 /* now return the DIO lines to data[1] - note they came inverted! */
547 data[1] = ~s->state;
549 #ifdef DAMMIT_ITS_BROKEN
550 /* DEBUG */
551 printk("s->state %08x data_out %08x\n", s->state, data[1]);
552 #endif
554 return 2;
557 /* The input or output configuration of each digital line is
558 * configured by a special insn_config instruction. chanspec
559 * contains the channel to be changed, and data[0] contains the
560 * value COMEDI_INPUT or COMEDI_OUTPUT. */
561 static int pcmuio_dio_insn_config(struct comedi_device *dev,
562 struct comedi_subdevice *s,
563 struct comedi_insn *insn, unsigned int *data)
565 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
566 chan % 8;
567 unsigned long ioaddr;
568 unsigned char byte;
570 /* Compute ioaddr for this channel */
571 ioaddr = subpriv->iobases[byte_no];
573 /* NOTE:
574 writing a 0 an IO channel's bit sets the channel to INPUT
575 and pulls the line high as well
577 writing a 1 to an IO channel's bit pulls the line low
579 All channels are implicitly always in OUTPUT mode -- but when
580 they are high they can be considered to be in INPUT mode..
582 Thus, we only force channels low if the config request was INPUT,
583 otherwise we do nothing to the hardware. */
585 switch (data[0]) {
586 case INSN_CONFIG_DIO_OUTPUT:
587 /* save to io_bits -- don't actually do anything since
588 all input channels are also output channels... */
589 s->io_bits |= 1 << chan;
590 break;
591 case INSN_CONFIG_DIO_INPUT:
592 /* write a 0 to the actual register representing the channel
593 to set it to 'input'. 0 means "float high". */
594 byte = inb(ioaddr);
595 byte &= ~(1 << bit_no);
596 /**< set input channel to '0' */
598 /* write out byte -- this is the only time we actually affect the
599 hardware as all channels are implicitly output -- but input
600 channels are set to float-high */
601 outb(byte, ioaddr);
603 /* save to io_bits */
604 s->io_bits &= ~(1 << chan);
605 break;
607 case INSN_CONFIG_DIO_QUERY:
608 /* retreive from shadow register */
609 data[1] =
610 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
611 return insn->n;
612 break;
614 default:
615 return -EINVAL;
616 break;
619 return insn->n;
622 static void init_asics(struct comedi_device *dev)
623 { /* sets up an
624 ASIC chip to defaults */
625 int asic;
627 for (asic = 0; asic < thisboard->num_asics; ++asic) {
628 int port, page;
629 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
631 switch_page(dev, asic, 0); /* switch back to page 0 */
633 /* first, clear all the DIO port bits */
634 for (port = 0; port < PORTS_PER_ASIC; ++port)
635 outb(0, baseaddr + REG_PORT0 + port);
637 /* Next, clear all the paged registers for each page */
638 for (page = 1; page < NUM_PAGES; ++page) {
639 int reg;
640 /* now clear all the paged registers */
641 switch_page(dev, asic, page);
642 for (reg = FIRST_PAGED_REG;
643 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
644 outb(0, baseaddr + reg);
647 /* DEBUG set rising edge interrupts on port0 of both asics */
648 /*switch_page(dev, asic, PAGE_POL);
649 outb(0xff, baseaddr + REG_POL0);
650 switch_page(dev, asic, PAGE_ENAB);
651 outb(0xff, baseaddr + REG_ENAB0); */
652 /* END DEBUG */
654 switch_page(dev, asic, 0); /* switch back to default page 0 */
659 static void switch_page(struct comedi_device *dev, int asic, int page)
661 if (asic < 0 || asic >= thisboard->num_asics)
662 return; /* paranoia */
663 if (page < 0 || page >= NUM_PAGES)
664 return; /* more paranoia */
666 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
667 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
669 /* now write out the shadow register */
670 outb(devpriv->asics[asic].pagelock,
671 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
674 #ifdef notused
675 static void lock_port(struct comedi_device *dev, int asic, int port)
677 if (asic < 0 || asic >= thisboard->num_asics)
678 return; /* paranoia */
679 if (port < 0 || port >= PORTS_PER_ASIC)
680 return; /* more paranoia */
682 devpriv->asics[asic].pagelock |= 0x1 << port;
683 /* now write out the shadow register */
684 outb(devpriv->asics[asic].pagelock,
685 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
688 static void unlock_port(struct comedi_device *dev, int asic, int port)
690 if (asic < 0 || asic >= thisboard->num_asics)
691 return; /* paranoia */
692 if (port < 0 || port >= PORTS_PER_ASIC)
693 return; /* more paranoia */
694 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
695 /* now write out the shadow register */
696 outb(devpriv->asics[asic].pagelock,
697 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
699 #endif /* notused */
701 static irqreturn_t interrupt_pcmuio(int irq, void *d)
703 int asic, got1 = 0;
704 struct comedi_device *dev = (struct comedi_device *)d;
706 for (asic = 0; asic < MAX_ASICS; ++asic) {
707 if (irq == devpriv->asics[asic].irq) {
708 unsigned long flags;
709 unsigned triggered = 0;
710 unsigned long iobase = devpriv->asics[asic].iobase;
711 /* it is an interrupt for ASIC #asic */
712 unsigned char int_pend;
714 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
715 flags);
717 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
719 if (int_pend) {
720 int port;
721 for (port = 0; port < INTR_PORTS_PER_ASIC;
722 ++port) {
723 if (int_pend & (0x1 << port)) {
724 unsigned char
725 io_lines_with_edges = 0;
726 switch_page(dev, asic,
727 PAGE_INT_ID);
728 io_lines_with_edges =
729 inb(iobase +
730 REG_INT_ID0 + port);
732 if (io_lines_with_edges)
733 /* clear pending interrupt */
734 outb(0, iobase +
735 REG_INT_ID0 +
736 port);
738 triggered |=
739 io_lines_with_edges <<
740 port * 8;
744 ++got1;
747 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
748 flags);
750 if (triggered) {
751 struct comedi_subdevice *s;
752 /* TODO here: dispatch io lines to subdevs with commands.. */
753 printk
754 ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
755 irq, asic, triggered);
756 for (s = dev->subdevices;
757 s < dev->subdevices + dev->n_subdevices;
758 ++s) {
759 if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
760 unsigned long flags;
761 unsigned oldevents;
763 spin_lock_irqsave(&subpriv->
764 intr.spinlock,
765 flags);
767 oldevents = s->async->events;
769 if (subpriv->intr.active) {
770 unsigned mytrig =
771 ((triggered >>
772 subpriv->intr.asic_chan)
774 ((0x1 << subpriv->
775 intr.
776 num_asic_chans) -
777 1)) << subpriv->
778 intr.first_chan;
779 if (mytrig &
780 subpriv->intr.enabled_mask)
782 unsigned int val
783 = 0;
784 unsigned int n,
785 ch, len;
787 len =
789 async->cmd.chanlist_len;
790 for (n = 0;
791 n < len;
792 n++) {
793 ch = CR_CHAN(s->async->cmd.chanlist[n]);
794 if (mytrig & (1U << ch)) {
795 val |= (1U << n);
798 /* Write the scan to the buffer. */
799 if (comedi_buf_put(s->async, ((short *)&val)[0])
801 comedi_buf_put
802 (s->async,
803 ((short *)
804 &val)[1]))
806 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
807 } else {
808 /* Overflow! Stop acquisition!! */
809 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
810 pcmuio_stop_intr
811 (dev,
815 /* Check for end of acquisition. */
816 if (!subpriv->intr.continuous) {
817 /* stop_src == TRIG_COUNT */
818 if (subpriv->intr.stop_count > 0) {
819 subpriv->intr.stop_count--;
820 if (subpriv->intr.stop_count == 0) {
821 s->async->events |= COMEDI_CB_EOA;
822 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
823 pcmuio_stop_intr
824 (dev,
832 spin_unlock_irqrestore
833 (&subpriv->intr.spinlock,
834 flags);
836 if (oldevents !=
837 s->async->events) {
838 comedi_event(dev, s);
848 if (!got1)
849 return IRQ_NONE; /* interrupt from other source */
850 return IRQ_HANDLED;
853 static void pcmuio_stop_intr(struct comedi_device *dev,
854 struct comedi_subdevice *s)
856 int nports, firstport, asic, port;
858 asic = subpriv->intr.asic;
859 if (asic < 0)
860 return; /* not an interrupt subdev */
862 subpriv->intr.enabled_mask = 0;
863 subpriv->intr.active = 0;
864 s->async->inttrig = 0;
865 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
866 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
867 switch_page(dev, asic, PAGE_ENAB);
868 for (port = firstport; port < firstport + nports; ++port) {
869 /* disable all intrs for this subdev.. */
870 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
874 static int pcmuio_start_intr(struct comedi_device *dev,
875 struct comedi_subdevice *s)
877 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
878 /* An empty acquisition! */
879 s->async->events |= COMEDI_CB_EOA;
880 subpriv->intr.active = 0;
881 return 1;
882 } else {
883 unsigned bits = 0, pol_bits = 0, n;
884 int nports, firstport, asic, port;
885 struct comedi_cmd *cmd = &s->async->cmd;
887 asic = subpriv->intr.asic;
888 if (asic < 0)
889 return 1; /* not an interrupt
890 subdev */
891 subpriv->intr.enabled_mask = 0;
892 subpriv->intr.active = 1;
893 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
894 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
895 if (cmd->chanlist) {
896 for (n = 0; n < cmd->chanlist_len; n++) {
897 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
898 pol_bits |= (CR_AREF(cmd->chanlist[n])
899 || CR_RANGE(cmd->
900 chanlist[n]) ? 1U : 0U)
901 << CR_CHAN(cmd->chanlist[n]);
904 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
905 1) << subpriv->intr.first_chan;
906 subpriv->intr.enabled_mask = bits;
908 switch_page(dev, asic, PAGE_ENAB);
909 for (port = firstport; port < firstport + nports; ++port) {
910 unsigned enab =
911 bits >> (subpriv->intr.first_chan + (port -
912 firstport) *
913 8) & 0xff, pol =
914 pol_bits >> (subpriv->intr.first_chan +
915 (port - firstport) * 8) & 0xff;
916 /* set enab intrs for this subdev.. */
917 outb(enab,
918 devpriv->asics[asic].iobase + REG_ENAB0 + port);
919 switch_page(dev, asic, PAGE_POL);
920 outb(pol,
921 devpriv->asics[asic].iobase + REG_ENAB0 + port);
924 return 0;
927 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
929 unsigned long flags;
931 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
932 if (subpriv->intr.active)
933 pcmuio_stop_intr(dev, s);
934 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
936 return 0;
940 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
942 static int
943 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
944 unsigned int trignum)
946 unsigned long flags;
947 int event = 0;
949 if (trignum != 0)
950 return -EINVAL;
952 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
953 s->async->inttrig = 0;
954 if (subpriv->intr.active) {
955 event = pcmuio_start_intr(dev, s);
957 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
959 if (event) {
960 comedi_event(dev, s);
963 return 1;
967 * 'do_cmd' function for an 'INTERRUPT' subdevice.
969 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
971 struct comedi_cmd *cmd = &s->async->cmd;
972 unsigned long flags;
973 int event = 0;
975 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
976 subpriv->intr.active = 1;
978 /* Set up end of acquisition. */
979 switch (cmd->stop_src) {
980 case TRIG_COUNT:
981 subpriv->intr.continuous = 0;
982 subpriv->intr.stop_count = cmd->stop_arg;
983 break;
984 default:
985 /* TRIG_NONE */
986 subpriv->intr.continuous = 1;
987 subpriv->intr.stop_count = 0;
988 break;
991 /* Set up start of acquisition. */
992 switch (cmd->start_src) {
993 case TRIG_INT:
994 s->async->inttrig = pcmuio_inttrig_start_intr;
995 break;
996 default:
997 /* TRIG_NOW */
998 event = pcmuio_start_intr(dev, s);
999 break;
1001 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
1003 if (event) {
1004 comedi_event(dev, s);
1007 return 0;
1010 static int
1011 pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1012 struct comedi_cmd *cmd)
1014 return comedi_pcm_cmdtest(dev, s, cmd);
1018 * A convenient macro that defines init_module() and cleanup_module(),
1019 * as necessary.
1021 static int __init driver_init_module(void)
1023 return comedi_driver_register(&driver);
1026 static void __exit driver_cleanup_module(void)
1028 comedi_driver_unregister(&driver);
1031 module_init(driver_init_module);
1032 module_exit(driver_cleanup_module);
1034 MODULE_AUTHOR("Comedi http://www.comedi.org");
1035 MODULE_DESCRIPTION("Comedi low-level driver");
1036 MODULE_LICENSE("GPL");