Merge branch 'io_remap_pfn_range' of git://www.jni.nu/cris
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / comedi / drivers / adv_pci1710.c
CommitLineData
0e8db97a
MD
1/*
2 * comedi/drivers/adv_pci1710.c
3 *
4 * Author: Michal Dobes <dobes@tesnet.cz>
5 *
6 * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
7 * for testing and informations.
8 *
9 * hardware driver for Advantech cards:
10 * card: PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
11 * driver: pci1710, pci1710hg, pci1711, pci1713, pci1720, pci1731
12 *
13 * Options:
14 * [0] - PCI bus number - if bus number and slot number are 0,
15 * then driver search for first unused card
16 * [1] - PCI slot number
17 *
18*/
19/*
20Driver: adv_pci1710
21Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
97feeef5 22 Advantech PCI-1720, PCI-1731
0e8db97a
MD
23Author: Michal Dobes <dobes@tesnet.cz>
24Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
25 PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
26 PCI-1731
27Status: works
28
29This driver supports AI, AO, DI and DO subdevices.
30AI subdevice supports cmd and insn interface,
31other subdevices support only insn interface.
32
33The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
34driver cannot distinguish between them, as would be normal for a
35PCI driver.
36
37Configuration options:
38 [0] - PCI bus of device (optional)
39 [1] - PCI slot of device (optional)
97feeef5
M
40 If bus/slot is not specified, the first available PCI
41 device will be used.
0e8db97a
MD
42*/
43
70265d24
JS
44#include <linux/interrupt.h>
45
0e8db97a
MD
46#include "../comedidev.h"
47
48#include "comedi_pci.h"
49
50#include "8253.h"
51#include "amcc_s5933.h"
52
97feeef5
M
53#define PCI171x_PARANOIDCHECK /* if defined, then is used code which control
54 * correct channel number on every 12 bit
55 * sample */
0e8db97a
MD
56
57#undef PCI171X_EXTDEBUG
58
59#define DRV_NAME "adv_pci1710"
60
61#undef DPRINTK
62#ifdef PCI171X_EXTDEBUG
5f74ea14 63#define DPRINTK(fmt, args...) printk(fmt, ## args)
0e8db97a
MD
64#else
65#define DPRINTK(fmt, args...)
66#endif
67
59af888d
GKH
68#define PCI_VENDOR_ID_ADVANTECH 0x13fe
69
2696fb57 70/* hardware types of the cards */
0e8db97a
MD
71#define TYPE_PCI171X 0
72#define TYPE_PCI1713 2
73#define TYPE_PCI1720 3
74
97feeef5
M
75#define IORANGE_171x 32
76#define IORANGE_1720 16
0e8db97a
MD
77
78#define PCI171x_AD_DATA 0 /* R: A/D data */
79#define PCI171x_SOFTTRG 0 /* W: soft trigger for A/D */
80#define PCI171x_RANGE 2 /* W: A/D gain/range register */
81#define PCI171x_MUX 4 /* W: A/D multiplexor control */
82#define PCI171x_STATUS 6 /* R: status register */
83#define PCI171x_CONTROL 6 /* W: control register */
84#define PCI171x_CLRINT 8 /* W: clear interrupts request */
85#define PCI171x_CLRFIFO 9 /* W: clear FIFO */
86#define PCI171x_DA1 10 /* W: D/A register */
87#define PCI171x_DA2 12 /* W: D/A register */
88#define PCI171x_DAREF 14 /* W: D/A reference control */
89#define PCI171x_DI 16 /* R: digi inputs */
90#define PCI171x_DO 16 /* R: digi inputs */
d5a2ffd8
UKK
91#define PCI171x_CNT0 24 /* R/W: 8254 counter 0 */
92#define PCI171x_CNT1 26 /* R/W: 8254 counter 1 */
93#define PCI171x_CNT2 28 /* R/W: 8254 counter 2 */
0e8db97a
MD
94#define PCI171x_CNTCTRL 30 /* W: 8254 counter control */
95
97feeef5
M
96/* upper bits from status register (PCI171x_STATUS) (lower is same with control
97 * reg) */
0e8db97a
MD
98#define Status_FE 0x0100 /* 1=FIFO is empty */
99#define Status_FH 0x0200 /* 1=FIFO is half full */
100#define Status_FF 0x0400 /* 1=FIFO is full, fatal error */
101#define Status_IRQ 0x0800 /* 1=IRQ occured */
2696fb57 102/* bits from control register (PCI171x_CONTROL) */
97feeef5
M
103#define Control_CNT0 0x0040 /* 1=CNT0 have external source,
104 * 0=have internal 100kHz source */
0e8db97a
MD
105#define Control_ONEFH 0x0020 /* 1=IRQ on FIFO is half full, 0=every sample */
106#define Control_IRQEN 0x0010 /* 1=enable IRQ */
107#define Control_GATE 0x0008 /* 1=enable external trigger GATE (8254?) */
108#define Control_EXT 0x0004 /* 1=external trigger source */
109#define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
110#define Control_SW 0x0001 /* 1=enable software trigger source */
2696fb57 111/* bits from counter control register (PCI171x_CNTCTRL) */
0e8db97a
MD
112#define Counter_BCD 0x0001 /* 0 = binary counter, 1 = BCD counter */
113#define Counter_M0 0x0002 /* M0-M2 select modes 0-5 */
114#define Counter_M1 0x0004 /* 000 = mode 0, 010 = mode 2 ... */
115#define Counter_M2 0x0008
116#define Counter_RW0 0x0010 /* RW0/RW1 select read/write mode */
117#define Counter_RW1 0x0020
118#define Counter_SC0 0x0040 /* Select Counter. Only 00 or 11 may */
97feeef5
M
119#define Counter_SC1 0x0080 /* be used, 00 for CNT0,
120 * 11 for read-back command */
0e8db97a
MD
121
122#define PCI1720_DA0 0 /* W: D/A register 0 */
123#define PCI1720_DA1 2 /* W: D/A register 1 */
124#define PCI1720_DA2 4 /* W: D/A register 2 */
125#define PCI1720_DA3 6 /* W: D/A register 3 */
126#define PCI1720_RANGE 8 /* R/W: D/A range register */
127#define PCI1720_SYNCOUT 9 /* W: D/A synchronized output register */
128#define PCI1720_SYNCONT 15 /* R/W: D/A synchronized control */
129
2696fb57 130/* D/A synchronized control (PCI1720_SYNCONT) */
0e8db97a
MD
131#define Syncont_SC0 1 /* set synchronous output mode */
132
9ced1de6 133static const struct comedi_lrange range_pci1710_3 = { 9, {
0a85b6f0
MT
134 BIP_RANGE(5),
135 BIP_RANGE(2.5),
136 BIP_RANGE(1.25),
137 BIP_RANGE(0.625),
138 BIP_RANGE(10),
139 UNI_RANGE(10),
140 UNI_RANGE(5),
141 UNI_RANGE(2.5),
142 UNI_RANGE(1.25)
143 }
0e8db97a
MD
144};
145
97feeef5
M
146static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
147 0x10, 0x11, 0x12, 0x13 };
0e8db97a 148
9ced1de6 149static const struct comedi_lrange range_pci1710hg = { 12, {
0a85b6f0
MT
150 BIP_RANGE(5),
151 BIP_RANGE(0.5),
152 BIP_RANGE(0.05),
153 BIP_RANGE(0.005),
154 BIP_RANGE(10),
155 BIP_RANGE(1),
156 BIP_RANGE(0.1),
157 BIP_RANGE(0.01),
158 UNI_RANGE(10),
159 UNI_RANGE(1),
160 UNI_RANGE(0.1),
161 UNI_RANGE(0.01)
162 }
0e8db97a
MD
163};
164
97feeef5
M
165static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
166 0x05, 0x06, 0x07, 0x10, 0x11,
167 0x12, 0x13 };
0e8db97a 168
9ced1de6 169static const struct comedi_lrange range_pci17x1 = { 5, {
0a85b6f0
MT
170 BIP_RANGE(10),
171 BIP_RANGE(5),
172 BIP_RANGE(2.5),
173 BIP_RANGE(1.25),
174 BIP_RANGE(0.625)
175 }
0e8db97a
MD
176};
177
178static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
179
9ced1de6 180static const struct comedi_lrange range_pci1720 = { 4, {
0a85b6f0
MT
181 UNI_RANGE(5),
182 UNI_RANGE(10),
183 BIP_RANGE(5),
184 BIP_RANGE(10)
185 }
0e8db97a
MD
186};
187
9ced1de6 188static const struct comedi_lrange range_pci171x_da = { 2, {
0a85b6f0
MT
189 UNI_RANGE(5),
190 UNI_RANGE(10),
191 }
0e8db97a
MD
192};
193
0a85b6f0
MT
194static int pci1710_attach(struct comedi_device *dev,
195 struct comedi_devconfig *it);
da91b269 196static int pci1710_detach(struct comedi_device *dev);
0e8db97a 197
7875a00b 198struct boardtype {
2696fb57 199 const char *name; /* board name */
0e8db97a 200 int device_id;
2696fb57
BP
201 int iorange; /* I/O range len */
202 char have_irq; /* 1=card support IRQ */
203 char cardtype; /* 0=1710& co. 2=1713, ... */
204 int n_aichan; /* num of A/D chans */
205 int n_aichand; /* num of A/D chans in diff mode */
206 int n_aochan; /* num of D/A chans */
207 int n_dichan; /* num of DI chans */
208 int n_dochan; /* num of DO chans */
209 int n_counter; /* num of counters */
210 int ai_maxdata; /* resolution of A/D */
211 int ao_maxdata; /* resolution of D/A */
212 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
213 const char *rangecode_ai; /* range codes for programming */
214 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
215 unsigned int ai_ns_min; /* max sample speed of card v ns */
216 unsigned int fifo_half_size; /* size of FIFO/2 */
7875a00b 217};
0e8db97a
MD
218
219static DEFINE_PCI_DEVICE_TABLE(pci1710_pci_table) = {
0a85b6f0
MT
220 {
221 PCI_VENDOR_ID_ADVANTECH, 0x1710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
222 PCI_VENDOR_ID_ADVANTECH, 0x1711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
223 PCI_VENDOR_ID_ADVANTECH, 0x1713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
224 PCI_VENDOR_ID_ADVANTECH, 0x1720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
225 PCI_VENDOR_ID_ADVANTECH, 0x1731, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
226 0}
0e8db97a
MD
227};
228
229MODULE_DEVICE_TABLE(pci, pci1710_pci_table);
230
7875a00b 231static const struct boardtype boardtypes[] = {
0e8db97a 232 {"pci1710", 0x1710,
0a85b6f0
MT
233 IORANGE_171x, 1, TYPE_PCI171X,
234 16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
235 &range_pci1710_3, range_codes_pci1710_3,
236 &range_pci171x_da,
237 10000, 2048},
0e8db97a 238 {"pci1710hg", 0x1710,
0a85b6f0
MT
239 IORANGE_171x, 1, TYPE_PCI171X,
240 16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
241 &range_pci1710hg, range_codes_pci1710hg,
242 &range_pci171x_da,
243 10000, 2048},
0e8db97a 244 {"pci1711", 0x1711,
0a85b6f0
MT
245 IORANGE_171x, 1, TYPE_PCI171X,
246 16, 0, 2, 16, 16, 1, 0x0fff, 0x0fff,
247 &range_pci17x1, range_codes_pci17x1, &range_pci171x_da,
248 10000, 512},
0e8db97a 249 {"pci1713", 0x1713,
0a85b6f0
MT
250 IORANGE_171x, 1, TYPE_PCI1713,
251 32, 16, 0, 0, 0, 0, 0x0fff, 0x0000,
252 &range_pci1710_3, range_codes_pci1710_3, NULL,
253 10000, 2048},
0e8db97a 254 {"pci1720", 0x1720,
0a85b6f0
MT
255 IORANGE_1720, 0, TYPE_PCI1720,
256 0, 0, 4, 0, 0, 0, 0x0000, 0x0fff,
257 NULL, NULL, &range_pci1720,
258 0, 0},
0e8db97a 259 {"pci1731", 0x1731,
0a85b6f0
MT
260 IORANGE_171x, 1, TYPE_PCI171X,
261 16, 0, 0, 16, 16, 0, 0x0fff, 0x0000,
262 &range_pci17x1, range_codes_pci17x1, NULL,
263 10000, 512},
2696fb57 264 /* dummy entry corresponding to driver name */
0e8db97a
MD
265 {.name = DRV_NAME},
266};
267
7875a00b 268#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
0e8db97a 269
139dfbdf 270static struct comedi_driver driver_pci1710 = {
0e8db97a
MD
271 .driver_name = DRV_NAME,
272 .module = THIS_MODULE,
273 .attach = pci1710_attach,
274 .detach = pci1710_detach,
275 .num_names = n_boardtypes,
276 .board_name = &boardtypes[0].name,
7875a00b 277 .offset = sizeof(struct boardtype),
0e8db97a
MD
278};
279
6e8131a8 280struct pci1710_private {
2696fb57
BP
281 struct pci_dev *pcidev; /* ptr to PCI device */
282 char valid; /* card is usable */
283 char neverending_ai; /* we do unlimited AI */
284 unsigned int CntrlReg; /* Control register */
285 unsigned int i8254_osc_base; /* frequence of onboard oscilator */
286 unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */
287 unsigned int ai_act_scan; /* how many scans we finished */
288 unsigned int ai_act_chan; /* actual position in actual scan */
289 unsigned int ai_buf_ptr; /* data buffer ptr in samples */
290 unsigned char ai_eos; /* 1=EOS wake up */
0e8db97a
MD
291 unsigned char ai_et;
292 unsigned int ai_et_CntrlReg;
293 unsigned int ai_et_MuxVal;
294 unsigned int ai_et_div1, ai_et_div2;
2696fb57
BP
295 unsigned int act_chanlist[32]; /* list of scaned channel */
296 unsigned char act_chanlist_len; /* len of scanlist */
297 unsigned char act_chanlist_pos; /* actual position in MUX list */
298 unsigned char da_ranges; /* copy of D/A outpit range register */
299 unsigned int ai_scans; /* len of scanlist */
300 unsigned int ai_n_chan; /* how many channels is measured */
301 unsigned int *ai_chanlist; /* actaul chanlist */
302 unsigned int ai_flags; /* flaglist */
303 unsigned int ai_data_len; /* len of data buffer */
0a85b6f0 304 short *ai_data; /* data buffer */
2696fb57 305 unsigned int ai_timer1; /* timers */
0e8db97a 306 unsigned int ai_timer2;
2696fb57 307 short ao_data[4]; /* data output buffer */
97feeef5
M
308 unsigned int cnt0_write_wait; /* after a write, wait for update of the
309 * internal state */
6e8131a8 310};
0e8db97a 311
6e8131a8 312#define devpriv ((struct pci1710_private *)dev->private)
7875a00b 313#define this_board ((const struct boardtype *)dev->board_ptr)
0e8db97a
MD
314
315/*
316==============================================================================
317*/
318
0a85b6f0
MT
319static int check_channel_list(struct comedi_device *dev,
320 struct comedi_subdevice *s,
321 unsigned int *chanlist, unsigned int n_chan);
322static void setup_channel_list(struct comedi_device *dev,
323 struct comedi_subdevice *s,
324 unsigned int *chanlist, unsigned int n_chan,
325 unsigned int seglen);
326static void start_pacer(struct comedi_device *dev, int mode,
327 unsigned int divisor1, unsigned int divisor2);
da91b269 328static int pci1710_reset(struct comedi_device *dev);
0a85b6f0
MT
329static int pci171x_ai_cancel(struct comedi_device *dev,
330 struct comedi_subdevice *s);
0e8db97a 331
97feeef5
M
332/* used for gain list programming */
333static const unsigned int muxonechan[] = {
334 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
0e8db97a
MD
335 0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
336 0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
337 0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
338};
339
340/*
341==============================================================================
342*/
0a85b6f0
MT
343static int pci171x_insn_read_ai(struct comedi_device *dev,
344 struct comedi_subdevice *s,
345 struct comedi_insn *insn, unsigned int *data)
0e8db97a
MD
346{
347 int n, timeout;
348#ifdef PCI171x_PARANOIDCHECK
349 unsigned int idata;
350#endif
351
352 DPRINTK("adv_pci1710 EDBG: BGN: pci171x_insn_read_ai(...)\n");
353 devpriv->CntrlReg &= Control_CNT0;
2696fb57 354 devpriv->CntrlReg |= Control_SW; /* set software trigger */
0e8db97a
MD
355 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
356 outb(0, dev->iobase + PCI171x_CLRFIFO);
357 outb(0, dev->iobase + PCI171x_CLRINT);
358
359 setup_channel_list(dev, s, &insn->chanspec, 1, 1);
360
361 DPRINTK("adv_pci1710 A ST=%4x IO=%x\n",
362 inw(dev->iobase + PCI171x_STATUS),
363 dev->iobase + PCI171x_STATUS);
364 for (n = 0; n < insn->n; n++) {
365 outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
366 DPRINTK("adv_pci1710 B n=%d ST=%4x\n", n,
367 inw(dev->iobase + PCI171x_STATUS));
5f74ea14 368 /* udelay(1); */
0e8db97a
MD
369 DPRINTK("adv_pci1710 C n=%d ST=%4x\n", n,
370 inw(dev->iobase + PCI171x_STATUS));
371 timeout = 100;
372 while (timeout--) {
373 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_FE))
374 goto conv_finish;
375 if (!(timeout % 10))
376 DPRINTK("adv_pci1710 D n=%d tm=%d ST=%4x\n", n,
377 timeout,
378 inw(dev->iobase + PCI171x_STATUS));
379 }
380 comedi_error(dev, "A/D insn timeout");
381 outb(0, dev->iobase + PCI171x_CLRFIFO);
382 outb(0, dev->iobase + PCI171x_CLRINT);
383 data[n] = 0;
0a85b6f0
MT
384 DPRINTK
385 ("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n",
386 n);
0e8db97a
MD
387 return -ETIME;
388
0a85b6f0 389conv_finish:
0e8db97a
MD
390#ifdef PCI171x_PARANOIDCHECK
391 idata = inw(dev->iobase + PCI171x_AD_DATA);
392 if (this_board->cardtype != TYPE_PCI1713)
393 if ((idata & 0xf000) != devpriv->act_chanlist[0]) {
394 comedi_error(dev, "A/D insn data droput!");
395 return -ETIME;
396 }
397 data[n] = idata & 0x0fff;
398#else
399 data[n] = inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff;
400#endif
401
402 }
403
404 outb(0, dev->iobase + PCI171x_CLRFIFO);
405 outb(0, dev->iobase + PCI171x_CLRINT);
406
407 DPRINTK("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n", n);
408 return n;
409}
410
411/*
412==============================================================================
413*/
0a85b6f0
MT
414static int pci171x_insn_write_ao(struct comedi_device *dev,
415 struct comedi_subdevice *s,
416 struct comedi_insn *insn, unsigned int *data)
0e8db97a
MD
417{
418 int n, chan, range, ofs;
419
420 chan = CR_CHAN(insn->chanspec);
421 range = CR_RANGE(insn->chanspec);
422 if (chan) {
423 devpriv->da_ranges &= 0xfb;
424 devpriv->da_ranges |= (range << 2);
425 outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
426 ofs = PCI171x_DA2;
427 } else {
428 devpriv->da_ranges &= 0xfe;
429 devpriv->da_ranges |= range;
430 outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
431 ofs = PCI171x_DA1;
432 }
433
434 for (n = 0; n < insn->n; n++)
435 outw(data[n], dev->iobase + ofs);
436
437 devpriv->ao_data[chan] = data[n];
438
439 return n;
440
441}
442
443/*
444==============================================================================
445*/
0a85b6f0
MT
446static int pci171x_insn_read_ao(struct comedi_device *dev,
447 struct comedi_subdevice *s,
448 struct comedi_insn *insn, unsigned int *data)
0e8db97a
MD
449{
450 int n, chan;
451
452 chan = CR_CHAN(insn->chanspec);
453 for (n = 0; n < insn->n; n++)
454 data[n] = devpriv->ao_data[chan];
455
456 return n;
457}
458
459/*
460==============================================================================
461*/
0a85b6f0
MT
462static int pci171x_insn_bits_di(struct comedi_device *dev,
463 struct comedi_subdevice *s,
464 struct comedi_insn *insn, unsigned int *data)
0e8db97a
MD
465{
466 data[1] = inw(dev->iobase + PCI171x_DI);
467
468 return 2;
469}
470
471/*
472==============================================================================
473*/
0a85b6f0
MT
474static int pci171x_insn_bits_do(struct comedi_device *dev,
475 struct comedi_subdevice *s,
476 struct comedi_insn *insn, unsigned int *data)
0e8db97a
MD
477{
478 if (data[0]) {
479 s->state &= ~data[0];
480 s->state |= (data[0] & data[1]);
481 outw(s->state, dev->iobase + PCI171x_DO);
482 }
483 data[1] = s->state;
484
485 return 2;
486}
487
488/*
489==============================================================================
490*/
0a85b6f0
MT
491static int pci171x_insn_counter_read(struct comedi_device *dev,
492 struct comedi_subdevice *s,
493 struct comedi_insn *insn,
494 unsigned int *data)
0e8db97a
MD
495{
496 unsigned int msb, lsb, ccntrl;
497 int i;
498
499 ccntrl = 0xD2; /* count only */
500 for (i = 0; i < insn->n; i++) {
501 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
502
503 lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
504 msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
505
506 data[0] = lsb | (msb << 8);
507 }
508
509 return insn->n;
510}
511
512/*
513==============================================================================
514*/
0a85b6f0
MT
515static int pci171x_insn_counter_write(struct comedi_device *dev,
516 struct comedi_subdevice *s,
517 struct comedi_insn *insn,
518 unsigned int *data)
0e8db97a
MD
519{
520 uint msb, lsb, ccntrl, status;
521
522 lsb = data[0] & 0x00FF;
523 msb = (data[0] & 0xFF00) >> 8;
524
525 /* write lsb, then msb */
526 outw(lsb, dev->iobase + PCI171x_CNT0);
527 outw(msb, dev->iobase + PCI171x_CNT0);
528
529 if (devpriv->cnt0_write_wait) {
530 /* wait for the new count to be loaded */
531 ccntrl = 0xE2;
532 do {
533 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
534 status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
535 } while (status & 0x40);
536 }
537
538 return insn->n;
539}
540
541/*
542==============================================================================
543*/
da91b269 544static int pci171x_insn_counter_config(struct comedi_device *dev,
0a85b6f0
MT
545 struct comedi_subdevice *s,
546 struct comedi_insn *insn,
547 unsigned int *data)
0e8db97a
MD
548{
549#ifdef unused
550 /* This doesn't work like a normal Comedi counter config */
551 uint ccntrl = 0;
552
553 devpriv->cnt0_write_wait = data[0] & 0x20;
554
555 /* internal or external clock? */
556 if (!(data[0] & 0x10)) { /* internal */
557 devpriv->CntrlReg &= ~Control_CNT0;
558 } else {
559 devpriv->CntrlReg |= Control_CNT0;
560 }
561 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
562
563 if (data[0] & 0x01)
564 ccntrl |= Counter_M0;
565 if (data[0] & 0x02)
566 ccntrl |= Counter_M1;
567 if (data[0] & 0x04)
568 ccntrl |= Counter_M2;
569 if (data[0] & 0x08)
570 ccntrl |= Counter_BCD;
571 ccntrl |= Counter_RW0; /* set read/write mode */
572 ccntrl |= Counter_RW1;
573 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
574#endif
575
576 return 1;
577}
578
579/*
580==============================================================================
581*/
0a85b6f0
MT
582static int pci1720_insn_write_ao(struct comedi_device *dev,
583 struct comedi_subdevice *s,
584 struct comedi_insn *insn, unsigned int *data)
0e8db97a
MD
585{
586 int n, rangereg, chan;
587
588 chan = CR_CHAN(insn->chanspec);
589 rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
590 rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
591 if (rangereg != devpriv->da_ranges) {
592 outb(rangereg, dev->iobase + PCI1720_RANGE);
593 devpriv->da_ranges = rangereg;
594 }
595
596 for (n = 0; n < insn->n; n++) {
597 outw(data[n], dev->iobase + PCI1720_DA0 + (chan << 1));
2696fb57 598 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
0e8db97a
MD
599 }
600
601 devpriv->ao_data[chan] = data[n];
602
603 return n;
604}
605
606/*
607==============================================================================
608*/
609static void interrupt_pci1710_every_sample(void *d)
610{
71b5f4f1 611 struct comedi_device *dev = d;
34c43922 612 struct comedi_subdevice *s = dev->subdevices + 0;
0e8db97a
MD
613 int m;
614#ifdef PCI171x_PARANOIDCHECK
790c5541 615 short sampl;
0e8db97a
MD
616#endif
617
618 DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_every_sample(...)\n");
619 m = inw(dev->iobase + PCI171x_STATUS);
620 if (m & Status_FE) {
5f74ea14 621 printk("comedi%d: A/D FIFO empty (%4x)\n", dev->minor, m);
0e8db97a
MD
622 pci171x_ai_cancel(dev, s);
623 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
624 comedi_event(dev, s);
625 return;
626 }
627 if (m & Status_FF) {
5f74ea14 628 printk
0a85b6f0
MT
629 ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
630 dev->minor, m);
0e8db97a
MD
631 pci171x_ai_cancel(dev, s);
632 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
633 comedi_event(dev, s);
634 return;
635 }
636
2696fb57 637 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
0e8db97a
MD
638
639 DPRINTK("FOR ");
640 for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
641#ifdef PCI171x_PARANOIDCHECK
642 sampl = inw(dev->iobase + PCI171x_AD_DATA);
643 DPRINTK("%04x:", sampl);
644 if (this_board->cardtype != TYPE_PCI1713)
645 if ((sampl & 0xf000) !=
0a85b6f0 646 devpriv->act_chanlist[s->async->cur_chan]) {
5f74ea14 647 printk
0a85b6f0
MT
648 ("comedi: A/D data dropout: received data from channel %d, expected %d!\n",
649 (sampl & 0xf000) >> 12,
650 (devpriv->
651 act_chanlist[s->
652 async->cur_chan] & 0xf000) >>
653 12);
0e8db97a
MD
654 pci171x_ai_cancel(dev, s);
655 s->async->events |=
0a85b6f0 656 COMEDI_CB_EOA | COMEDI_CB_ERROR;
0e8db97a
MD
657 comedi_event(dev, s);
658 return;
659 }
660 DPRINTK("%8d %2d %8d~", s->async->buf_int_ptr,
661 s->async->cur_chan, s->async->buf_int_count);
662 comedi_buf_put(s->async, sampl & 0x0fff);
663#else
664 comedi_buf_put(s->async,
0a85b6f0 665 inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
0e8db97a
MD
666#endif
667 ++s->async->cur_chan;
668
ec14016e 669 if (s->async->cur_chan >= devpriv->ai_n_chan)
0e8db97a 670 s->async->cur_chan = 0;
ec14016e 671
0e8db97a 672
2696fb57 673 if (s->async->cur_chan == 0) { /* one scan done */
0e8db97a 674 devpriv->ai_act_scan++;
0a85b6f0
MT
675 DPRINTK
676 ("adv_pci1710 EDBG: EOS1 bic %d bip %d buc %d bup %d\n",
677 s->async->buf_int_count, s->async->buf_int_ptr,
678 s->async->buf_user_count, s->async->buf_user_ptr);
0e8db97a 679 DPRINTK("adv_pci1710 EDBG: EOS2\n");
2696fb57 680 if ((!devpriv->neverending_ai) && (devpriv->ai_act_scan >= devpriv->ai_scans)) { /* all data sampled */
0e8db97a
MD
681 pci171x_ai_cancel(dev, s);
682 s->async->events |= COMEDI_CB_EOA;
683 comedi_event(dev, s);
684 return;
685 }
686 }
687 }
688
2696fb57 689 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
0e8db97a
MD
690 DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_every_sample(...)\n");
691
692 comedi_event(dev, s);
693}
694
695/*
696==============================================================================
697*/
0a85b6f0
MT
698static int move_block_from_fifo(struct comedi_device *dev,
699 struct comedi_subdevice *s, int n, int turn)
0e8db97a
MD
700{
701 int i, j;
702#ifdef PCI171x_PARANOIDCHECK
703 int sampl;
704#endif
705 DPRINTK("adv_pci1710 EDBG: BGN: move_block_from_fifo(...,%d,%d)\n", n,
706 turn);
707 j = s->async->cur_chan;
708 for (i = 0; i < n; i++) {
709#ifdef PCI171x_PARANOIDCHECK
710 sampl = inw(dev->iobase + PCI171x_AD_DATA);
711 if (this_board->cardtype != TYPE_PCI1713)
712 if ((sampl & 0xf000) != devpriv->act_chanlist[j]) {
5f74ea14 713 printk
0a85b6f0
MT
714 ("comedi%d: A/D FIFO data dropout: received data from channel %d, expected %d! (%d/%d/%d/%d/%d/%4x)\n",
715 dev->minor, (sampl & 0xf000) >> 12,
716 (devpriv->act_chanlist[j] & 0xf000) >> 12,
717 i, j, devpriv->ai_act_scan, n, turn,
718 sampl);
0e8db97a
MD
719 pci171x_ai_cancel(dev, s);
720 s->async->events |=
0a85b6f0 721 COMEDI_CB_EOA | COMEDI_CB_ERROR;
0e8db97a
MD
722 comedi_event(dev, s);
723 return 1;
724 }
725 comedi_buf_put(s->async, sampl & 0x0fff);
726#else
727 comedi_buf_put(s->async,
0a85b6f0 728 inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
0e8db97a
MD
729#endif
730 j++;
731 if (j >= devpriv->ai_n_chan) {
732 j = 0;
733 devpriv->ai_act_scan++;
734 }
735 }
61283d22 736 s->async->cur_chan = j;
0e8db97a
MD
737 DPRINTK("adv_pci1710 EDBG: END: move_block_from_fifo(...)\n");
738 return 0;
739}
740
741/*
742==============================================================================
743*/
744static void interrupt_pci1710_half_fifo(void *d)
745{
71b5f4f1 746 struct comedi_device *dev = d;
34c43922 747 struct comedi_subdevice *s = dev->subdevices + 0;
0e8db97a
MD
748 int m, samplesinbuf;
749
750 DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_half_fifo(...)\n");
751 m = inw(dev->iobase + PCI171x_STATUS);
752 if (!(m & Status_FH)) {
5f74ea14 753 printk("comedi%d: A/D FIFO not half full! (%4x)\n",
0a85b6f0 754 dev->minor, m);
0e8db97a
MD
755 pci171x_ai_cancel(dev, s);
756 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
757 comedi_event(dev, s);
758 return;
759 }
760 if (m & Status_FF) {
5f74ea14 761 printk
0a85b6f0
MT
762 ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
763 dev->minor, m);
0e8db97a
MD
764 pci171x_ai_cancel(dev, s);
765 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
766 comedi_event(dev, s);
767 return;
768 }
769
770 samplesinbuf = this_board->fifo_half_size;
790c5541
BP
771 if (samplesinbuf * sizeof(short) >= devpriv->ai_data_len) {
772 m = devpriv->ai_data_len / sizeof(short);
0e8db97a
MD
773 if (move_block_from_fifo(dev, s, m, 0))
774 return;
775 samplesinbuf -= m;
776 }
777
778 if (samplesinbuf) {
779 if (move_block_from_fifo(dev, s, samplesinbuf, 1))
780 return;
781 }
782
783 if (!devpriv->neverending_ai)
97feeef5
M
784 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data
785 sampled */
0e8db97a
MD
786 pci171x_ai_cancel(dev, s);
787 s->async->events |= COMEDI_CB_EOA;
788 comedi_event(dev, s);
789 return;
790 }
2696fb57 791 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
0e8db97a
MD
792 DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_half_fifo(...)\n");
793
794 comedi_event(dev, s);
795}
796
797/*
798==============================================================================
799*/
70265d24 800static irqreturn_t interrupt_service_pci1710(int irq, void *d)
0e8db97a 801{
71b5f4f1 802 struct comedi_device *dev = d;
0e8db97a
MD
803
804 DPRINTK("adv_pci1710 EDBG: BGN: interrupt_service_pci1710(%d,...)\n",
805 irq);
2696fb57
BP
806 if (!dev->attached) /* is device attached? */
807 return IRQ_NONE; /* no, exit */
0e8db97a 808
2696fb57
BP
809 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ)) /* is this interrupt from our board? */
810 return IRQ_NONE; /* no, exit */
0e8db97a
MD
811
812 DPRINTK("adv_pci1710 EDBG: interrupt_service_pci1710() ST: %4x\n",
813 inw(dev->iobase + PCI171x_STATUS));
814
2696fb57 815 if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
0e8db97a
MD
816 devpriv->ai_et = 0;
817 devpriv->CntrlReg &= Control_CNT0;
2696fb57 818 devpriv->CntrlReg |= Control_SW; /* set software trigger */
0e8db97a
MD
819 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
820 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
821 outb(0, dev->iobase + PCI171x_CLRFIFO);
822 outb(0, dev->iobase + PCI171x_CLRINT);
823 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
824 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
2696fb57 825 /* start pacer */
0e8db97a
MD
826 start_pacer(dev, 1, devpriv->ai_et_div1, devpriv->ai_et_div2);
827 return IRQ_HANDLED;
828 }
2696fb57 829 if (devpriv->ai_eos) { /* We use FIFO half full INT or not? */
0e8db97a
MD
830 interrupt_pci1710_every_sample(d);
831 } else {
832 interrupt_pci1710_half_fifo(d);
833 }
834 DPRINTK("adv_pci1710 EDBG: END: interrupt_service_pci1710(...)\n");
835 return IRQ_HANDLED;
836}
837
838/*
839==============================================================================
840*/
da91b269 841static int pci171x_ai_docmd_and_mode(int mode, struct comedi_device *dev,
0a85b6f0 842 struct comedi_subdevice *s)
0e8db97a 843{
48b1aff5 844 unsigned int divisor1 = 0, divisor2 = 0;
0e8db97a
MD
845 unsigned int seglen;
846
847 DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_docmd_and_mode(%d,...)\n",
848 mode);
2696fb57 849 start_pacer(dev, -1, 0, 0); /* stop pacer */
0e8db97a
MD
850
851 seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
0a85b6f0 852 devpriv->ai_n_chan);
0e8db97a
MD
853 if (seglen < 1)
854 return -EINVAL;
855 setup_channel_list(dev, s, devpriv->ai_chanlist,
0a85b6f0 856 devpriv->ai_n_chan, seglen);
0e8db97a
MD
857
858 outb(0, dev->iobase + PCI171x_CLRFIFO);
859 outb(0, dev->iobase + PCI171x_CLRINT);
860
861 devpriv->ai_do = mode;
862
863 devpriv->ai_act_scan = 0;
864 s->async->cur_chan = 0;
865 devpriv->ai_buf_ptr = 0;
866 devpriv->neverending_ai = 0;
867
868 devpriv->CntrlReg &= Control_CNT0;
2696fb57 869 if ((devpriv->ai_flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? devpriv->ai_eos=1; */
0e8db97a
MD
870 devpriv->ai_eos = 1;
871 } else {
872 devpriv->CntrlReg |= Control_ONEFH;
873 devpriv->ai_eos = 0;
874 }
875
ec14016e 876 if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
0e8db97a 877 devpriv->neverending_ai = 1;
ec14016e
JW
878 /* well, user want neverending */
879 else
0e8db97a 880 devpriv->neverending_ai = 0;
ec14016e 881
0e8db97a
MD
882 switch (mode) {
883 case 1:
884 case 2:
885 if (devpriv->ai_timer1 < this_board->ai_ns_min)
886 devpriv->ai_timer1 = this_board->ai_ns_min;
887 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
888 if (mode == 2) {
889 devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
890 devpriv->CntrlReg &=
0a85b6f0 891 ~(Control_PACER | Control_ONEFH | Control_GATE);
0e8db97a
MD
892 devpriv->CntrlReg |= Control_EXT;
893 devpriv->ai_et = 1;
894 } else {
895 devpriv->ai_et = 0;
896 }
897 i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
0a85b6f0
MT
898 &divisor2, &devpriv->ai_timer1,
899 devpriv->ai_flags & TRIG_ROUND_MASK);
900 DPRINTK
901 ("adv_pci1710 EDBG: OSC base=%u div1=%u div2=%u timer=%u\n",
902 devpriv->i8254_osc_base, divisor1, divisor2,
903 devpriv->ai_timer1);
0e8db97a
MD
904 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
905 if (mode != 2) {
2696fb57 906 /* start pacer */
0e8db97a
MD
907 start_pacer(dev, mode, divisor1, divisor2);
908 } else {
909 devpriv->ai_et_div1 = divisor1;
910 devpriv->ai_et_div2 = divisor2;
911 }
912 break;
913 case 3:
914 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
915 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
916 break;
917 }
918
919 DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_docmd_and_mode(...)\n");
920 return 0;
921}
922
923#ifdef PCI171X_EXTDEBUG
924/*
925==============================================================================
926*/
da91b269 927static void pci171x_cmdtest_out(int e, struct comedi_cmd *cmd)
0e8db97a 928{
5f74ea14 929 printk("adv_pci1710 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
0a85b6f0 930 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
5f74ea14 931 printk("adv_pci1710 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
0a85b6f0 932 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
5f74ea14 933 printk("adv_pci1710 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
0a85b6f0 934 cmd->scan_end_src);
5f74ea14 935 printk("adv_pci1710 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
0a85b6f0 936 e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
0e8db97a
MD
937}
938#endif
939
940/*
941==============================================================================
942*/
0a85b6f0
MT
943static int pci171x_ai_cmdtest(struct comedi_device *dev,
944 struct comedi_subdevice *s,
945 struct comedi_cmd *cmd)
0e8db97a
MD
946{
947 int err = 0;
a8cb9ad9
GKH
948 int tmp;
949 unsigned int divisor1 = 0, divisor2 = 0;
0e8db97a
MD
950
951 DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...)\n");
952#ifdef PCI171X_EXTDEBUG
953 pci171x_cmdtest_out(-1, cmd);
954#endif
955 /* step 1: make sure trigger sources are trivially valid */
956
957 tmp = cmd->start_src;
958 cmd->start_src &= TRIG_NOW | TRIG_EXT;
959 if (!cmd->start_src || tmp != cmd->start_src)
960 err++;
961
962 tmp = cmd->scan_begin_src;
963 cmd->scan_begin_src &= TRIG_FOLLOW;
964 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
965 err++;
966
967 tmp = cmd->convert_src;
968 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
969 if (!cmd->convert_src || tmp != cmd->convert_src)
970 err++;
971
972 tmp = cmd->scan_end_src;
973 cmd->scan_end_src &= TRIG_COUNT;
974 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
975 err++;
976
977 tmp = cmd->stop_src;
978 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
979 if (!cmd->stop_src || tmp != cmd->stop_src)
980 err++;
981
982 if (err) {
983#ifdef PCI171X_EXTDEBUG
984 pci171x_cmdtest_out(1, cmd);
985#endif
0a85b6f0
MT
986 DPRINTK
987 ("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=1\n",
988 err);
0e8db97a
MD
989 return 1;
990 }
991
992 /* step 2: make sure trigger sources are unique and mutually compatible */
993
994 if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) {
995 cmd->start_src = TRIG_NOW;
996 err++;
997 }
998
999 if (cmd->scan_begin_src != TRIG_FOLLOW) {
1000 cmd->scan_begin_src = TRIG_FOLLOW;
1001 err++;
1002 }
1003
1004 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
1005 err++;
1006
1007 if (cmd->scan_end_src != TRIG_COUNT) {
1008 cmd->scan_end_src = TRIG_COUNT;
1009 err++;
1010 }
1011
1012 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
1013 err++;
1014
1015 if (err) {
1016#ifdef PCI171X_EXTDEBUG
1017 pci171x_cmdtest_out(2, cmd);
1018#endif
0a85b6f0
MT
1019 DPRINTK
1020 ("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=2\n",
1021 err);
0e8db97a
MD
1022 return 2;
1023 }
1024
1025 /* step 3: make sure arguments are trivially compatible */
1026
1027 if (cmd->start_arg != 0) {
1028 cmd->start_arg = 0;
1029 err++;
1030 }
1031
1032 if (cmd->scan_begin_arg != 0) {
1033 cmd->scan_begin_arg = 0;
1034 err++;
1035 }
1036
1037 if (cmd->convert_src == TRIG_TIMER) {
1038 if (cmd->convert_arg < this_board->ai_ns_min) {
1039 cmd->convert_arg = this_board->ai_ns_min;
1040 err++;
1041 }
1042 } else { /* TRIG_FOLLOW */
1043 if (cmd->convert_arg != 0) {
1044 cmd->convert_arg = 0;
1045 err++;
1046 }
1047 }
1048
0e8db97a
MD
1049 if (cmd->scan_end_arg != cmd->chanlist_len) {
1050 cmd->scan_end_arg = cmd->chanlist_len;
1051 err++;
1052 }
1053 if (cmd->stop_src == TRIG_COUNT) {
1054 if (!cmd->stop_arg) {
1055 cmd->stop_arg = 1;
1056 err++;
1057 }
1058 } else { /* TRIG_NONE */
1059 if (cmd->stop_arg != 0) {
1060 cmd->stop_arg = 0;
1061 err++;
1062 }
1063 }
1064
1065 if (err) {
1066#ifdef PCI171X_EXTDEBUG
1067 pci171x_cmdtest_out(3, cmd);
1068#endif
0a85b6f0
MT
1069 DPRINTK
1070 ("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=3\n",
1071 err);
0e8db97a
MD
1072 return 3;
1073 }
1074
1075 /* step 4: fix up any arguments */
1076
1077 if (cmd->convert_src == TRIG_TIMER) {
1078 tmp = cmd->convert_arg;
1079 i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
0a85b6f0
MT
1080 &divisor2, &cmd->convert_arg,
1081 cmd->flags & TRIG_ROUND_MASK);
0e8db97a
MD
1082 if (cmd->convert_arg < this_board->ai_ns_min)
1083 cmd->convert_arg = this_board->ai_ns_min;
1084 if (tmp != cmd->convert_arg)
1085 err++;
1086 }
1087
1088 if (err) {
0a85b6f0
MT
1089 DPRINTK
1090 ("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=4\n",
1091 err);
0e8db97a
MD
1092 return 4;
1093 }
1094
1095 /* step 5: complain about special chanlist considerations */
1096
1097 if (cmd->chanlist) {
1098 if (!check_channel_list(dev, s, cmd->chanlist,
0a85b6f0 1099 cmd->chanlist_len))
2696fb57 1100 return 5; /* incorrect channels list */
0e8db97a
MD
1101 }
1102
1103 DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) ret=0\n");
1104 return 0;
1105}
1106
1107/*
1108==============================================================================
1109*/
da91b269 1110static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
0e8db97a 1111{
ea6d0d4c 1112 struct comedi_cmd *cmd = &s->async->cmd;
0e8db97a
MD
1113
1114 DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmd(...)\n");
1115 devpriv->ai_n_chan = cmd->chanlist_len;
1116 devpriv->ai_chanlist = cmd->chanlist;
1117 devpriv->ai_flags = cmd->flags;
1118 devpriv->ai_data_len = s->async->prealloc_bufsz;
1119 devpriv->ai_data = s->async->prealloc_buf;
1120 devpriv->ai_timer1 = 0;
1121 devpriv->ai_timer2 = 0;
1122
ec14016e 1123 if (cmd->stop_src == TRIG_COUNT)
0e8db97a 1124 devpriv->ai_scans = cmd->stop_arg;
ec14016e 1125 else
0e8db97a 1126 devpriv->ai_scans = 0;
ec14016e 1127
0e8db97a 1128
2696fb57
BP
1129 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 2, 3 */
1130 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 and 2 */
0e8db97a
MD
1131 devpriv->ai_timer1 = cmd->convert_arg;
1132 return pci171x_ai_docmd_and_mode(cmd->start_src ==
0a85b6f0
MT
1133 TRIG_EXT ? 2 : 1, dev,
1134 s);
0e8db97a 1135 }
2696fb57 1136 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
0e8db97a
MD
1137 return pci171x_ai_docmd_and_mode(3, dev, s);
1138 }
1139 }
1140
1141 return -1;
1142}
1143
1144/*
1145==============================================================================
1146 Check if channel list from user is builded correctly
1147 If it's ok, then program scan/gain logic.
1148 This works for all cards.
1149*/
0a85b6f0
MT
1150static int check_channel_list(struct comedi_device *dev,
1151 struct comedi_subdevice *s,
1152 unsigned int *chanlist, unsigned int n_chan)
0e8db97a
MD
1153{
1154 unsigned int chansegment[32];
1155 unsigned int i, nowmustbechan, seglen, segpos;
1156
1157 DPRINTK("adv_pci1710 EDBG: check_channel_list(...,%d)\n", n_chan);
1158 /* correct channel and range number check itself comedi/range.c */
1159 if (n_chan < 1) {
1160 comedi_error(dev, "range/channel list is empty!");
1161 return 0;
1162 }
1163
1164 if (n_chan > 1) {
2696fb57
BP
1165 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
1166 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) { /* build part of chanlist */
5f74ea14 1167 /* printk("%d. %d %d\n",i,CR_CHAN(chanlist[i]),CR_RANGE(chanlist[i])); */
0e8db97a 1168 if (chanlist[0] == chanlist[i])
2696fb57
BP
1169 break; /* we detect loop, this must by finish */
1170 if (CR_CHAN(chanlist[i]) & 1) /* odd channel cann't by differencial */
0e8db97a
MD
1171 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
1172 comedi_error(dev,
0a85b6f0 1173 "Odd channel can't be differential input!\n");
0e8db97a
MD
1174 return 0;
1175 }
1176 nowmustbechan =
0a85b6f0 1177 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
0e8db97a
MD
1178 if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
1179 nowmustbechan = (nowmustbechan + 1) % s->n_chan;
2696fb57 1180 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continous :-( */
5f74ea14 1181 printk
0a85b6f0
MT
1182 ("channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
1183 i, CR_CHAN(chanlist[i]), nowmustbechan,
1184 CR_CHAN(chanlist[0]));
0e8db97a
MD
1185 return 0;
1186 }
2696fb57 1187 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
0e8db97a
MD
1188 }
1189
2696fb57 1190 for (i = 0, segpos = 0; i < n_chan; i++) { /* check whole chanlist */
5f74ea14 1191 /* printk("%d %d=%d %d\n",CR_CHAN(chansegment[i%seglen]),CR_RANGE(chansegment[i%seglen]),CR_CHAN(chanlist[i]),CR_RANGE(chanlist[i])); */
0e8db97a 1192 if (chanlist[i] != chansegment[i % seglen]) {
5f74ea14 1193 printk
0a85b6f0
MT
1194 ("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
1195 i, CR_CHAN(chansegment[i]),
1196 CR_RANGE(chansegment[i]),
1197 CR_AREF(chansegment[i]),
1198 CR_CHAN(chanlist[i % seglen]),
1199 CR_RANGE(chanlist[i % seglen]),
1200 CR_AREF(chansegment[i % seglen]));
2696fb57 1201 return 0; /* chan/gain list is strange */
0e8db97a
MD
1202 }
1203 }
1204 } else {
1205 seglen = 1;
1206 }
1207 return seglen;
1208}
1209
0a85b6f0
MT
1210static void setup_channel_list(struct comedi_device *dev,
1211 struct comedi_subdevice *s,
1212 unsigned int *chanlist, unsigned int n_chan,
1213 unsigned int seglen)
0e8db97a
MD
1214{
1215 unsigned int i, range, chanprog;
1216
1217 DPRINTK("adv_pci1710 EDBG: setup_channel_list(...,%d,%d)\n", n_chan,
1218 seglen);
1219 devpriv->act_chanlist_len = seglen;
1220 devpriv->act_chanlist_pos = 0;
1221
1222 DPRINTK("SegLen: %d\n", seglen);
2696fb57 1223 for (i = 0; i < seglen; i++) { /* store range list to card */
0e8db97a
MD
1224 chanprog = muxonechan[CR_CHAN(chanlist[i])];
1225 outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
1226 range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
1227 if (CR_AREF(chanlist[i]) == AREF_DIFF)
1228 range |= 0x0020;
1229 outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
1230#ifdef PCI171x_PARANOIDCHECK
1231 devpriv->act_chanlist[i] =
0a85b6f0 1232 (CR_CHAN(chanlist[i]) << 12) & 0xf000;
0e8db97a
MD
1233#endif
1234 DPRINTK("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
1235 devpriv->act_chanlist[i]);
1236 }
61283d22
IA
1237#ifdef PCI171x_PARANOIDCHECK
1238 for ( ; i < n_chan; i++) { /* store remainder of channel list */
1239 devpriv->act_chanlist[i] =
1240 (CR_CHAN(chanlist[i]) << 12) & 0xf000;
1241 }
1242#endif
0e8db97a
MD
1243
1244 devpriv->ai_et_MuxVal =
0a85b6f0 1245 CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
0e8db97a
MD
1246 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX); /* select channel interval to scan */
1247 DPRINTK("MUX: %4x L%4x.H%4x\n",
1248 CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8),
1249 CR_CHAN(chanlist[0]), CR_CHAN(chanlist[seglen - 1]));
1250}
1251
1252/*
1253==============================================================================
1254*/
0a85b6f0
MT
1255static void start_pacer(struct comedi_device *dev, int mode,
1256 unsigned int divisor1, unsigned int divisor2)
0e8db97a
MD
1257{
1258 DPRINTK("adv_pci1710 EDBG: BGN: start_pacer(%d,%u,%u)\n", mode,
1259 divisor1, divisor2);
1260 outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
1261 outw(0x74, dev->iobase + PCI171x_CNTCTRL);
1262
1263 if (mode == 1) {
1264 outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
1265 outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
1266 outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
1267 outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
1268 }
1269 DPRINTK("adv_pci1710 EDBG: END: start_pacer(...)\n");
1270}
1271
1272/*
1273==============================================================================
1274*/
0a85b6f0
MT
1275static int pci171x_ai_cancel(struct comedi_device *dev,
1276 struct comedi_subdevice *s)
0e8db97a
MD
1277{
1278 DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cancel(...)\n");
1279
1280 switch (this_board->cardtype) {
1281 default:
1282 devpriv->CntrlReg &= Control_CNT0;
1283 devpriv->CntrlReg |= Control_SW;
1284
2696fb57 1285 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
0e8db97a
MD
1286 start_pacer(dev, -1, 0, 0);
1287 outb(0, dev->iobase + PCI171x_CLRFIFO);
1288 outb(0, dev->iobase + PCI171x_CLRINT);
1289 break;
1290 }
1291
1292 devpriv->ai_do = 0;
1293 devpriv->ai_act_scan = 0;
1294 s->async->cur_chan = 0;
1295 devpriv->ai_buf_ptr = 0;
1296 devpriv->neverending_ai = 0;
1297
1298 DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_cancel(...)\n");
1299 return 0;
1300}
1301
1302/*
1303==============================================================================
1304*/
da91b269 1305static int pci171x_reset(struct comedi_device *dev)
0e8db97a
MD
1306{
1307 DPRINTK("adv_pci1710 EDBG: BGN: pci171x_reset(...)\n");
1308 outw(0x30, dev->iobase + PCI171x_CNTCTRL);
2696fb57
BP
1309 devpriv->CntrlReg = Control_SW | Control_CNT0; /* Software trigger, CNT0=external */
1310 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
1311 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
1312 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
1313 start_pacer(dev, -1, 0, 0); /* stop 8254 */
0e8db97a
MD
1314 devpriv->da_ranges = 0;
1315 if (this_board->n_aochan) {
2696fb57
BP
1316 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF); /* set DACs to 0..5V */
1317 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
0e8db97a
MD
1318 devpriv->ao_data[0] = 0x0000;
1319 if (this_board->n_aochan > 1) {
1320 outw(0, dev->iobase + PCI171x_DA2);
1321 devpriv->ao_data[1] = 0x0000;
1322 }
1323 }
2696fb57
BP
1324 outw(0, dev->iobase + PCI171x_DO); /* digital outputs to 0 */
1325 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
1326 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
0e8db97a
MD
1327
1328 DPRINTK("adv_pci1710 EDBG: END: pci171x_reset(...)\n");
1329 return 0;
1330}
1331
1332/*
1333==============================================================================
1334*/
da91b269 1335static int pci1720_reset(struct comedi_device *dev)
0e8db97a
MD
1336{
1337 DPRINTK("adv_pci1710 EDBG: BGN: pci1720_reset(...)\n");
2696fb57 1338 outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT); /* set synchronous output mode */
0e8db97a 1339 devpriv->da_ranges = 0xAA;
2696fb57
BP
1340 outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE); /* set all ranges to +/-5V */
1341 outw(0x0800, dev->iobase + PCI1720_DA0); /* set outputs to 0V */
0e8db97a
MD
1342 outw(0x0800, dev->iobase + PCI1720_DA1);
1343 outw(0x0800, dev->iobase + PCI1720_DA2);
1344 outw(0x0800, dev->iobase + PCI1720_DA3);
2696fb57 1345 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
0e8db97a
MD
1346 devpriv->ao_data[0] = 0x0800;
1347 devpriv->ao_data[1] = 0x0800;
1348 devpriv->ao_data[2] = 0x0800;
1349 devpriv->ao_data[3] = 0x0800;
1350 DPRINTK("adv_pci1710 EDBG: END: pci1720_reset(...)\n");
1351 return 0;
1352}
1353
1354/*
1355==============================================================================
1356*/
da91b269 1357static int pci1710_reset(struct comedi_device *dev)
0e8db97a
MD
1358{
1359 DPRINTK("adv_pci1710 EDBG: BGN: pci1710_reset(...)\n");
1360 switch (this_board->cardtype) {
1361 case TYPE_PCI1720:
1362 return pci1720_reset(dev);
1363 default:
1364 return pci171x_reset(dev);
1365 }
1366 DPRINTK("adv_pci1710 EDBG: END: pci1710_reset(...)\n");
1367}
1368
1369/*
1370==============================================================================
1371*/
0a85b6f0
MT
1372static int pci1710_attach(struct comedi_device *dev,
1373 struct comedi_devconfig *it)
0e8db97a 1374{
34c43922 1375 struct comedi_subdevice *s;
0e8db97a
MD
1376 int ret, subdev, n_subdevices;
1377 unsigned int irq;
1378 unsigned long iobase;
1379 struct pci_dev *pcidev;
1380 int opt_bus, opt_slot;
1381 const char *errstr;
1382 unsigned char pci_bus, pci_slot, pci_func;
1383 int i;
1384 int board_index;
1385
5f74ea14 1386 printk("comedi%d: adv_pci1710: ", dev->minor);
0e8db97a
MD
1387
1388 opt_bus = it->options[0];
1389 opt_slot = it->options[1];
1390
c3744138
BP
1391 ret = alloc_private(dev, sizeof(struct pci1710_private));
1392 if (ret < 0) {
5f74ea14 1393 printk(" - Allocation failed!\n");
0e8db97a
MD
1394 return -ENOMEM;
1395 }
1396
1397 /* Look for matching PCI device */
1398 errstr = "not found!";
1399 pcidev = NULL;
1400 board_index = this_board - boardtypes;
1401 while (NULL != (pcidev = pci_get_device(PCI_VENDOR_ID_ADVANTECH,
0a85b6f0
MT
1402 PCI_ANY_ID, pcidev))) {
1403 if (strcmp(this_board->name, DRV_NAME) == 0) {
1404 for (i = 0; i < n_boardtypes; ++i) {
1405 if (pcidev->device == boardtypes[i].device_id) {
0e8db97a
MD
1406 board_index = i;
1407 break;
1408 }
1409 }
0a85b6f0
MT
1410 if (i == n_boardtypes)
1411 continue;
1412 } else {
1413 if (pcidev->device != boardtypes[board_index].device_id)
1414 continue;
0e8db97a
MD
1415 }
1416
1417 /* Found matching vendor/device. */
1418 if (opt_bus || opt_slot) {
1419 /* Check bus/slot. */
1420 if (opt_bus != pcidev->bus->number
0a85b6f0 1421 || opt_slot != PCI_SLOT(pcidev->devfn))
0e8db97a
MD
1422 continue; /* no match */
1423 }
1424 /*
0a85b6f0
MT
1425 * Look for device that isn't in use.
1426 * Enable PCI device and request regions.
1427 */
0e8db97a 1428 if (comedi_pci_enable(pcidev, DRV_NAME)) {
0a85b6f0
MT
1429 errstr =
1430 "failed to enable PCI device and request regions!";
0e8db97a
MD
1431 continue;
1432 }
2696fb57 1433 /* fixup board_ptr in case we were using the dummy entry with the driver name */
0e8db97a
MD
1434 dev->board_ptr = &boardtypes[board_index];
1435 break;
1436 }
1437
1438 if (!pcidev) {
1439 if (opt_bus || opt_slot) {
5f74ea14 1440 printk(" - Card at b:s %d:%d %s\n",
0a85b6f0 1441 opt_bus, opt_slot, errstr);
0e8db97a 1442 } else {
5f74ea14 1443 printk(" - Card %s\n", errstr);
0e8db97a
MD
1444 }
1445 return -EIO;
1446 }
1447
1448 pci_bus = pcidev->bus->number;
1449 pci_slot = PCI_SLOT(pcidev->devfn);
1450 pci_func = PCI_FUNC(pcidev->devfn);
1451 irq = pcidev->irq;
1452 iobase = pci_resource_start(pcidev, 2);
1453
5f74ea14 1454 printk(", b:s:f=%d:%d:%d, io=0x%4lx", pci_bus, pci_slot, pci_func,
0a85b6f0 1455 iobase);
0e8db97a
MD
1456
1457 dev->iobase = iobase;
1458
1459 dev->board_name = this_board->name;
1460 devpriv->pcidev = pcidev;
1461
1462 n_subdevices = 0;
1463 if (this_board->n_aichan)
1464 n_subdevices++;
1465 if (this_board->n_aochan)
1466 n_subdevices++;
1467 if (this_board->n_dichan)
1468 n_subdevices++;
1469 if (this_board->n_dochan)
1470 n_subdevices++;
1471 if (this_board->n_counter)
1472 n_subdevices++;
1473
c3744138
BP
1474 ret = alloc_subdevices(dev, n_subdevices);
1475 if (ret < 0) {
5f74ea14 1476 printk(" - Allocation failed!\n");
0e8db97a
MD
1477 return ret;
1478 }
1479
1480 pci1710_reset(dev);
1481
1482 if (this_board->have_irq) {
1483 if (irq) {
5f74ea14 1484 if (request_irq(irq, interrupt_service_pci1710,
0e8db97a
MD
1485 IRQF_SHARED, "Advantech PCI-1710",
1486 dev)) {
5f74ea14 1487 printk
0a85b6f0
MT
1488 (", unable to allocate IRQ %d, DISABLING IT",
1489 irq);
0e8db97a
MD
1490 irq = 0; /* Can't use IRQ */
1491 } else {
5f74ea14 1492 printk(", irq=%u", irq);
0e8db97a
MD
1493 }
1494 } else {
5f74ea14 1495 printk(", IRQ disabled");
0e8db97a
MD
1496 }
1497 } else {
1498 irq = 0;
1499 }
1500
1501 dev->irq = irq;
1502
1503 printk(".\n");
1504
1505 subdev = 0;
1506
1507 if (this_board->n_aichan) {
1508 s = dev->subdevices + subdev;
1509 dev->read_subdev = s;
1510 s->type = COMEDI_SUBD_AI;
1511 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1512 if (this_board->n_aichand)
1513 s->subdev_flags |= SDF_DIFF;
1514 s->n_chan = this_board->n_aichan;
1515 s->maxdata = this_board->ai_maxdata;
1516 s->len_chanlist = this_board->n_aichan;
1517 s->range_table = this_board->rangelist_ai;
1518 s->cancel = pci171x_ai_cancel;
1519 s->insn_read = pci171x_insn_read_ai;
1520 if (irq) {
1521 s->subdev_flags |= SDF_CMD_READ;
1522 s->do_cmdtest = pci171x_ai_cmdtest;
1523 s->do_cmd = pci171x_ai_cmd;
1524 }
2696fb57 1525 devpriv->i8254_osc_base = 100; /* 100ns=10MHz */
0e8db97a
MD
1526 subdev++;
1527 }
1528
1529 if (this_board->n_aochan) {
1530 s = dev->subdevices + subdev;
1531 s->type = COMEDI_SUBD_AO;
1532 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1533 s->n_chan = this_board->n_aochan;
1534 s->maxdata = this_board->ao_maxdata;
1535 s->len_chanlist = this_board->n_aochan;
1536 s->range_table = this_board->rangelist_ao;
1537 switch (this_board->cardtype) {
1538 case TYPE_PCI1720:
1539 s->insn_write = pci1720_insn_write_ao;
1540 break;
1541 default:
1542 s->insn_write = pci171x_insn_write_ao;
1543 break;
1544 }
1545 s->insn_read = pci171x_insn_read_ao;
1546 subdev++;
1547 }
1548
1549 if (this_board->n_dichan) {
1550 s = dev->subdevices + subdev;
1551 s->type = COMEDI_SUBD_DI;
1552 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
1553 s->n_chan = this_board->n_dichan;
1554 s->maxdata = 1;
1555 s->len_chanlist = this_board->n_dichan;
1556 s->range_table = &range_digital;
1557 s->io_bits = 0; /* all bits input */
1558 s->insn_bits = pci171x_insn_bits_di;
1559 subdev++;
1560 }
1561
1562 if (this_board->n_dochan) {
1563 s = dev->subdevices + subdev;
1564 s->type = COMEDI_SUBD_DO;
1565 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1566 s->n_chan = this_board->n_dochan;
1567 s->maxdata = 1;
1568 s->len_chanlist = this_board->n_dochan;
1569 s->range_table = &range_digital;
97feeef5
M
1570 /* all bits output */
1571 s->io_bits = (1 << this_board->n_dochan) - 1;
0e8db97a
MD
1572 s->state = 0;
1573 s->insn_bits = pci171x_insn_bits_do;
1574 subdev++;
1575 }
1576
1577 if (this_board->n_counter) {
1578 s = dev->subdevices + subdev;
1579 s->type = COMEDI_SUBD_COUNTER;
1580 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1581 s->n_chan = this_board->n_counter;
1582 s->len_chanlist = this_board->n_counter;
1583 s->maxdata = 0xffff;
1584 s->range_table = &range_unknown;
1585 s->insn_read = pci171x_insn_counter_read;
1586 s->insn_write = pci171x_insn_counter_write;
1587 s->insn_config = pci171x_insn_counter_config;
1588 subdev++;
1589 }
1590
1591 devpriv->valid = 1;
1592
1593 return 0;
1594}
1595
1596/*
1597==============================================================================
1598*/
da91b269 1599static int pci1710_detach(struct comedi_device *dev)
0e8db97a
MD
1600{
1601
1602 if (dev->private) {
1603 if (devpriv->valid)
1604 pci1710_reset(dev);
1605 if (dev->irq)
5f74ea14 1606 free_irq(dev->irq, dev);
0e8db97a 1607 if (devpriv->pcidev) {
ec14016e 1608 if (dev->iobase)
0e8db97a 1609 comedi_pci_disable(devpriv->pcidev);
ec14016e 1610
0e8db97a
MD
1611 pci_dev_put(devpriv->pcidev);
1612 }
1613 }
1614
1615 return 0;
1616}
1617
1618/*
1619==============================================================================
1620*/
727b286b
AT
1621static int __devinit driver_pci1710_pci_probe(struct pci_dev *dev,
1622 const struct pci_device_id *ent)
1623{
1624 return comedi_pci_auto_config(dev, driver_pci1710.driver_name);
1625}
1626
1627static void __devexit driver_pci1710_pci_remove(struct pci_dev *dev)
1628{
1629 comedi_pci_auto_unconfig(dev);
1630}
1631
1632static struct pci_driver driver_pci1710_pci_driver = {
1633 .id_table = pci1710_pci_table,
1634 .probe = &driver_pci1710_pci_probe,
1635 .remove = __devexit_p(&driver_pci1710_pci_remove)
1636};
1637
1638static int __init driver_pci1710_init_module(void)
1639{
1640 int retval;
1641
1642 retval = comedi_driver_register(&driver_pci1710);
1643 if (retval < 0)
1644 return retval;
1645
1646 driver_pci1710_pci_driver.name = (char *)driver_pci1710.driver_name;
1647 return pci_register_driver(&driver_pci1710_pci_driver);
1648}
1649
1650static void __exit driver_pci1710_cleanup_module(void)
1651{
1652 pci_unregister_driver(&driver_pci1710_pci_driver);
1653 comedi_driver_unregister(&driver_pci1710);
1654}
1655
1656module_init(driver_pci1710_init_module);
1657module_exit(driver_pci1710_cleanup_module);
0e8db97a
MD
1658/*
1659==============================================================================
1660*/
90f703d3
AT
1661
1662MODULE_AUTHOR("Comedi http://www.comedi.org");
1663MODULE_DESCRIPTION("Comedi low-level driver");
1664MODULE_LICENSE("GPL");