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.
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
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)*/)
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. */
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
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)
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 {
154
const int num_channels_per_port;
158
static const struct pcmuio_board pcmuio_boards[] = {
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 */
183
int asic; /* if non-negative, this subdev has an interrupt asic */
184
int first_chan; /* if nonnegative, the first channel id for
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 */
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 {
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 */
208
unsigned long iobase;
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
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);
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);
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
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,
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");
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");
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
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);
337
kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
339
if (!devpriv->sprivs) {
340
printk("cannot allocate subdevice private data structures\n");
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");
357
for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
360
s = dev->subdevices + sdev_no;
361
s->private = devpriv->sprivs + sdev_no;
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;
376
/* save the ioport address for each 'port' of 8 channels in the
378
for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
379
if (port >= PORTS_PER_ASIC) {
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;
412
asic = 0; /* reset the asic to our first asic, to do intr subdevs */
418
init_asics(dev); /* clear out all the registers, basically */
420
for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
422
&& request_irq(irq[asic], interrupt_pcmuio,
423
IRQF_SHARED, thisboard->name, dev)) {
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;
432
devpriv->asics[asic].irq = irq[asic];
435
dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
439
printk("irq: %u ", irq[0]);
440
if (irq[1] && thisboard->num_asics == 2)
441
printk("second ASIC irq: %u ", irq[1]);
443
printk("(IRQ mode disabled) ");
446
printk("attached\n");
452
* _detach is called to deconfigure a device. It should deallocate
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)
463
printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
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);
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)
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
504
printk("write mask: %08x data: %08x\n", data[0], data[1]);
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
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);
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 */
538
#ifdef DAMMIT_ITS_BROKEN
540
printk("data_out_byte %02x\n", (unsigned)byte);
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! */
549
#ifdef DAMMIT_ITS_BROKEN
551
printk("s->state %08x data_out %08x\n", s->state, data[1]);
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 =
567
unsigned long ioaddr;
570
/* Compute ioaddr for this channel */
571
ioaddr = subpriv->iobases[byte_no];
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. */
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;
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". */
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 */
603
/* save to io_bits */
604
s->io_bits &= ~(1 << chan);
607
case INSN_CONFIG_DIO_QUERY:
608
/* retrieve from shadow register */
610
(s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
622
static void init_asics(struct comedi_device *dev)
624
ASIC chip to defaults */
627
for (asic = 0; asic < thisboard->num_asics; ++asic) {
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) {
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); */
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);
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);
701
static irqreturn_t interrupt_pcmuio(int irq, void *d)
704
struct comedi_device *dev = (struct comedi_device *)d;
706
for (asic = 0; asic < MAX_ASICS; ++asic) {
707
if (irq == devpriv->asics[asic].irq) {
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,
717
int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
721
for (port = 0; port < INTR_PORTS_PER_ASIC;
723
if (int_pend & (0x1 << port)) {
725
io_lines_with_edges = 0;
726
switch_page(dev, asic,
728
io_lines_with_edges =
732
if (io_lines_with_edges)
733
/* clear pending interrupt */
739
io_lines_with_edges <<
747
spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
751
struct comedi_subdevice *s;
752
/* TODO here: dispatch io lines to subdevs with commands.. */
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;
759
if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
763
spin_lock_irqsave(&subpriv->
767
oldevents = s->async->events;
769
if (subpriv->intr.active) {
772
subpriv->intr.asic_chan)
780
subpriv->intr.enabled_mask)
789
async->cmd.chanlist_len;
793
ch = CR_CHAN(s->async->cmd.chanlist[n]);
794
if (mytrig & (1U << ch)) {
798
/* Write the scan to the buffer. */
799
if (comedi_buf_put(s->async, ((short *)&val)[0])
806
s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
808
/* Overflow! Stop acquisition!! */
809
/* TODO: STOP_ACQUISITION_CALL_HERE!! */
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!! */
832
spin_unlock_irqrestore
833
(&subpriv->intr.spinlock,
838
comedi_event(dev, s);
849
return IRQ_NONE; /* interrupt from other source */
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;
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;
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;
889
return 1; /* not an interrupt
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;
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])
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) {
911
bits >> (subpriv->intr.first_chan + (port -
914
pol_bits >> (subpriv->intr.first_chan +
915
(port - firstport) * 8) & 0xff;
916
/* set enab intrs for this subdev.. */
918
devpriv->asics[asic].iobase + REG_ENAB0 + port);
919
switch_page(dev, asic, PAGE_POL);
921
devpriv->asics[asic].iobase + REG_ENAB0 + port);
927
static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
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);
940
* Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
943
pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
944
unsigned int trignum)
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);
960
comedi_event(dev, s);
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;
975
spin_lock_irqsave(&subpriv->intr.spinlock, flags);
976
subpriv->intr.active = 1;
978
/* Set up end of acquisition. */
979
switch (cmd->stop_src) {
981
subpriv->intr.continuous = 0;
982
subpriv->intr.stop_count = cmd->stop_arg;
986
subpriv->intr.continuous = 1;
987
subpriv->intr.stop_count = 0;
991
/* Set up start of acquisition. */
992
switch (cmd->start_src) {
994
s->async->inttrig = pcmuio_inttrig_start_intr;
998
event = pcmuio_start_intr(dev, s);
1001
spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
1004
comedi_event(dev, s);
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(),
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");