staging: comedi: adl_pci9111: remove INTERRUPT_DEBUG code
[linux-2.6-block.git] / drivers / staging / comedi / drivers / adl_pci9111.c
CommitLineData
8cb9b9fb
EP
1/*
2
842ec6ba 3comedi/drivers/adl_pci9111.c
8cb9b9fb 4
842ec6ba 5Hardware driver for PCI9111 ADLink cards:
8cb9b9fb 6
842ec6ba 7PCI-9111HR
8cb9b9fb 8
842ec6ba 9Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
8cb9b9fb 10
842ec6ba
MD
11This program is free software; you can redistribute it and/or modify
12it under the terms of the GNU General Public License as published by
13the Free Software Foundation; either version 2 of the License, or
14(at your option) any later version.
8cb9b9fb 15
842ec6ba
MD
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19GNU General Public License for more details.
8cb9b9fb 20
842ec6ba
MD
21You should have received a copy of the GNU General Public License
22along with this program; if not, write to the Free Software
23Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
8cb9b9fb
EP
24*/
25
26/*
27Driver: adl_pci9111
28Description: Adlink PCI-9111HR
29Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
30Devices: [ADLink] PCI-9111HR (adl_pci9111)
31Status: experimental
32
33Supports:
34
842ec6ba
MD
35 - ai_insn read
36 - ao_insn read/write
37 - di_insn read
38 - do_insn read/write
39 - ai_do_cmd mode with the following sources:
8cb9b9fb 40
842ec6ba
MD
41 - start_src TRIG_NOW
42 - scan_begin_src TRIG_FOLLOW TRIG_TIMER TRIG_EXT
43 - convert_src TRIG_TIMER TRIG_EXT
44 - scan_end_src TRIG_COUNT
45 - stop_src TRIG_COUNT TRIG_NONE
8cb9b9fb 46
842ec6ba
MD
47The scanned channels must be consecutive and start from 0. They must
48all have the same range and aref.
8cb9b9fb 49
3e5a0ba0 50Configuration options: not applicable, uses PCI auto config
8cb9b9fb
EP
51*/
52
53/*
54CHANGELOG:
55
842ec6ba
MD
562005/02/17 Extend AI streaming capabilities. Now, scan_begin_arg can be
57a multiple of chanlist_len*convert_arg.
582002/02/19 Fixed the two's complement conversion in pci9111_(hr_)ai_get_data.
592002/02/18 Added external trigger support for analog input.
8cb9b9fb
EP
60
61TODO:
62
842ec6ba
MD
63 - Really test implemented functionality.
64 - Add support for the PCI-9111DG with a probe routine to identify
65 the card type (perhaps with the help of the channel number readback
66 of the A/D Data register).
67 - Add external multiplexer support.
8cb9b9fb
EP
68
69*/
70
71#include "../comedidev.h"
72
73#include <linux/delay.h>
70265d24 74#include <linux/interrupt.h>
8cb9b9fb
EP
75
76#include "8253.h"
8cb9b9fb
EP
77#include "comedi_fc.h"
78
2f6df34c
MR
79#define PCI9111_DRIVER_NAME "adl_pci9111"
80#define PCI9111_HR_DEVICE_ID 0x9111
8cb9b9fb 81
8cb9b9fb
EP
82#define PCI9111_FIFO_HALF_SIZE 512
83
8cb9b9fb 84#define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000
8cb9b9fb
EP
85
86#define PCI9111_RANGE_SETTING_DELAY 10
87#define PCI9111_AI_INSTANT_READ_UDELAY_US 2
88#define PCI9111_AI_INSTANT_READ_TIMEOUT 100
89
90#define PCI9111_8254_CLOCK_PERIOD_NS 500
91
8cb9b9fb
EP
92/* IO address map */
93
b5d8d119 94#define PCI9111_AI_FIFO_REG 0x00
dc79022a 95#define PCI9111_AO_REG 0x00
101e490a 96#define PCI9111_DIO_REG 0x02
2f6df34c
MR
97#define PCI9111_REGISTER_EXTENDED_IO_PORTS 0x04
98#define PCI9111_REGISTER_AD_CHANNEL_CONTROL 0x06 /* Channel
99 selection */
100#define PCI9111_REGISTER_AD_CHANNEL_READBACK 0x06
101#define PCI9111_REGISTER_INPUT_SIGNAL_RANGE 0x08
102#define PCI9111_REGISTER_RANGE_STATUS_READBACK 0x08
103#define PCI9111_REGISTER_TRIGGER_MODE_CONTROL 0x0A
104#define PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK 0x0A
105#define PCI9111_REGISTER_SOFTWARE_TRIGGER 0x0E
106#define PCI9111_REGISTER_INTERRUPT_CONTROL 0x0C
9d093151 107#define PCI9111_8254_BASE_REG 0x40
2f6df34c 108#define PCI9111_REGISTER_INTERRUPT_CLEAR 0x48
8cb9b9fb 109
2f6df34c
MR
110#define PCI9111_TRIGGER_MASK 0x0F
111#define PCI9111_PTRG_OFF (0 << 3)
112#define PCI9111_PTRG_ON (1 << 3)
8cb9b9fb
EP
113#define PCI9111_EITS_EXTERNAL (1 << 2)
114#define PCI9111_EITS_INTERNAL (0 << 2)
115#define PCI9111_TPST_SOFTWARE_TRIGGER (0 << 1)
116#define PCI9111_TPST_TIMER_PACER (1 << 1)
117#define PCI9111_ASCAN_ON (1 << 0)
118#define PCI9111_ASCAN_OFF (0 << 0)
119
120#define PCI9111_ISC0_SET_IRQ_ON_ENDING_OF_AD_CONVERSION (0 << 0)
121#define PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL (1 << 0)
2f6df34c
MR
122#define PCI9111_ISC1_SET_IRQ_ON_TIMER_TICK (0 << 1)
123#define PCI9111_ISC1_SET_IRQ_ON_EXT_TRG (1 << 1)
124#define PCI9111_FFEN_SET_FIFO_ENABLE (0 << 2)
8cb9b9fb
EP
125#define PCI9111_FFEN_SET_FIFO_DISABLE (1 << 2)
126
127#define PCI9111_CHANNEL_MASK 0x0F
128
129#define PCI9111_RANGE_MASK 0x07
130#define PCI9111_FIFO_EMPTY_MASK 0x10
131#define PCI9111_FIFO_HALF_FULL_MASK 0x20
132#define PCI9111_FIFO_FULL_MASK 0x40
133#define PCI9111_AD_BUSY_MASK 0x80
134
8cb9b9fb
EP
135/*
136 * Define inlined function
137 */
138
139#define pci9111_trigger_and_autoscan_get() \
886edb0c 140 (inb(dev->iobase + PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK)&0x0F)
8cb9b9fb
EP
141
142#define pci9111_trigger_and_autoscan_set(flags) \
886edb0c 143 outb(flags, dev->iobase + PCI9111_REGISTER_TRIGGER_MODE_CONTROL)
8cb9b9fb 144
842ec6ba 145#define pci9111_interrupt_and_fifo_get() \
886edb0c 146 ((inb(dev->iobase + PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK) \
842ec6ba 147 >> 4) & 0x03)
8cb9b9fb
EP
148
149#define pci9111_interrupt_and_fifo_set(flags) \
886edb0c 150 outb(flags, dev->iobase + PCI9111_REGISTER_INTERRUPT_CONTROL)
8cb9b9fb
EP
151
152#define pci9111_interrupt_clear() \
886edb0c 153 outb(0, dev->iobase + PCI9111_REGISTER_INTERRUPT_CLEAR)
8cb9b9fb
EP
154
155#define pci9111_software_trigger() \
886edb0c 156 outb(0, dev->iobase + PCI9111_REGISTER_SOFTWARE_TRIGGER)
8cb9b9fb 157
842ec6ba
MD
158#define pci9111_fifo_reset() do { \
159 outb(PCI9111_FFEN_SET_FIFO_ENABLE, \
886edb0c 160 dev->iobase + PCI9111_REGISTER_INTERRUPT_CONTROL); \
842ec6ba 161 outb(PCI9111_FFEN_SET_FIFO_DISABLE, \
886edb0c 162 dev->iobase + PCI9111_REGISTER_INTERRUPT_CONTROL); \
842ec6ba 163 outb(PCI9111_FFEN_SET_FIFO_ENABLE, \
886edb0c 164 dev->iobase + PCI9111_REGISTER_INTERRUPT_CONTROL); \
842ec6ba 165 } while (0)
8cb9b9fb
EP
166
167#define pci9111_is_fifo_full() \
886edb0c 168 ((inb(dev->iobase + PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
842ec6ba 169 PCI9111_FIFO_FULL_MASK) == 0)
8cb9b9fb
EP
170
171#define pci9111_is_fifo_half_full() \
886edb0c 172 ((inb(dev->iobase + PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
842ec6ba 173 PCI9111_FIFO_HALF_FULL_MASK) == 0)
8cb9b9fb
EP
174
175#define pci9111_is_fifo_empty() \
886edb0c 176 ((inb(dev->iobase + PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
842ec6ba 177 PCI9111_FIFO_EMPTY_MASK) == 0)
8cb9b9fb 178
842ec6ba
MD
179#define pci9111_ai_channel_set(channel) \
180 outb((channel)&PCI9111_CHANNEL_MASK, \
886edb0c 181 dev->iobase + PCI9111_REGISTER_AD_CHANNEL_CONTROL)
8cb9b9fb 182
842ec6ba 183#define pci9111_ai_channel_get() \
886edb0c 184 (inb(dev->iobase + PCI9111_REGISTER_AD_CHANNEL_READBACK) \
842ec6ba 185 &PCI9111_CHANNEL_MASK)
8cb9b9fb 186
842ec6ba
MD
187#define pci9111_ai_range_set(range) \
188 outb((range)&PCI9111_RANGE_MASK, \
886edb0c 189 dev->iobase + PCI9111_REGISTER_INPUT_SIGNAL_RANGE)
8cb9b9fb 190
842ec6ba 191#define pci9111_ai_range_get() \
886edb0c 192 (inb(dev->iobase + PCI9111_REGISTER_RANGE_STATUS_READBACK) \
842ec6ba 193 &PCI9111_RANGE_MASK)
8cb9b9fb 194
9ced1de6 195static const struct comedi_lrange pci9111_hr_ai_range = {
8cb9b9fb
EP
196 5,
197 {
0a85b6f0
MT
198 BIP_RANGE(10),
199 BIP_RANGE(5),
200 BIP_RANGE(2.5),
201 BIP_RANGE(1.25),
202 BIP_RANGE(0.625)
203 }
8cb9b9fb
EP
204};
205
52f8ac98 206/* Private data structure */
8cb9b9fb 207
c350fa19 208struct pci9111_private_data {
7ecac4c3
M
209 unsigned long lcr_io_base; /* Local configuration register base
210 * address */
8cb9b9fb
EP
211
212 int stop_counter;
213 int stop_is_none;
214
215 unsigned int scan_delay;
216 unsigned int chanlist_len;
217 unsigned int chunk_counter;
218 unsigned int chunk_num_samples;
219
52f8ac98 220 int ao_readback; /* Last written analog output data */
8cb9b9fb 221
7ecac4c3
M
222 unsigned int timer_divisor_1; /* Divisor values for the 8254 timer
223 * pacer */
525d1b13 224 unsigned int timer_divisor_2;
8cb9b9fb 225
52f8ac98 226 int is_valid; /* Is device valid */
8cb9b9fb 227
790c5541 228 short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
c350fa19 229};
8cb9b9fb 230
52f8ac98
BP
231/* ------------------------------------------------------------------ */
232/* PLX9050 SECTION */
233/* ------------------------------------------------------------------ */
8cb9b9fb
EP
234
235#define PLX9050_REGISTER_INTERRUPT_CONTROL 0x4c
236
237#define PLX9050_LINTI1_ENABLE (1 << 0)
238#define PLX9050_LINTI1_ACTIVE_HIGH (1 << 1)
239#define PLX9050_LINTI1_STATUS (1 << 2)
240#define PLX9050_LINTI2_ENABLE (1 << 3)
241#define PLX9050_LINTI2_ACTIVE_HIGH (1 << 4)
242#define PLX9050_LINTI2_STATUS (1 << 5)
243#define PLX9050_PCI_INTERRUPT_ENABLE (1 << 6)
244#define PLX9050_SOFTWARE_INTERRUPT (1 << 7)
245
246static void plx9050_interrupt_control(unsigned long io_base,
0a85b6f0
MT
247 bool LINTi1_enable,
248 bool LINTi1_active_high,
249 bool LINTi2_enable,
250 bool LINTi2_active_high,
251 bool interrupt_enable)
8cb9b9fb
EP
252{
253 int flags = 0;
254
255 if (LINTi1_enable)
256 flags |= PLX9050_LINTI1_ENABLE;
257 if (LINTi1_active_high)
258 flags |= PLX9050_LINTI1_ACTIVE_HIGH;
259 if (LINTi2_enable)
260 flags |= PLX9050_LINTI2_ENABLE;
261 if (LINTi2_active_high)
262 flags |= PLX9050_LINTI2_ACTIVE_HIGH;
263
264 if (interrupt_enable)
265 flags |= PLX9050_PCI_INTERRUPT_ENABLE;
266
267 outb(flags, io_base + PLX9050_REGISTER_INTERRUPT_CONTROL);
268}
269
52f8ac98
BP
270/* ------------------------------------------------------------------ */
271/* MISCELLANEOUS SECTION */
272/* ------------------------------------------------------------------ */
8cb9b9fb 273
52f8ac98 274/* 8254 timer */
8cb9b9fb 275
da91b269 276static void pci9111_timer_set(struct comedi_device *dev)
8cb9b9fb 277{
98943079 278 struct pci9111_private_data *dev_private = dev->private;
9d093151 279 unsigned long timer_base = dev->iobase + PCI9111_8254_BASE_REG;
98943079 280
9d093151
HS
281 i8254_set_mode(timer_base, 1, 0, I8254_MODE0 | I8254_BINARY);
282 i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
283 i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
8cb9b9fb 284
5f74ea14 285 udelay(1);
8cb9b9fb 286
9d093151
HS
287 i8254_write(timer_base, 1, 2, dev_private->timer_divisor_2);
288 i8254_write(timer_base, 1, 1, dev_private->timer_divisor_1);
8cb9b9fb
EP
289}
290
655f78f6 291enum pci9111_trigger_sources {
8cb9b9fb
EP
292 software,
293 timer_pacer,
294 external
655f78f6 295};
8cb9b9fb 296
da91b269 297static void pci9111_trigger_source_set(struct comedi_device *dev,
0a85b6f0 298 enum pci9111_trigger_sources source)
8cb9b9fb
EP
299{
300 int flags;
301
302 flags = pci9111_trigger_and_autoscan_get() & 0x09;
303
304 switch (source) {
305 case software:
306 flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_SOFTWARE_TRIGGER;
307 break;
308
309 case timer_pacer:
310 flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_TIMER_PACER;
311 break;
312
313 case external:
314 flags |= PCI9111_EITS_EXTERNAL;
315 break;
316 }
317
318 pci9111_trigger_and_autoscan_set(flags);
319}
320
da91b269 321static void pci9111_pretrigger_set(struct comedi_device *dev, bool pretrigger)
8cb9b9fb
EP
322{
323 int flags;
324
325 flags = pci9111_trigger_and_autoscan_get() & 0x07;
326
327 if (pretrigger)
328 flags |= PCI9111_PTRG_ON;
329
330 pci9111_trigger_and_autoscan_set(flags);
331}
332
da91b269 333static void pci9111_autoscan_set(struct comedi_device *dev, bool autoscan)
8cb9b9fb
EP
334{
335 int flags;
336
337 flags = pci9111_trigger_and_autoscan_get() & 0x0e;
338
339 if (autoscan)
340 flags |= PCI9111_ASCAN_ON;
341
342 pci9111_trigger_and_autoscan_set(flags);
343}
344
3ba97b3c 345enum pci9111_ISC0_sources {
8cb9b9fb
EP
346 irq_on_eoc,
347 irq_on_fifo_half_full
3ba97b3c 348};
8cb9b9fb 349
52f8ac98 350enum pci9111_ISC1_sources {
8cb9b9fb
EP
351 irq_on_timer_tick,
352 irq_on_external_trigger
52f8ac98 353};
8cb9b9fb 354
da91b269 355static void pci9111_interrupt_source_set(struct comedi_device *dev,
0a85b6f0
MT
356 enum pci9111_ISC0_sources irq_0_source,
357 enum pci9111_ISC1_sources irq_1_source)
8cb9b9fb
EP
358{
359 int flags;
360
361 flags = pci9111_interrupt_and_fifo_get() & 0x04;
362
363 if (irq_0_source == irq_on_fifo_half_full)
364 flags |= PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL;
365
366 if (irq_1_source == irq_on_external_trigger)
367 flags |= PCI9111_ISC1_SET_IRQ_ON_EXT_TRG;
368
369 pci9111_interrupt_and_fifo_set(flags);
370}
371
52f8ac98
BP
372/* ------------------------------------------------------------------ */
373/* HARDWARE TRIGGERED ANALOG INPUT SECTION */
374/* ------------------------------------------------------------------ */
8cb9b9fb 375
52f8ac98 376/* Cancel analog input autoscan */
8cb9b9fb 377
0a85b6f0
MT
378static int pci9111_ai_cancel(struct comedi_device *dev,
379 struct comedi_subdevice *s)
8cb9b9fb 380{
98943079
HS
381 struct pci9111_private_data *dev_private = dev->private;
382
52f8ac98 383 /* Disable interrupts */
8cb9b9fb
EP
384
385 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
0a85b6f0 386 true, false);
8cb9b9fb
EP
387
388 pci9111_trigger_source_set(dev, software);
389
390 pci9111_autoscan_set(dev, false);
391
392 pci9111_fifo_reset();
393
8cb9b9fb
EP
394 return 0;
395}
396
52f8ac98 397/* Test analog input command */
8cb9b9fb 398
7ecac4c3
M
399#define pci9111_check_trigger_src(src, flags) do { \
400 tmp = src; \
401 src &= flags; \
402 if (!src || tmp != src) \
403 error++; \
404 } while (false);
8cb9b9fb
EP
405
406static int
da91b269 407pci9111_ai_do_cmd_test(struct comedi_device *dev,
0a85b6f0 408 struct comedi_subdevice *s, struct comedi_cmd *cmd)
8cb9b9fb 409{
98943079 410 struct pci9111_private_data *dev_private = dev->private;
8cb9b9fb
EP
411 int tmp;
412 int error = 0;
413 int range, reference;
414 int i;
8cb9b9fb 415
52f8ac98 416 /* Step 1 : check if trigger are trivialy valid */
8cb9b9fb
EP
417
418 pci9111_check_trigger_src(cmd->start_src, TRIG_NOW);
419 pci9111_check_trigger_src(cmd->scan_begin_src,
0a85b6f0 420 TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
8cb9b9fb
EP
421 pci9111_check_trigger_src(cmd->convert_src, TRIG_TIMER | TRIG_EXT);
422 pci9111_check_trigger_src(cmd->scan_end_src, TRIG_COUNT);
423 pci9111_check_trigger_src(cmd->stop_src, TRIG_COUNT | TRIG_NONE);
424
425 if (error)
426 return 1;
427
7ecac4c3
M
428 /* step 2 : make sure trigger sources are unique and mutually
429 * compatible */
8cb9b9fb
EP
430
431 if (cmd->start_src != TRIG_NOW)
432 error++;
433
434 if ((cmd->scan_begin_src != TRIG_TIMER) &&
0a85b6f0
MT
435 (cmd->scan_begin_src != TRIG_FOLLOW) &&
436 (cmd->scan_begin_src != TRIG_EXT))
8cb9b9fb
EP
437 error++;
438
2306d9b1 439 if ((cmd->convert_src != TRIG_TIMER) && (cmd->convert_src != TRIG_EXT))
8cb9b9fb 440 error++;
8cb9b9fb 441 if ((cmd->convert_src == TRIG_TIMER) &&
0a85b6f0 442 !((cmd->scan_begin_src == TRIG_TIMER) ||
2306d9b1 443 (cmd->scan_begin_src == TRIG_FOLLOW)))
8cb9b9fb 444 error++;
8cb9b9fb 445 if ((cmd->convert_src == TRIG_EXT) &&
0a85b6f0 446 !((cmd->scan_begin_src == TRIG_EXT) ||
2306d9b1 447 (cmd->scan_begin_src == TRIG_FOLLOW)))
8cb9b9fb 448 error++;
2306d9b1 449
8cb9b9fb
EP
450
451 if (cmd->scan_end_src != TRIG_COUNT)
452 error++;
453 if ((cmd->stop_src != TRIG_COUNT) && (cmd->stop_src != TRIG_NONE))
454 error++;
455
456 if (error)
457 return 2;
458
52f8ac98 459 /* Step 3 : make sure arguments are trivialy compatible */
8cb9b9fb 460
8cb9b9fb
EP
461 if ((cmd->start_src == TRIG_NOW) && (cmd->start_arg != 0)) {
462 cmd->start_arg = 0;
463 error++;
464 }
465
466 if ((cmd->convert_src == TRIG_TIMER) &&
02baee8c
HS
467 (cmd->convert_arg < PCI9111_AI_ACQUISITION_PERIOD_MIN_NS)) {
468 cmd->convert_arg = PCI9111_AI_ACQUISITION_PERIOD_MIN_NS;
8cb9b9fb
EP
469 error++;
470 }
471 if ((cmd->convert_src == TRIG_EXT) && (cmd->convert_arg != 0)) {
472 cmd->convert_arg = 0;
473 error++;
474 }
475
476 if ((cmd->scan_begin_src == TRIG_TIMER) &&
02baee8c
HS
477 (cmd->scan_begin_arg < PCI9111_AI_ACQUISITION_PERIOD_MIN_NS)) {
478 cmd->scan_begin_arg = PCI9111_AI_ACQUISITION_PERIOD_MIN_NS;
8cb9b9fb
EP
479 error++;
480 }
7ecac4c3
M
481 if ((cmd->scan_begin_src == TRIG_FOLLOW)
482 && (cmd->scan_begin_arg != 0)) {
8cb9b9fb
EP
483 cmd->scan_begin_arg = 0;
484 error++;
485 }
486 if ((cmd->scan_begin_src == TRIG_EXT) && (cmd->scan_begin_arg != 0)) {
487 cmd->scan_begin_arg = 0;
488 error++;
489 }
490
491 if ((cmd->scan_end_src == TRIG_COUNT) &&
0a85b6f0 492 (cmd->scan_end_arg != cmd->chanlist_len)) {
8cb9b9fb
EP
493 cmd->scan_end_arg = cmd->chanlist_len;
494 error++;
495 }
496
497 if ((cmd->stop_src == TRIG_COUNT) && (cmd->stop_arg < 1)) {
498 cmd->stop_arg = 1;
499 error++;
500 }
501 if ((cmd->stop_src == TRIG_NONE) && (cmd->stop_arg != 0)) {
502 cmd->stop_arg = 0;
503 error++;
504 }
505
506 if (error)
507 return 3;
508
52f8ac98 509 /* Step 4 : fix up any arguments */
8cb9b9fb
EP
510
511 if (cmd->convert_src == TRIG_TIMER) {
512 tmp = cmd->convert_arg;
513 i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS,
0a85b6f0
MT
514 &(dev_private->timer_divisor_1),
515 &(dev_private->timer_divisor_2),
516 &(cmd->convert_arg),
517 cmd->flags & TRIG_ROUND_MASK);
8cb9b9fb
EP
518 if (tmp != cmd->convert_arg)
519 error++;
520 }
52f8ac98
BP
521 /* There's only one timer on this card, so the scan_begin timer must */
522 /* be a multiple of chanlist_len*convert_arg */
8cb9b9fb
EP
523
524 if (cmd->scan_begin_src == TRIG_TIMER) {
525
526 unsigned int scan_begin_min;
527 unsigned int scan_begin_arg;
528 unsigned int scan_factor;
529
530 scan_begin_min = cmd->chanlist_len * cmd->convert_arg;
531
532 if (cmd->scan_begin_arg != scan_begin_min) {
533 if (scan_begin_min < cmd->scan_begin_arg) {
534 scan_factor =
0a85b6f0 535 cmd->scan_begin_arg / scan_begin_min;
8cb9b9fb
EP
536 scan_begin_arg = scan_factor * scan_begin_min;
537 if (cmd->scan_begin_arg != scan_begin_arg) {
538 cmd->scan_begin_arg = scan_begin_arg;
539 error++;
540 }
541 } else {
542 cmd->scan_begin_arg = scan_begin_min;
543 error++;
544 }
545 }
546 }
547
548 if (error)
549 return 4;
550
52f8ac98 551 /* Step 5 : check channel list */
8cb9b9fb
EP
552
553 if (cmd->chanlist) {
554
555 range = CR_RANGE(cmd->chanlist[0]);
556 reference = CR_AREF(cmd->chanlist[0]);
557
558 if (cmd->chanlist_len > 1) {
559 for (i = 0; i < cmd->chanlist_len; i++) {
560 if (CR_CHAN(cmd->chanlist[i]) != i) {
561 comedi_error(dev,
0a85b6f0
MT
562 "entries in chanlist must be consecutive "
563 "channels,counting upwards from 0\n");
8cb9b9fb
EP
564 error++;
565 }
566 if (CR_RANGE(cmd->chanlist[i]) != range) {
567 comedi_error(dev,
0a85b6f0 568 "entries in chanlist must all have the same gain\n");
8cb9b9fb
EP
569 error++;
570 }
571 if (CR_AREF(cmd->chanlist[i]) != reference) {
572 comedi_error(dev,
0a85b6f0 573 "entries in chanlist must all have the same reference\n");
8cb9b9fb
EP
574 error++;
575 }
576 }
8cb9b9fb
EP
577 }
578 }
579
580 if (error)
581 return 5;
582
583 return 0;
584
585}
586
52f8ac98 587/* Analog input command */
8cb9b9fb 588
0a85b6f0 589static int pci9111_ai_do_cmd(struct comedi_device *dev,
d1d7b20d 590 struct comedi_subdevice *s)
8cb9b9fb 591{
98943079 592 struct pci9111_private_data *dev_private = dev->private;
d1d7b20d 593 struct comedi_cmd *async_cmd = &s->async->cmd;
8cb9b9fb
EP
594
595 if (!dev->irq) {
596 comedi_error(dev,
0a85b6f0 597 "no irq assigned for PCI9111, cannot do hardware conversion");
8cb9b9fb
EP
598 return -1;
599 }
52f8ac98
BP
600 /* Set channel scan limit */
601 /* PCI9111 allows only scanning from channel 0 to channel n */
602 /* TODO: handle the case of an external multiplexer */
8cb9b9fb
EP
603
604 if (async_cmd->chanlist_len > 1) {
605 pci9111_ai_channel_set((async_cmd->chanlist_len) - 1);
606 pci9111_autoscan_set(dev, true);
607 } else {
608 pci9111_ai_channel_set(CR_CHAN(async_cmd->chanlist[0]));
609 pci9111_autoscan_set(dev, false);
610 }
611
52f8ac98
BP
612 /* Set gain */
613 /* This is the same gain on every channel */
8cb9b9fb
EP
614
615 pci9111_ai_range_set(CR_RANGE(async_cmd->chanlist[0]));
616
617 /* Set counter */
618
619 switch (async_cmd->stop_src) {
620 case TRIG_COUNT:
621 dev_private->stop_counter =
0a85b6f0 622 async_cmd->stop_arg * async_cmd->chanlist_len;
8cb9b9fb
EP
623 dev_private->stop_is_none = 0;
624 break;
625
626 case TRIG_NONE:
627 dev_private->stop_counter = 0;
628 dev_private->stop_is_none = 1;
629 break;
630
631 default:
632 comedi_error(dev, "Invalid stop trigger");
633 return -1;
634 }
635
52f8ac98 636 /* Set timer pacer */
8cb9b9fb
EP
637
638 dev_private->scan_delay = 0;
639 switch (async_cmd->convert_src) {
640 case TRIG_TIMER:
641 i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS,
0a85b6f0
MT
642 &(dev_private->timer_divisor_1),
643 &(dev_private->timer_divisor_2),
644 &(async_cmd->convert_arg),
645 async_cmd->
646 flags & TRIG_ROUND_MASK);
8cb9b9fb
EP
647
648 pci9111_trigger_source_set(dev, software);
649 pci9111_timer_set(dev);
650 pci9111_fifo_reset();
651 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
0a85b6f0 652 irq_on_timer_tick);
8cb9b9fb
EP
653 pci9111_trigger_source_set(dev, timer_pacer);
654 plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
0a85b6f0 655 false, true, true);
8cb9b9fb 656
6c2fd308
IA
657 if (async_cmd->scan_begin_src == TRIG_TIMER) {
658 dev_private->scan_delay =
659 (async_cmd->scan_begin_arg /
660 (async_cmd->convert_arg *
661 async_cmd->chanlist_len)) - 1;
662 }
8cb9b9fb
EP
663
664 break;
665
666 case TRIG_EXT:
667
668 pci9111_trigger_source_set(dev, external);
669 pci9111_fifo_reset();
670 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
0a85b6f0 671 irq_on_timer_tick);
8cb9b9fb 672 plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
0a85b6f0 673 false, true, true);
8cb9b9fb
EP
674
675 break;
676
677 default:
678 comedi_error(dev, "Invalid convert trigger");
679 return -1;
680 }
681
682 dev_private->stop_counter *= (1 + dev_private->scan_delay);
683 dev_private->chanlist_len = async_cmd->chanlist_len;
684 dev_private->chunk_counter = 0;
685 dev_private->chunk_num_samples =
0a85b6f0 686 dev_private->chanlist_len * (1 + dev_private->scan_delay);
8cb9b9fb 687
8cb9b9fb
EP
688 return 0;
689}
690
0a85b6f0
MT
691static void pci9111_ai_munge(struct comedi_device *dev,
692 struct comedi_subdevice *s, void *data,
693 unsigned int num_bytes,
694 unsigned int start_chan_index)
8cb9b9fb 695{
790c5541 696 short *array = data;
af031edf
HS
697 unsigned int maxdata = s->maxdata;
698 unsigned int invert = (maxdata + 1) >> 1;
699 unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
700 unsigned int num_samples = num_bytes / sizeof(short);
701 unsigned int i;
702
703 for (i = 0; i < num_samples; i++)
704 array[i] = ((array[i] >> shift) & maxdata) ^ invert;
8cb9b9fb
EP
705}
706
52f8ac98
BP
707/* ------------------------------------------------------------------ */
708/* INTERRUPT SECTION */
709/* ------------------------------------------------------------------ */
8cb9b9fb 710
70265d24 711static irqreturn_t pci9111_interrupt(int irq, void *p_device)
8cb9b9fb 712{
71b5f4f1 713 struct comedi_device *dev = p_device;
98943079 714 struct pci9111_private_data *dev_private = dev->private;
d1d7b20d 715 struct comedi_subdevice *s = dev->read_subdev;
d163679c 716 struct comedi_async *async;
8cb9b9fb
EP
717 unsigned long irq_flags;
718 unsigned char intcsr;
719
720 if (!dev->attached) {
52f8ac98
BP
721 /* Ignore interrupt before device fully attached. */
722 /* Might not even have allocated subdevices yet! */
8cb9b9fb
EP
723 return IRQ_NONE;
724 }
725
d1d7b20d 726 async = s->async;
8cb9b9fb 727
5f74ea14 728 spin_lock_irqsave(&dev->spinlock, irq_flags);
8cb9b9fb 729
52f8ac98 730 /* Check if we are source of interrupt */
8cb9b9fb 731 intcsr = inb(dev_private->lcr_io_base +
0a85b6f0 732 PLX9050_REGISTER_INTERRUPT_CONTROL);
8cb9b9fb 733 if (!(((intcsr & PLX9050_PCI_INTERRUPT_ENABLE) != 0)
0a85b6f0
MT
734 && (((intcsr & (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS))
735 == (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS))
736 || ((intcsr & (PLX9050_LINTI2_ENABLE | PLX9050_LINTI2_STATUS))
737 == (PLX9050_LINTI2_ENABLE | PLX9050_LINTI2_STATUS))))) {
52f8ac98
BP
738 /* Not the source of the interrupt. */
739 /* (N.B. not using PLX9050_SOFTWARE_INTERRUPT) */
5f74ea14 740 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
8cb9b9fb
EP
741 return IRQ_NONE;
742 }
743
744 if ((intcsr & (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) ==
0a85b6f0 745 (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) {
52f8ac98 746 /* Interrupt comes from fifo_half-full signal */
8cb9b9fb
EP
747
748 if (pci9111_is_fifo_full()) {
0a85b6f0 749 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
8cb9b9fb
EP
750 comedi_error(dev, PCI9111_DRIVER_NAME " fifo overflow");
751 pci9111_interrupt_clear();
d1d7b20d 752 pci9111_ai_cancel(dev, s);
8cb9b9fb 753 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
d1d7b20d 754 comedi_event(dev, s);
8cb9b9fb
EP
755
756 return IRQ_HANDLED;
757 }
758
759 if (pci9111_is_fifo_half_full()) {
760 unsigned int num_samples;
761 unsigned int bytes_written = 0;
762
8cb9b9fb 763 num_samples =
0a85b6f0
MT
764 PCI9111_FIFO_HALF_SIZE >
765 dev_private->stop_counter
766 && !dev_private->
767 stop_is_none ? dev_private->stop_counter :
768 PCI9111_FIFO_HALF_SIZE;
b5d8d119 769 insw(dev->iobase + PCI9111_AI_FIFO_REG,
0a85b6f0 770 dev_private->ai_bounce_buffer, num_samples);
8cb9b9fb
EP
771
772 if (dev_private->scan_delay < 1) {
773 bytes_written =
d1d7b20d 774 cfc_write_array_to_buffer(s,
0a85b6f0
MT
775 dev_private->
776 ai_bounce_buffer,
777 num_samples *
778 sizeof(short));
8cb9b9fb
EP
779 } else {
780 int position = 0;
781 int to_read;
782
783 while (position < num_samples) {
784 if (dev_private->chunk_counter <
0a85b6f0 785 dev_private->chanlist_len) {
8cb9b9fb 786 to_read =
0a85b6f0
MT
787 dev_private->chanlist_len -
788 dev_private->chunk_counter;
8cb9b9fb
EP
789
790 if (to_read >
0a85b6f0 791 num_samples - position)
8cb9b9fb 792 to_read =
0a85b6f0
MT
793 num_samples -
794 position;
8cb9b9fb
EP
795
796 bytes_written +=
0a85b6f0 797 cfc_write_array_to_buffer
d1d7b20d 798 (s,
0a85b6f0
MT
799 dev_private->ai_bounce_buffer
800 + position,
801 to_read * sizeof(short));
8cb9b9fb
EP
802 } else {
803 to_read =
0a85b6f0
MT
804 dev_private->chunk_num_samples
805 -
806 dev_private->chunk_counter;
8cb9b9fb 807 if (to_read >
0a85b6f0 808 num_samples - position)
8cb9b9fb 809 to_read =
0a85b6f0
MT
810 num_samples -
811 position;
8cb9b9fb
EP
812
813 bytes_written +=
0a85b6f0 814 sizeof(short) * to_read;
8cb9b9fb
EP
815 }
816
817 position += to_read;
818 dev_private->chunk_counter += to_read;
819
820 if (dev_private->chunk_counter >=
0a85b6f0 821 dev_private->chunk_num_samples)
8cb9b9fb
EP
822 dev_private->chunk_counter = 0;
823 }
824 }
825
826 dev_private->stop_counter -=
0a85b6f0 827 bytes_written / sizeof(short);
8cb9b9fb
EP
828 }
829 }
830
831 if ((dev_private->stop_counter == 0) && (!dev_private->stop_is_none)) {
832 async->events |= COMEDI_CB_EOA;
d1d7b20d 833 pci9111_ai_cancel(dev, s);
8cb9b9fb
EP
834 }
835
836 /* Very important, otherwise another interrupt request will be inserted
837 * and will cause driver hangs on processing interrupt event. */
838
839 pci9111_interrupt_clear();
840
5f74ea14 841 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
8cb9b9fb 842
d1d7b20d 843 comedi_event(dev, s);
8cb9b9fb
EP
844
845 return IRQ_HANDLED;
846}
847
52f8ac98
BP
848/* ------------------------------------------------------------------ */
849/* INSTANT ANALOG INPUT OUTPUT SECTION */
850/* ------------------------------------------------------------------ */
8cb9b9fb 851
52f8ac98 852/* analog instant input */
8cb9b9fb 853
da91b269 854static int pci9111_ai_insn_read(struct comedi_device *dev,
d1d7b20d 855 struct comedi_subdevice *s,
0a85b6f0 856 struct comedi_insn *insn, unsigned int *data)
8cb9b9fb 857{
2f002cc9
HS
858 unsigned int maxdata = s->maxdata;
859 unsigned int invert = (maxdata + 1) >> 1;
860 unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
861 int timeout;
862 int i;
8cb9b9fb 863
8cb9b9fb
EP
864 pci9111_ai_channel_set(CR_CHAN((&insn->chanspec)[0]));
865
2306d9b1 866 if ((pci9111_ai_range_get()) != CR_RANGE((&insn->chanspec)[0]))
8cb9b9fb 867 pci9111_ai_range_set(CR_RANGE((&insn->chanspec)[0]));
8cb9b9fb
EP
868
869 pci9111_fifo_reset();
870
871 for (i = 0; i < insn->n; i++) {
872 pci9111_software_trigger();
873
874 timeout = PCI9111_AI_INSTANT_READ_TIMEOUT;
875
876 while (timeout--) {
877 if (!pci9111_is_fifo_empty())
878 goto conversion_done;
879 }
880
881 comedi_error(dev, "A/D read timeout");
882 data[i] = 0;
883 pci9111_fifo_reset();
884 return -ETIME;
885
0a85b6f0 886conversion_done:
8cb9b9fb 887
2f002cc9
HS
888 data[i] = inw(dev->iobase + PCI9111_AI_FIFO_REG);
889 data[i] = ((data[i] >> shift) & maxdata) ^ invert;
8cb9b9fb
EP
890 }
891
8cb9b9fb
EP
892 return i;
893}
894
2084fd19
HS
895static int pci9111_ao_insn_write(struct comedi_device *dev,
896 struct comedi_subdevice *s,
897 struct comedi_insn *insn,
898 unsigned int *data)
8cb9b9fb 899{
98943079 900 struct pci9111_private_data *dev_private = dev->private;
2084fd19 901 unsigned int val = 0;
8cb9b9fb
EP
902 int i;
903
904 for (i = 0; i < insn->n; i++) {
2084fd19
HS
905 val = data[i];
906 outw(val, dev->iobase + PCI9111_AO_REG);
8cb9b9fb 907 }
2084fd19 908 dev_private->ao_readback = val;
8cb9b9fb 909
2084fd19 910 return insn->n;
8cb9b9fb
EP
911}
912
da91b269 913static int pci9111_ao_insn_read(struct comedi_device *dev,
0a85b6f0 914 struct comedi_subdevice *s,
b3450faf
HS
915 struct comedi_insn *insn,
916 unsigned int *data)
8cb9b9fb 917{
98943079 918 struct pci9111_private_data *dev_private = dev->private;
8cb9b9fb
EP
919 int i;
920
2306d9b1 921 for (i = 0; i < insn->n; i++)
b3450faf 922 data[i] = dev_private->ao_readback;
8cb9b9fb 923
b3450faf 924 return insn->n;
8cb9b9fb
EP
925}
926
da91b269 927static int pci9111_di_insn_bits(struct comedi_device *dev,
d1d7b20d 928 struct comedi_subdevice *s,
bfa6d3b8
HS
929 struct comedi_insn *insn,
930 unsigned int *data)
8cb9b9fb 931{
bfa6d3b8 932 data[1] = inw(dev->iobase + PCI9111_DIO_REG);
8cb9b9fb 933
a2714e3e 934 return insn->n;
8cb9b9fb
EP
935}
936
da91b269 937static int pci9111_do_insn_bits(struct comedi_device *dev,
d1d7b20d 938 struct comedi_subdevice *s,
83dcfee0
HS
939 struct comedi_insn *insn,
940 unsigned int *data)
8cb9b9fb 941{
83dcfee0
HS
942 unsigned int mask = data[0];
943 unsigned int bits = data[1];
8cb9b9fb 944
83dcfee0
HS
945 if (mask) {
946 s->state &= ~mask;
947 s->state |= (bits & mask);
8cb9b9fb 948
83dcfee0
HS
949 outw(s->state, dev->iobase + PCI9111_DIO_REG);
950 }
8cb9b9fb 951
83dcfee0 952 data[1] = s->state;
8cb9b9fb 953
a2714e3e 954 return insn->n;
8cb9b9fb
EP
955}
956
52f8ac98
BP
957/* ------------------------------------------------------------------ */
958/* INITIALISATION SECTION */
959/* ------------------------------------------------------------------ */
8cb9b9fb 960
52f8ac98 961/* Reset device */
8cb9b9fb 962
da91b269 963static int pci9111_reset(struct comedi_device *dev)
8cb9b9fb 964{
98943079
HS
965 struct pci9111_private_data *dev_private = dev->private;
966
52f8ac98 967 /* Set trigger source to software */
8cb9b9fb
EP
968
969 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
0a85b6f0 970 true, false);
8cb9b9fb
EP
971
972 pci9111_trigger_source_set(dev, software);
973 pci9111_pretrigger_set(dev, false);
974 pci9111_autoscan_set(dev, false);
975
52f8ac98 976 /* Reset 8254 chip */
8cb9b9fb
EP
977
978 dev_private->timer_divisor_1 = 0;
979 dev_private->timer_divisor_2 = 0;
980
981 pci9111_timer_set(dev);
982
983 return 0;
984}
985
3e5a0ba0
HS
986static int pci9111_attach_pci(struct comedi_device *dev,
987 struct pci_dev *pcidev)
8cb9b9fb 988{
98943079 989 struct pci9111_private_data *dev_private;
d1d7b20d 990 struct comedi_subdevice *s;
98943079 991 int ret;
3e5a0ba0
HS
992
993 comedi_set_hw_dev(dev, &pcidev->dev);
994 dev->board_name = dev->driver->driver_name;
8cb9b9fb 995
98943079
HS
996 ret = alloc_private(dev, sizeof(*dev_private));
997 if (ret)
998 return ret;
999 dev_private = dev->private;
1000
3e5a0ba0
HS
1001 ret = comedi_pci_enable(pcidev, dev->board_name);
1002 if (ret)
1003 return ret;
1004 dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
1005 dev->iobase = pci_resource_start(pcidev, 2);
8cb9b9fb
EP
1006
1007 pci9111_reset(dev);
1008
6a7b1b0c 1009 if (pcidev->irq > 0) {
3e5a0ba0
HS
1010 ret = request_irq(dev->irq, pci9111_interrupt,
1011 IRQF_SHARED, dev->board_name, dev);
1012 if (ret)
1013 return ret;
6a7b1b0c 1014 dev->irq = pcidev->irq;
8cb9b9fb 1015 }
8cb9b9fb 1016
98943079
HS
1017 ret = comedi_alloc_subdevices(dev, 4);
1018 if (ret)
1019 return ret;
8cb9b9fb 1020
573e31af 1021 s = &dev->subdevices[0];
d1d7b20d 1022 dev->read_subdev = s;
02baee8c
HS
1023 s->type = COMEDI_SUBD_AI;
1024 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ;
1025 s->n_chan = 16;
1026 s->maxdata = 0xffff;
1027 s->len_chanlist = 16;
1028 s->range_table = &pci9111_hr_ai_range;
1029 s->cancel = pci9111_ai_cancel;
1030 s->insn_read = pci9111_ai_insn_read;
1031 s->do_cmdtest = pci9111_ai_do_cmd_test;
1032 s->do_cmd = pci9111_ai_do_cmd;
1033 s->munge = pci9111_ai_munge;
d1d7b20d 1034
573e31af 1035 s = &dev->subdevices[1];
05841b36
HS
1036 s->type = COMEDI_SUBD_AO;
1037 s->subdev_flags = SDF_WRITABLE | SDF_COMMON;
1038 s->n_chan = 1;
1039 s->maxdata = 0x0fff;
1040 s->len_chanlist = 1;
1041 s->range_table = &range_bipolar10;
1042 s->insn_write = pci9111_ao_insn_write;
1043 s->insn_read = pci9111_ao_insn_read;
d1d7b20d 1044
573e31af 1045 s = &dev->subdevices[2];
3acf3176
HS
1046 s->type = COMEDI_SUBD_DI;
1047 s->subdev_flags = SDF_READABLE;
1048 s->n_chan = 16;
1049 s->maxdata = 1;
1050 s->range_table = &range_digital;
1051 s->insn_bits = pci9111_di_insn_bits;
d1d7b20d 1052
573e31af 1053 s = &dev->subdevices[3];
3acf3176
HS
1054 s->type = COMEDI_SUBD_DO;
1055 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1056 s->n_chan = 16;
1057 s->maxdata = 1;
1058 s->range_table = &range_digital;
1059 s->insn_bits = pci9111_do_insn_bits;
8cb9b9fb
EP
1060
1061 dev_private->is_valid = 1;
1062
034f8734
HS
1063 dev_info(dev->class_dev, "%s attached\n", dev->board_name);
1064
8cb9b9fb
EP
1065 return 0;
1066}
1067
484ecc95 1068static void pci9111_detach(struct comedi_device *dev)
8cb9b9fb 1069{
6a7b1b0c 1070 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
98943079 1071 struct pci9111_private_data *dev_private = dev->private;
6a7b1b0c 1072
98943079 1073 if (dev_private) {
8cb9b9fb
EP
1074 if (dev_private->is_valid)
1075 pci9111_reset(dev);
8cb9b9fb 1076 }
2306d9b1 1077 if (dev->irq != 0)
5f74ea14 1078 free_irq(dev->irq, dev);
6a7b1b0c 1079 if (pcidev) {
2306d9b1 1080 if (dev->iobase)
6a7b1b0c
HS
1081 comedi_pci_disable(pcidev);
1082 pci_dev_put(pcidev);
8cb9b9fb 1083 }
8cb9b9fb 1084}
90f703d3 1085
75e6301b
HS
1086static struct comedi_driver adl_pci9111_driver = {
1087 .driver_name = "adl_pci9111",
e68a83fe 1088 .module = THIS_MODULE,
3e5a0ba0 1089 .attach_pci = pci9111_attach_pci,
e68a83fe
HS
1090 .detach = pci9111_detach,
1091};
1092
75e6301b
HS
1093static int __devinit pci9111_pci_probe(struct pci_dev *dev,
1094 const struct pci_device_id *ent)
e68a83fe 1095{
75e6301b 1096 return comedi_pci_auto_config(dev, &adl_pci9111_driver);
e68a83fe
HS
1097}
1098
75e6301b 1099static void __devexit pci9111_pci_remove(struct pci_dev *dev)
e68a83fe
HS
1100{
1101 comedi_pci_auto_unconfig(dev);
1102}
1103
1104static DEFINE_PCI_DEVICE_TABLE(pci9111_pci_table) = {
1105 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI9111_HR_DEVICE_ID) },
1106 /* { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID) }, */
1107 { 0 }
1108};
1109MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
1110
75e6301b
HS
1111static struct pci_driver adl_pci9111_pci_driver = {
1112 .name = "adl_pci9111",
e68a83fe 1113 .id_table = pci9111_pci_table,
75e6301b
HS
1114 .probe = pci9111_pci_probe,
1115 .remove = __devexit_p(pci9111_pci_remove),
e68a83fe 1116};
75e6301b 1117module_comedi_pci_driver(adl_pci9111_driver, adl_pci9111_pci_driver);
e68a83fe 1118
90f703d3
AT
1119MODULE_AUTHOR("Comedi http://www.comedi.org");
1120MODULE_DESCRIPTION("Comedi low-level driver");
1121MODULE_LICENSE("GPL");