]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/staging/comedi/drivers/pcmmio.c
staging: comedi: fix return value for insn_bits functions
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / pcmmio.c
CommitLineData
6baef150
CC
1/*
2 comedi/drivers/pcmmio.c
3 Driver for Winsystems PC-104 based multifunction IO board.
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
7
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.
12
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.
17
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.
21*/
22/*
23Driver: pcmmio
24Description: A driver for the PCM-MIO multifunction board
25Devices: [Winsystems] PCM-MIO (pcmmio)
26Author: Calin Culianu <calin@ajvar.org>
27Updated: Wed, May 16 2007 16:21:10 -0500
28Status: works
29
30A driver for the relatively new PCM-MIO multifunction board from
31Winsystems. This board is a PC-104 based I/O board. It contains
32four subdevices:
33 subdevice 0 - 16 channels of 16-bit AI
34 subdevice 1 - 8 channels of 16-bit AO
d2d08955
DPJ
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)
6baef150
CC
39
40 Some notes:
41
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...
45
46 Asynchronous I/O for the DIO subdevices *is* implemented, however! They are
47 basically edge-triggered interrupts for any configuration of the first
48 24 DIO-lines.
49
50 Also note that this interrupt support is untested.
51
52 A few words about edge-detection IRQ support (commands on DIO):
53
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.
57
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.
60
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!
68
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.
72
73Configuration Options:
74 [0] - I/O port base address
d2d08955
DPJ
75 [1] - IRQ (optional -- for edge-detect interrupt support only,
76 leave out if you don't need this feature)
6baef150
CC
77*/
78
25436dc9 79#include <linux/interrupt.h>
5a0e3ad6 80#include <linux/slab.h>
6baef150 81#include "../comedidev.h"
0b8f754a 82#include "pcm_common.h"
6baef150
CC
83#include <linux/pci.h> /* for PCI devices */
84
6baef150
CC
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)*/)
98/* IO Memory sizes */
99#define ASIC_IOSIZE (0x0B)
100#define PCMMIO48_IOSIZE ASIC_IOSIZE
101
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.
105
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.
114 */
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
d2d08955
DPJ
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.
126 */
6baef150
CC
127#define REG_POL0 0x8
128#define REG_POL1 0x9
129#define REG_POL2 0xA
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
136
137#define NUM_PAGED_REGS 3
138#define NUM_PAGES 4
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))
d2d08955 143#define REG_LOCK_MASK (~(REG_PAGE_MASK))
6baef150
CC
144#define PAGE_POL 1
145#define PAGE_ENAB 2
146#define PAGE_INT_ID 3
147
6baef150
CC
148/*
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.
152 */
657f81ec 153struct pcmmio_board {
6baef150
CC
154 const char *name;
155 const int dio_num_asics;
156 const int dio_num_ports;
157 const int total_iosize;
158 const int ai_bits;
159 const int ao_bits;
160 const int n_ai_chans;
161 const int n_ao_chans;
9ced1de6 162 const struct comedi_lrange *ai_range_table, *ao_range_table;
56b8421c
AT
163 int (*ai_rinsn) (struct comedi_device *dev,
164 struct comedi_subdevice *s,
165 struct comedi_insn *insn,
166 unsigned int *data);
167 int (*ao_rinsn) (struct comedi_device *dev,
168 struct comedi_subdevice *s,
169 struct comedi_insn *insn,
170 unsigned int *data);
171 int (*ao_winsn) (struct comedi_device *dev,
172 struct comedi_subdevice *s,
173 struct comedi_insn *insn,
174 unsigned int *data);
657f81ec 175};
6baef150 176
d2d08955
DPJ
177static const struct comedi_lrange ranges_ai = {
178 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
6baef150
CC
179};
180
d2d08955
DPJ
181static const struct comedi_lrange ranges_ao = {
182 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
0a85b6f0 183 RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
6baef150
CC
184};
185
6baef150 186/* this structure is for data unique to this subdevice. */
4467df94 187struct pcmmio_subdev_private {
6baef150
CC
188
189 union {
d2d08955
DPJ
190 /* for DIO: mapping of halfwords (bytes)
191 in port/chanarray to iobase */
6baef150
CC
192 unsigned long iobases[PORTS_PER_SUBDEV];
193
194 /* for AI/AO */
195 unsigned long iobase;
196 };
197 union {
198 struct {
199
200 /* The below is only used for intr subdevices */
201 struct {
d2d08955
DPJ
202 /*
203 * if non-negative, this subdev has an
204 * interrupt asic
205 */
206 int asic;
207 /*
208 * if nonnegative, the first channel id for
209 * interrupts.
210 */
211 int first_chan;
212 /*
213 * the number of asic channels in this subdev
214 * that have interrutps
215 */
216 int num_asic_chans;
217 /*
218 * if nonnegative, the first channel id with
219 * respect to the asic that has interrupts
220 */
221 int asic_chan;
222 /*
223 * subdev-relative channel mask for channels
224 * we are interested in
225 */
226 int enabled_mask;
6baef150
CC
227 int active;
228 int stop_count;
229 int continuous;
230 spinlock_t spinlock;
231 } intr;
232 } dio;
233 struct {
d2d08955
DPJ
234 /* the last unsigned int data written */
235 unsigned int shadow_samples[8];
6baef150
CC
236 } ao;
237 };
4467df94 238};
6baef150 239
d2d08955
DPJ
240/*
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.
244 */
e56ab715 245struct pcmmio_private {
6baef150
CC
246 /* stuff for DIO */
247 struct {
248 unsigned char pagelock; /* current page and lock */
d2d08955
DPJ
249 /* shadow of POLx registers */
250 unsigned char pol[NUM_PAGED_REGS];
251 /* shadow of ENABx registers */
252 unsigned char enab[NUM_PAGED_REGS];
6baef150
CC
253 int num;
254 unsigned long iobase;
255 unsigned int irq;
256 spinlock_t spinlock;
257 } asics[MAX_ASICS];
4467df94 258 struct pcmmio_subdev_private *sprivs;
e56ab715 259};
6baef150
CC
260
261/*
262 * most drivers define the following macro to make it easy to
263 * access the private structure.
264 */
e56ab715 265#define devpriv ((struct pcmmio_private *)dev->private)
4467df94 266#define subpriv ((struct pcmmio_subdev_private *)s->private)
6baef150
CC
267
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 */
0a85b6f0
MT
273static int pcmmio_dio_insn_bits(struct comedi_device *dev,
274 struct comedi_subdevice *s,
275 struct comedi_insn *insn, unsigned int *data)
6baef150
CC
276{
277 int byte_no;
6baef150
CC
278
279 /* NOTE:
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
284
285 Therefore everything is always inverted. */
286
287 /* The insn data is a mask in data[0] and the new data
288 * in data[1], each channel cooresponding to a bit. */
289
290#ifdef DAMMIT_ITS_BROKEN
291 /* DEBUG */
bcd9a1e9 292 printk(KERN_DEBUG "write mask: %08x data: %08x\n", data[0], data[1]);
6baef150
CC
293#endif
294
295 s->state = 0;
296
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],
0a85b6f0
MT
300 /* bit offset of port in 32-bit doubleword */
301 offset = byte_no * 8;
6baef150
CC
302 /* this 8-bit port's data */
303 unsigned char byte = 0,
0a85b6f0
MT
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;
6baef150
CC
308
309 byte = inb(ioaddr); /* read all 8-bits for this port */
310
311#ifdef DAMMIT_ITS_BROKEN
312 /* DEBUG */
0a85b6f0 313 printk
bcd9a1e9
JT
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);
6baef150
CC
317#endif
318
319 if (write_mask_byte) {
d2d08955
DPJ
320 /*
321 * this byte has some write_bits
322 * -- so set the output lines
323 */
324 /* clear bits for write mask */
325 byte &= ~write_mask_byte;
326 /* set to inverted data_byte */
327 byte |= ~data_byte & write_mask_byte;
6baef150
CC
328 /* Write out the new digital output state */
329 outb(byte, ioaddr);
330 }
331#ifdef DAMMIT_ITS_BROKEN
332 /* DEBUG */
6a857636 333 printk(KERN_DEBUG "data_out_byte %02x\n", (unsigned)byte);
6baef150
CC
334#endif
335 /* save the digital input lines for this byte.. */
336 s->state |= ((unsigned int)byte) << offset;
337 }
338
339 /* now return the DIO lines to data[1] - note they came inverted! */
340 data[1] = ~s->state;
341
342#ifdef DAMMIT_ITS_BROKEN
343 /* DEBUG */
bcd9a1e9 344 printk(KERN_DEBUG "s->state %08x data_out %08x\n", s->state, data[1]);
6baef150
CC
345#endif
346
a2714e3e 347 return insn->n;
6baef150
CC
348}
349
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. */
0a85b6f0
MT
354static int pcmmio_dio_insn_config(struct comedi_device *dev,
355 struct comedi_subdevice *s,
356 struct comedi_insn *insn, unsigned int *data)
6baef150
CC
357{
358 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
0a85b6f0 359 chan % 8;
6baef150
CC
360 unsigned long ioaddr;
361 unsigned char byte;
362
363 /* Compute ioaddr for this channel */
364 ioaddr = subpriv->iobases[byte_no];
365
366 /* NOTE:
367 writing a 0 an IO channel's bit sets the channel to INPUT
368 and pulls the line high as well
369
370 writing a 1 to an IO channel's bit pulls the line low
371
372 All channels are implicitly always in OUTPUT mode -- but when
373 they are high they can be considered to be in INPUT mode..
374
375 Thus, we only force channels low if the config request was INPUT,
376 otherwise we do nothing to the hardware. */
377
378 switch (data[0]) {
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;
383 break;
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". */
387 byte = inb(ioaddr);
388 byte &= ~(1 << bit_no);
389 /**< set input channel to '0' */
390
013f230c
DPJ
391 /*
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
395 */
6baef150
CC
396 outb(byte, ioaddr);
397
398 /* save to io_bits */
399 s->io_bits &= ~(1 << chan);
400 break;
401
402 case INSN_CONFIG_DIO_QUERY:
25985edc 403 /* retrieve from shadow register */
6baef150 404 data[1] =
0a85b6f0 405 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
6baef150
CC
406 return insn->n;
407 break;
408
409 default:
410 return -EINVAL;
411 break;
412 }
413
414 return insn->n;
415}
416
b2bb98e1
HS
417static void switch_page(struct comedi_device *dev, int asic, int page)
418{
57bbeb3b
HS
419 const struct pcmmio_board *board = comedi_board(dev);
420
421 if (asic < 0 || asic >= board->dio_num_asics)
b2bb98e1
HS
422 return; /* paranoia */
423 if (page < 0 || page >= NUM_PAGES)
424 return; /* more paranoia */
425
426 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
427 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
428
429 /* now write out the shadow register */
430 outb(devpriv->asics[asic].pagelock,
431 devpriv->asics[asic].iobase + REG_PAGELOCK);
432}
433
da91b269 434static void init_asics(struct comedi_device *dev)
6baef150
CC
435{ /* sets up an
436 ASIC chip to defaults */
57bbeb3b 437 const struct pcmmio_board *board = comedi_board(dev);
6baef150
CC
438 int asic;
439
57bbeb3b 440 for (asic = 0; asic < board->dio_num_asics; ++asic) {
6baef150
CC
441 int port, page;
442 unsigned long baseaddr = devpriv->asics[asic].iobase;
443
444 switch_page(dev, asic, 0); /* switch back to page 0 */
445
446 /* first, clear all the DIO port bits */
447 for (port = 0; port < PORTS_PER_ASIC; ++port)
448 outb(0, baseaddr + REG_PORT0 + port);
449
450 /* Next, clear all the paged registers for each page */
451 for (page = 1; page < NUM_PAGES; ++page) {
452 int reg;
453 /* now clear all the paged registers */
454 switch_page(dev, asic, page);
455 for (reg = FIRST_PAGED_REG;
0a85b6f0 456 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
6baef150
CC
457 outb(0, baseaddr + reg);
458 }
459
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); */
465 /* END DEBUG */
466
013f230c
DPJ
467 /* switch back to default page 0 */
468 switch_page(dev, asic, 0);
6baef150
CC
469 }
470}
471
6baef150 472#ifdef notused
da91b269 473static void lock_port(struct comedi_device *dev, int asic, int port)
6baef150 474{
57bbeb3b
HS
475 const struct pcmmio_board *board = comedi_board(dev);
476
477 if (asic < 0 || asic >= board->dio_num_asics)
6baef150
CC
478 return; /* paranoia */
479 if (port < 0 || port >= PORTS_PER_ASIC)
480 return; /* more paranoia */
481
482 devpriv->asics[asic].pagelock |= 0x1 << port;
483 /* now write out the shadow register */
484 outb(devpriv->asics[asic].pagelock,
0a85b6f0 485 devpriv->asics[asic].iobase + REG_PAGELOCK);
6baef150
CC
486 return;
487}
488
da91b269 489static void unlock_port(struct comedi_device *dev, int asic, int port)
6baef150 490{
57bbeb3b
HS
491 const struct pcmmio_board *board = comedi_board(dev);
492
493 if (asic < 0 || asic >= board->dio_num_asics)
6baef150
CC
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,
0a85b6f0 500 devpriv->asics[asic].iobase + REG_PAGELOCK);
6baef150
CC
501}
502#endif /* notused */
503
b2bb98e1
HS
504static void pcmmio_stop_intr(struct comedi_device *dev,
505 struct comedi_subdevice *s)
506{
507 int nports, firstport, asic, port;
508
509 asic = subpriv->dio.intr.asic;
510 if (asic < 0)
511 return; /* not an interrupt subdev */
512
513 subpriv->dio.intr.enabled_mask = 0;
514 subpriv->dio.intr.active = 0;
515 s->async->inttrig = 0;
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);
522 }
523}
524
70265d24 525static irqreturn_t interrupt_pcmmio(int irq, void *d)
6baef150
CC
526{
527 int asic, got1 = 0;
0a85b6f0 528 struct comedi_device *dev = (struct comedi_device *)d;
6baef150
CC
529
530 for (asic = 0; asic < MAX_ASICS; ++asic) {
531 if (irq == devpriv->asics[asic].irq) {
532 unsigned long flags;
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;
537
0a85b6f0
MT
538 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
539 flags);
6baef150
CC
540
541 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
542
543 if (int_pend) {
544 int port;
545 for (port = 0; port < INTR_PORTS_PER_ASIC;
0a85b6f0 546 ++port) {
6baef150
CC
547 if (int_pend & (0x1 << port)) {
548 unsigned char
0a85b6f0 549 io_lines_with_edges = 0;
6baef150 550 switch_page(dev, asic,
0a85b6f0 551 PAGE_INT_ID);
6baef150 552 io_lines_with_edges =
0a85b6f0 553 inb(iobase +
6baef150
CC
554 REG_INT_ID0 + port);
555
556 if (io_lines_with_edges)
013f230c
DPJ
557 /*
558 * clear pending
559 * interrupt
560 */
6baef150 561 outb(0, iobase +
0a85b6f0
MT
562 REG_INT_ID0 +
563 port);
6baef150
CC
564
565 triggered |=
0a85b6f0
MT
566 io_lines_with_edges <<
567 port * 8;
6baef150
CC
568 }
569 }
570
571 ++got1;
572 }
573
0a85b6f0
MT
574 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
575 flags);
6baef150
CC
576
577 if (triggered) {
34c43922 578 struct comedi_subdevice *s;
013f230c
DPJ
579 /*
580 * TODO here: dispatch io lines to subdevs
581 * with commands..
582 */
0a85b6f0 583 printk
bcd9a1e9 584 (KERN_DEBUG "got edge detect interrupt %d asic %d which_chans: %06x\n",
0a85b6f0 585 irq, asic, triggered);
6baef150 586 for (s = dev->subdevices + 2;
0a85b6f0
MT
587 s < dev->subdevices + dev->n_subdevices;
588 ++s) {
013f230c
DPJ
589 /*
590 * this is an interrupt subdev,
591 * and it matches this asic!
592 */
593 if (subpriv->dio.intr.asic == asic) {
6baef150
CC
594 unsigned long flags;
595 unsigned oldevents;
596
0a85b6f0
MT
597 spin_lock_irqsave(&subpriv->dio.
598 intr.spinlock,
599 flags);
6baef150
CC
600
601 oldevents = s->async->events;
602
603 if (subpriv->dio.intr.active) {
604 unsigned mytrig =
0a85b6f0
MT
605 ((triggered >>
606 subpriv->dio.intr.asic_chan)
607 &
608 ((0x1 << subpriv->
609 dio.intr.
610 num_asic_chans) -
611 1)) << subpriv->
612 dio.intr.first_chan;
613 if (mytrig &
614 subpriv->dio.
615 intr.enabled_mask) {
616 unsigned int val
617 = 0;
6baef150 618 unsigned int n,
0a85b6f0 619 ch, len;
6baef150 620
0a85b6f0
MT
621 len =
622 s->
623 async->cmd.chanlist_len;
6baef150 624 for (n = 0;
0a85b6f0
MT
625 n < len;
626 n++) {
6baef150 627 ch = CR_CHAN(s->async->cmd.chanlist[n]);
013f230c 628 if (mytrig & (1U << ch))
6baef150 629 val |= (1U << n);
6baef150
CC
630 }
631 /* Write the scan to the buffer. */
0a85b6f0
MT
632 if (comedi_buf_put(s->async, ((short *)&val)[0])
633 &&
634 comedi_buf_put
635 (s->async,
636 ((short *)
013f230c 637 &val)[1])) {
6baef150
CC
638 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
639 } else {
640 /* Overflow! Stop acquisition!! */
641 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
642 pcmmio_stop_intr
0a85b6f0
MT
643 (dev,
644 s);
6baef150
CC
645 }
646
647 /* Check for end of acquisition. */
0a85b6f0 648 if (!subpriv->dio.intr.continuous) {
6baef150
CC
649 /* stop_src == TRIG_COUNT */
650 if (subpriv->dio.intr.stop_count > 0) {
0a85b6f0 651 subpriv->dio.intr.stop_count--;
6baef150
CC
652 if (subpriv->dio.intr.stop_count == 0) {
653 s->async->events |= COMEDI_CB_EOA;
654 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
655 pcmmio_stop_intr
0a85b6f0
MT
656 (dev,
657 s);
6baef150
CC
658 }
659 }
660 }
661 }
662 }
663
0a85b6f0
MT
664 spin_unlock_irqrestore
665 (&subpriv->dio.intr.
666 spinlock, flags);
6baef150
CC
667
668 if (oldevents !=
0a85b6f0 669 s->async->events) {
6baef150
CC
670 comedi_event(dev, s);
671 }
672
673 }
674
b2bb98e1
HS
675 }
676 }
677
678 }
6baef150 679 }
b2bb98e1
HS
680 if (!got1)
681 return IRQ_NONE; /* interrupt from other source */
682 return IRQ_HANDLED;
6baef150
CC
683}
684
0a85b6f0
MT
685static int pcmmio_start_intr(struct comedi_device *dev,
686 struct comedi_subdevice *s)
6baef150
CC
687{
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;
692 return 1;
693 } else {
694 unsigned bits = 0, pol_bits = 0, n;
695 int nports, firstport, asic, port;
ea6d0d4c 696 struct comedi_cmd *cmd = &s->async->cmd;
6baef150 697
c3744138 698 asic = subpriv->dio.intr.asic;
0a85b6f0 699 if (asic < 0)
6baef150
CC
700 return 1; /* not an interrupt
701 subdev */
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;
706 if (cmd->chanlist) {
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])
0a85b6f0
MT
710 || CR_RANGE(cmd->
711 chanlist[n]) ? 1U : 0U)
712 << CR_CHAN(cmd->chanlist[n]);
6baef150
CC
713 }
714 }
715 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
0a85b6f0 716 1) << subpriv->dio.intr.first_chan;
6baef150
CC
717 subpriv->dio.intr.enabled_mask = bits;
718
013f230c
DPJ
719 {
720 /*
721 * the below code configures the board
722 * to use a specific IRQ from 0-15.
723 */
6baef150 724 unsigned char b;
013f230c
DPJ
725 /*
726 * set resource enable register
727 * to enable IRQ operation
728 */
6baef150
CC
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 */
734 }
735
736 switch_page(dev, asic, PAGE_ENAB);
737 for (port = firstport; port < firstport + nports; ++port) {
738 unsigned enab =
0a85b6f0
MT
739 bits >> (subpriv->dio.intr.first_chan + (port -
740 firstport)
741 * 8) & 0xff, pol =
742 pol_bits >> (subpriv->dio.intr.first_chan +
743 (port - firstport) * 8) & 0xff;
6baef150
CC
744 /* set enab intrs for this subdev.. */
745 outb(enab,
0a85b6f0 746 devpriv->asics[asic].iobase + REG_ENAB0 + port);
6baef150
CC
747 switch_page(dev, asic, PAGE_POL);
748 outb(pol,
0a85b6f0 749 devpriv->asics[asic].iobase + REG_ENAB0 + port);
6baef150
CC
750 }
751 }
752 return 0;
753}
754
da91b269 755static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
6baef150
CC
756{
757 unsigned long flags;
758
5f74ea14 759 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
6baef150
CC
760 if (subpriv->dio.intr.active)
761 pcmmio_stop_intr(dev, s);
5f74ea14 762 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
6baef150
CC
763
764 return 0;
765}
766
767/*
768 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
769 */
770static int
da91b269 771pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 772 unsigned int trignum)
6baef150
CC
773{
774 unsigned long flags;
775 int event = 0;
776
777 if (trignum != 0)
778 return -EINVAL;
779
5f74ea14 780 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
6baef150 781 s->async->inttrig = 0;
013f230c 782 if (subpriv->dio.intr.active)
6baef150 783 event = pcmmio_start_intr(dev, s);
5f74ea14 784 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
6baef150 785
013f230c 786 if (event)
6baef150 787 comedi_event(dev, s);
6baef150
CC
788
789 return 1;
790}
791
792/*
793 * 'do_cmd' function for an 'INTERRUPT' subdevice.
794 */
da91b269 795static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
6baef150 796{
ea6d0d4c 797 struct comedi_cmd *cmd = &s->async->cmd;
6baef150
CC
798 unsigned long flags;
799 int event = 0;
800
5f74ea14 801 spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
6baef150
CC
802 subpriv->dio.intr.active = 1;
803
804 /* Set up end of acquisition. */
805 switch (cmd->stop_src) {
806 case TRIG_COUNT:
807 subpriv->dio.intr.continuous = 0;
808 subpriv->dio.intr.stop_count = cmd->stop_arg;
809 break;
810 default:
811 /* TRIG_NONE */
812 subpriv->dio.intr.continuous = 1;
813 subpriv->dio.intr.stop_count = 0;
814 break;
815 }
816
817 /* Set up start of acquisition. */
818 switch (cmd->start_src) {
819 case TRIG_INT:
820 s->async->inttrig = pcmmio_inttrig_start_intr;
821 break;
822 default:
823 /* TRIG_NOW */
824 event = pcmmio_start_intr(dev, s);
825 break;
826 }
5f74ea14 827 spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
6baef150 828
013f230c 829 if (event)
6baef150 830 comedi_event(dev, s);
6baef150
CC
831
832 return 0;
833}
834
6baef150 835static int
0a85b6f0
MT
836pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
837 struct comedi_cmd *cmd)
6baef150 838{
0b8f754a 839 return comedi_pcm_cmdtest(dev, s, cmd);
6baef150
CC
840}
841
842static int adc_wait_ready(unsigned long iobase)
843{
844 unsigned long retry = 100000;
845 while (retry--)
846 if (inb(iobase + 3) & 0x80)
847 return 0;
848 return 1;
849}
850
851/* All this is for AI and AO */
da91b269 852static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 853 struct comedi_insn *insn, unsigned int *data)
6baef150
CC
854{
855 int n;
856 unsigned long iobase = subpriv->iobase;
857
858 /*
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)
865
866 Additionally note that the BASE += 4 if the channel >= 8
867 */
868
869 /* convert n samples */
870 for (n = 0; n < insn->n; n++) {
871 unsigned chan = CR_CHAN(insn->chanspec), range =
0a85b6f0 872 CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
6baef150
CC
873 unsigned char command_byte = 0;
874 unsigned iooffset = 0;
790c5541 875 short sample, adc_adjust = 0;
6baef150
CC
876
877 if (chan > 7)
013f230c
DPJ
878 chan -= 8, iooffset = 4; /*
879 * use the second dword
880 * for channels > 7
881 */
6baef150
CC
882
883 if (aref != AREF_DIFF) {
884 aref = AREF_GROUND;
013f230c
DPJ
885 command_byte |= 1 << 7; /*
886 * set bit 7 to indicate
887 * single-ended
888 */
6baef150
CC
889 }
890 if (range < 2)
013f230c
DPJ
891 adc_adjust = 0x8000; /*
892 * bipolar ranges
893 * (-5,5 .. -10,10 need to be
894 * adjusted -- that is.. they
895 * need to wrap around by
896 * adding 0x8000
897 */
6baef150
CC
898
899 if (chan % 2) {
013f230c
DPJ
900 command_byte |= 1 << 6; /*
901 * odd-numbered channels
902 * have bit 6 set
903 */
6baef150
CC
904 }
905
906 /* select the channel, bits 4-5 == chan/2 */
907 command_byte |= ((chan / 2) & 0x3) << 4;
908
909 /* set the range, bits 2-3 */
910 command_byte |= (range & 0x3) << 2;
911
912 /* need to do this twice to make sure mux settled */
013f230c
DPJ
913 /* chan/range/aref select */
914 outb(command_byte, iobase + iooffset + 2);
6baef150 915
013f230c
DPJ
916 /* wait for the adc to say it finised the conversion */
917 adc_wait_ready(iobase + iooffset);
6baef150 918
013f230c
DPJ
919 /* select the chan/range/aref AGAIN */
920 outb(command_byte, iobase + iooffset + 2);
6baef150
CC
921
922 adc_wait_ready(iobase + iooffset);
923
013f230c
DPJ
924 /* read data lo byte */
925 sample = inb(iobase + iooffset + 0);
926
927 /* read data hi byte */
928 sample |= inb(iobase + iooffset + 1) << 8;
6baef150
CC
929 sample += adc_adjust; /* adjustment .. munge data */
930 data[n] = sample;
931 }
932 /* return the number of samples read/written */
933 return n;
934}
935
da91b269 936static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 937 struct comedi_insn *insn, unsigned int *data)
6baef150
CC
938{
939 int n;
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];
944 }
945 return n;
946}
947
948static int wait_dac_ready(unsigned long iobase)
949{
950 unsigned long retry = 100000L;
951
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
25985edc 956 non-existent hardware. */
6baef150
CC
957
958 while (retry--) {
959 if (inb(iobase + 3) & 0x80)
960 return 0;
961
962 }
963 return 1;
964}
965
da91b269 966static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 967 struct comedi_insn *insn, unsigned int *data)
6baef150
CC
968{
969 int n;
970 unsigned iobase = subpriv->iobase, iooffset = 0;
971
972 for (n = 0; n < insn->n; n++) {
973 unsigned chan = CR_CHAN(insn->chanspec), range =
0a85b6f0 974 CR_RANGE(insn->chanspec);
6baef150
CC
975 if (chan < s->n_chan) {
976 unsigned char command_byte = 0, range_byte =
0a85b6f0 977 range & ((1 << 4) - 1);
6baef150
CC
978 if (chan >= 4)
979 chan -= 4, iooffset += 4;
980 /* set the range.. */
981 outb(range_byte, iobase + iooffset + 0);
982 outb(0, iobase + iooffset + 1);
983
984 /* tell it to begin */
985 command_byte = (chan << 1) | 0x60;
986 outb(command_byte, iobase + iooffset + 2);
987
988 wait_dac_ready(iobase + iooffset);
989
013f230c
DPJ
990 /* low order byte */
991 outb(data[n] & 0xff, iobase + iooffset + 0);
992
993 /* high order byte */
994 outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
995
996 /*
997 * set bit 4 of command byte to indicate
998 * data is loaded and trigger conversion
999 */
1000 command_byte = 0x70 | (chan << 1);
6baef150
CC
1001 /* trigger converion */
1002 outb(command_byte, iobase + iooffset + 2);
1003
1004 wait_dac_ready(iobase + iooffset);
1005
013f230c
DPJ
1006 /* save to shadow register for ao_rinsn */
1007 subpriv->ao.shadow_samples[chan] = data[n];
6baef150
CC
1008 }
1009 }
1010 return n;
1011}
1012
b2bb98e1
HS
1013static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1014{
57bbeb3b 1015 const struct pcmmio_board *board = comedi_board(dev);
b2bb98e1
HS
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];
8b6c5694 1021 int ret;
b2bb98e1
HS
1022
1023 iobase = it->options[0];
1024 irq[0] = it->options[1];
1025
1026 printk(KERN_INFO "comedi%d: %s: io: %lx attaching...\n", dev->minor,
2d2111ea 1027 dev->driver->driver_name, iobase);
b2bb98e1
HS
1028
1029 dev->iobase = iobase;
1030
1031 if (!iobase || !request_region(iobase,
57bbeb3b 1032 board->total_iosize,
2d2111ea 1033 dev->driver->driver_name)) {
b2bb98e1
HS
1034 printk(KERN_ERR "comedi%d: I/O port conflict\n", dev->minor);
1035 return -EIO;
1036 }
1037
57bbeb3b 1038 dev->board_name = board->name;
b2bb98e1 1039
6baef150 1040/*
b2bb98e1
HS
1041 * Allocate the private structure area. alloc_private() is a
1042 * convenient macro defined in comedidev.h.
6baef150 1043 */
b2bb98e1
HS
1044 if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
1045 printk(KERN_ERR "comedi%d: cannot allocate private data structure\n",
1046 dev->minor);
1047 return -ENOMEM;
1048 }
1049
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;
1054 /*
1055 * this gets actually set at the end of this function when we
1056 * request_irqs
1057 */
1058 devpriv->asics[asic].irq = 0;
1059 spin_lock_init(&devpriv->asics[asic].spinlock);
1060 }
1061
57bbeb3b 1062 chans_left = CHANS_PER_ASIC * board->dio_num_asics;
b2bb98e1
HS
1063 n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
1064 n_subdevs = n_dio_subdevs + 2;
1065 devpriv->sprivs =
1066 kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
1067 GFP_KERNEL);
1068 if (!devpriv->sprivs) {
1069 printk(KERN_ERR "comedi%d: cannot allocate subdevice private data structures\n",
1070 dev->minor);
1071 return -ENOMEM;
1072 }
eea6838b 1073
8b6c5694
HS
1074 ret = comedi_alloc_subdevices(dev, n_subdevs);
1075 if (ret)
1076 return ret;
b2bb98e1
HS
1077
1078 /* First, AI */
1079 sdev_no = 0;
1080 s = dev->subdevices + sdev_no;
1081 s->private = devpriv->sprivs + sdev_no;
57bbeb3b
HS
1082 s->maxdata = (1 << board->ai_bits) - 1;
1083 s->range_table = board->ai_range_table;
b2bb98e1
HS
1084 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
1085 s->type = COMEDI_SUBD_AI;
57bbeb3b 1086 s->n_chan = board->n_ai_chans;
b2bb98e1 1087 s->len_chanlist = s->n_chan;
57bbeb3b 1088 s->insn_read = board->ai_rinsn;
b2bb98e1
HS
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);
1093
1094 /* Next, AO */
1095 ++sdev_no;
1096 s = dev->subdevices + sdev_no;
1097 s->private = devpriv->sprivs + sdev_no;
57bbeb3b
HS
1098 s->maxdata = (1 << board->ao_bits) - 1;
1099 s->range_table = board->ao_range_table;
b2bb98e1
HS
1100 s->subdev_flags = SDF_READABLE;
1101 s->type = COMEDI_SUBD_AO;
57bbeb3b 1102 s->n_chan = board->n_ao_chans;
b2bb98e1 1103 s->len_chanlist = s->n_chan;
57bbeb3b
HS
1104 s->insn_read = board->ao_rinsn;
1105 s->insn_write = board->ao_winsn;
b2bb98e1
HS
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);
1110
1111 ++sdev_no;
1112 port = 0;
1113 asic = 0;
1114 for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
1115 int byte_no;
1116
1117 s = dev->subdevices + sdev_no;
1118 s->private = devpriv->sprivs + sdev_no;
1119 s->maxdata = 1;
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;
1132
1133 /* save the ioport address for each 'port' of 8 channels in the
1134 subdevice */
1135 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
1136 if (port >= PORTS_PER_ASIC) {
1137 port = 0;
1138 ++asic;
1139 thisasic_chanct = 0;
1140 }
1141 subpriv->iobases[byte_no] =
1142 devpriv->asics[asic].iobase + port;
1143
1144 if (thisasic_chanct <
1145 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
1146 && subpriv->dio.intr.asic < 0) {
1147 /*
1148 * this is an interrupt subdevice,
1149 * so setup the struct
1150 */
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;
1161 s->len_chanlist =
1162 subpriv->dio.intr.num_asic_chans;
1163 }
1164 thisasic_chanct += CHANS_PER_PORT;
1165 }
1166 spin_lock_init(&subpriv->dio.intr.spinlock);
1167
1168 chans_left -= s->n_chan;
1169
1170 if (!chans_left) {
1171 /*
1172 * reset the asic to our first asic,
1173 * to do intr subdevs
1174 */
1175 asic = 0;
1176 port = 0;
1177 }
1178
1179 }
1180
1181 init_asics(dev); /* clear out all the registers, basically */
1182
1183 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
1184 if (irq[asic]
1185 && request_irq(irq[asic], interrupt_pcmmio,
57bbeb3b 1186 IRQF_SHARED, board->name, dev)) {
b2bb98e1
HS
1187 int i;
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;
1192 }
1193 irq[asic] = 0;
1194 }
1195 devpriv->asics[asic].irq = irq[asic];
1196 }
1197
1198 dev->irq = irq[0]; /*
1199 * grr.. wish comedi dev struct supported
1200 * multiple irqs..
1201 */
1202
1203 if (irq[0]) {
1204 printk(KERN_DEBUG "comedi%d: irq: %u\n", dev->minor, irq[0]);
57bbeb3b 1205 if (board->dio_num_asics == 2 && irq[1])
b2bb98e1
HS
1206 printk(KERN_DEBUG "comedi%d: second ASIC irq: %u\n",
1207 dev->minor, irq[1]);
1208 } else {
1209 printk(KERN_INFO "comedi%d: (IRQ mode disabled)\n", dev->minor);
1210 }
1211
1212 printk(KERN_INFO "comedi%d: attached\n", dev->minor);
1213
1214 return 1;
1215}
1216
484ecc95 1217static void pcmmio_detach(struct comedi_device *dev)
b2bb98e1 1218{
57bbeb3b 1219 const struct pcmmio_board *board = comedi_board(dev);
b2bb98e1
HS
1220 int i;
1221
b2bb98e1 1222 if (dev->iobase)
57bbeb3b 1223 release_region(dev->iobase, board->total_iosize);
b2bb98e1
HS
1224 for (i = 0; i < MAX_ASICS; ++i) {
1225 if (devpriv && devpriv->asics[i].irq)
1226 free_irq(devpriv->asics[i].irq, dev);
1227 }
b2bb98e1
HS
1228 if (devpriv && devpriv->sprivs)
1229 kfree(devpriv->sprivs);
b2bb98e1
HS
1230}
1231
1232static const struct pcmmio_board pcmmio_boards[] = {
1233 {
1234 .name = "pcmmio",
1235 .dio_num_asics = 1,
1236 .dio_num_ports = 6,
1237 .total_iosize = 32,
1238 .ai_bits = 16,
1239 .ao_bits = 16,
1240 .n_ai_chans = 16,
1241 .n_ao_chans = 8,
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
1247 },
1248};
1249
294f930d 1250static struct comedi_driver pcmmio_driver = {
b2bb98e1
HS
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),
1258};
294f930d 1259module_comedi_driver(pcmmio_driver);
90f703d3
AT
1260
1261MODULE_AUTHOR("Comedi http://www.comedi.org");
1262MODULE_DESCRIPTION("Comedi low-level driver");
1263MODULE_LICENSE("GPL");