staging: comedi: pcl818: only init async command members when needed
[deliverable/linux.git] / drivers / staging / comedi / drivers / pcl818.c
CommitLineData
4da6a1d8
MD
1/*
2 comedi/drivers/pcl818.c
3
4 Author: Michal Dobes <dobes@tesnet.cz>
5
6 hardware driver for Advantech cards:
7 card: PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818, PCL-718
8 driver: pcl818l, pcl818h, pcl818hd, pcl818hg, pcl818, pcl718
9*/
10/*
11Driver: pcl818
12Description: Advantech PCL-818 cards, PCL-718
13Author: Michal Dobes <dobes@tesnet.cz>
14Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
15 PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
16 PCL-718 (pcl718)
17Status: works
18
19All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
20Differences are only at maximal sample speed, range list and FIFO
21support.
22The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
23only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
24PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
25but this code is untested.
26A word or two about DMA. Driver support DMA operations at two ways:
271) DMA uses two buffers and after one is filled then is generated
28 INT and DMA restart with second buffer. With this mode I'm unable run
29 more that 80Ksamples/secs without data dropouts on K6/233.
302) DMA uses one buffer and run in autoinit mode and the data are
31 from DMA buffer moved on the fly with 2kHz interrupts from RTC.
32 This mode is used if the interrupt 8 is available for allocation.
33 If not, then first DMA mode is used. With this I can run at
34 full speed one card (100ksamples/secs) or two cards with
35 60ksamples/secs each (more is problem on account of ISA limitations).
36 To use this mode you must have compiled kernel with disabled
37 "Enhanced Real Time Clock Support".
38 Maybe you can have problems if you use xntpd or similar.
39 If you've data dropouts with DMA mode 2 then:
40 a) disable IDE DMA
41 b) switch text mode console to fb.
42
43 Options for PCL-818L:
44 [0] - IO Base
45 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
46 [2] - DMA (0=disable, 1, 3)
47 [3] - 0, 10=10MHz clock for 8254
48 1= 1MHz clock for 8254
49 [4] - 0, 5=A/D input -5V.. +5V
50 1, 10=A/D input -10V..+10V
51 [5] - 0, 5=D/A output 0-5V (internal reference -5V)
52 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 53 2 =D/A output unknown (external reference)
4da6a1d8
MD
54
55 Options for PCL-818, PCL-818H:
56 [0] - IO Base
57 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
58 [2] - DMA (0=disable, 1, 3)
59 [3] - 0, 10=10MHz clock for 8254
60 1= 1MHz clock for 8254
61 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
62 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 63 2 =D/A output unknown (external reference)
4da6a1d8
MD
64
65 Options for PCL-818HD, PCL-818HG:
66 [0] - IO Base
67 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
68 [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
69 1=use DMA ch 1, 3=use DMA ch 3)
70 [3] - 0, 10=10MHz clock for 8254
71 1= 1MHz clock for 8254
72 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
73 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 74 2 =D/A output unknown (external reference)
4da6a1d8
MD
75
76 Options for PCL-718:
77 [0] - IO Base
78 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
79 [2] - DMA (0=disable, 1, 3)
80 [3] - 0, 10=10MHz clock for 8254
81 1= 1MHz clock for 8254
82 [4] - 0=A/D Range is +/-10V
83 1= +/-5V
84 2= +/-2.5V
85 3= +/-1V
86 4= +/-0.5V
87 5= user defined bipolar
88 6= 0-10V
89 7= 0-5V
90 8= 0-2V
91 9= 0-1V
92 10= user defined unipolar
93 [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
94 1, 10=D/A outputs 0-10V (internal reference -10V)
bbc9a991 95 2=D/A outputs unknown (external reference)
4da6a1d8
MD
96 [6] - 0, 60=max 60kHz A/D sampling
97 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
98
99*/
100
ce157f80 101#include <linux/module.h>
5a0e3ad6 102#include <linux/gfp.h>
4da6a1d8 103#include <linux/delay.h>
845d131e 104#include <linux/io.h>
aecfd1ec 105#include <linux/interrupt.h>
4da6a1d8
MD
106#include <asm/dma.h>
107
aecfd1ec
HS
108#include "../comedidev.h"
109
27020ffe 110#include "comedi_fc.h"
4da6a1d8
MD
111#include "8253.h"
112
0109253d 113/* #define PCL818_MODE13_AO 1 */
4da6a1d8 114
0109253d 115/* boards constants */
4da6a1d8
MD
116
117#define boardPCL818L 0
118#define boardPCL818H 1
119#define boardPCL818HD 2
120#define boardPCL818HG 3
121#define boardPCL818 4
122#define boardPCL718 5
123
0109253d 124/* IO space len */
4da6a1d8 125#define PCLx1x_RANGE 16
0109253d 126/* IO space len if we use FIFO */
4da6a1d8
MD
127#define PCLx1xFIFO_RANGE 32
128
0109253d 129/* W: clear INT request */
4da6a1d8 130#define PCL818_CLRINT 8
0109253d 131/* R: return status byte */
4da6a1d8 132#define PCL818_STATUS 8
0109253d 133/* R: A/D high byte W: A/D range control */
4da6a1d8 134#define PCL818_RANGE 1
0109253d 135/* R: next mux scan channel W: mux scan channel & range control pointer */
4da6a1d8 136#define PCL818_MUX 2
0109253d 137/* R/W: operation control register */
4da6a1d8 138#define PCL818_CONTROL 9
0109253d 139/* W: counter enable */
4da6a1d8
MD
140#define PCL818_CNTENABLE 10
141
0109253d 142/* R: low byte of A/D W: soft A/D trigger */
4da6a1d8 143#define PCL818_AD_LO 0
0109253d 144/* R: high byte of A/D W: A/D range control */
4da6a1d8 145#define PCL818_AD_HI 1
0109253d 146/* W: D/A low&high byte */
4da6a1d8
MD
147#define PCL818_DA_LO 4
148#define PCL818_DA_HI 5
0109253d 149/* R: low&high byte of DI */
4da6a1d8
MD
150#define PCL818_DI_LO 3
151#define PCL818_DI_HI 11
0109253d 152/* W: low&high byte of DO */
4da6a1d8
MD
153#define PCL818_DO_LO 3
154#define PCL818_DO_HI 11
0109253d 155/* W: PCL718 second D/A */
4da6a1d8
MD
156#define PCL718_DA2_LO 6
157#define PCL718_DA2_HI 7
0109253d 158/* counters */
4da6a1d8
MD
159#define PCL818_CTR0 12
160#define PCL818_CTR1 13
161#define PCL818_CTR2 14
0109253d 162/* W: counter control */
4da6a1d8
MD
163#define PCL818_CTRCTL 15
164
0109253d 165/* W: fifo enable/disable */
4da6a1d8 166#define PCL818_FI_ENABLE 6
0109253d 167/* W: fifo interrupt clear */
4da6a1d8 168#define PCL818_FI_INTCLR 20
0109253d 169/* W: fifo interrupt clear */
4da6a1d8 170#define PCL818_FI_FLUSH 25
0109253d 171/* R: fifo status */
4da6a1d8 172#define PCL818_FI_STATUS 25
0109253d 173/* R: one record from FIFO */
4da6a1d8
MD
174#define PCL818_FI_DATALO 23
175#define PCL818_FI_DATAHI 23
176
0109253d 177/* type of interrupt handler */
4da6a1d8
MD
178#define INT_TYPE_AI1_INT 1
179#define INT_TYPE_AI1_DMA 2
180#define INT_TYPE_AI1_FIFO 3
181#define INT_TYPE_AI3_INT 4
182#define INT_TYPE_AI3_DMA 5
183#define INT_TYPE_AI3_FIFO 6
184#ifdef PCL818_MODE13_AO
185#define INT_TYPE_AO1_INT 7
186#define INT_TYPE_AO3_INT 8
187#endif
188
4da6a1d8
MD
189#define MAGIC_DMA_WORD 0x5a5a
190
9ced1de6 191static const struct comedi_lrange range_pcl818h_ai = { 9, {
0a85b6f0
MT
192 BIP_RANGE(5),
193 BIP_RANGE(2.5),
194 BIP_RANGE(1.25),
195 BIP_RANGE(0.625),
196 UNI_RANGE(10),
197 UNI_RANGE(5),
198 UNI_RANGE(2.5),
199 UNI_RANGE(1.25),
200 BIP_RANGE(10),
201 }
4da6a1d8
MD
202};
203
9ced1de6 204static const struct comedi_lrange range_pcl818hg_ai = { 10, {
0a85b6f0
MT
205 BIP_RANGE(5),
206 BIP_RANGE(0.5),
207 BIP_RANGE(0.05),
208 BIP_RANGE(0.005),
209 UNI_RANGE(10),
210 UNI_RANGE(1),
211 UNI_RANGE(0.1),
212 UNI_RANGE(0.01),
213 BIP_RANGE(10),
214 BIP_RANGE(1),
215 BIP_RANGE(0.1),
216 BIP_RANGE(0.01),
217 }
4da6a1d8
MD
218};
219
9ced1de6 220static const struct comedi_lrange range_pcl818l_l_ai = { 4, {
0a85b6f0
MT
221 BIP_RANGE(5),
222 BIP_RANGE(2.5),
223 BIP_RANGE(1.25),
224 BIP_RANGE(0.625),
225 }
4da6a1d8
MD
226};
227
9ced1de6 228static const struct comedi_lrange range_pcl818l_h_ai = { 4, {
0a85b6f0
MT
229 BIP_RANGE(10),
230 BIP_RANGE(5),
231 BIP_RANGE(2.5),
232 BIP_RANGE(1.25),
233 }
4da6a1d8
MD
234};
235
9ced1de6 236static const struct comedi_lrange range718_bipolar1 = { 1, {BIP_RANGE(1),} };
74c7c503
JC
237static const struct comedi_lrange range718_bipolar0_5 = {
238 1, {BIP_RANGE(0.5),} };
9ced1de6
BP
239static const struct comedi_lrange range718_unipolar2 = { 1, {UNI_RANGE(2),} };
240static const struct comedi_lrange range718_unipolar1 = { 1, {BIP_RANGE(1),} };
4da6a1d8 241
4634b815
BP
242struct pcl818_board {
243
0109253d
BP
244 const char *name; /* driver name */
245 int n_ranges; /* len of range list */
246 int n_aichan_se; /* num of A/D chans in single ended mode */
247 int n_aichan_diff; /* num of A/D chans in diferencial mode */
39eaedb6 248 unsigned int ns_min; /* minimal allowed delay between samples (in ns) */
0109253d
BP
249 int n_aochan; /* num of D/A chans */
250 int n_dichan; /* num of DI chans */
251 int n_dochan; /* num of DO chans */
252 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
253 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
254 unsigned int io_range; /* len of IO space */
255 unsigned int IRQbits; /* allowed interrupts */
256 unsigned int DMAbits; /* allowed DMA chans */
257 int ai_maxdata; /* maxdata for A/D */
258 int ao_maxdata; /* maxdata for D/A */
259 unsigned char fifo; /* 1=board has FIFO */
4da6a1d8 260 int is_818;
4634b815
BP
261};
262
087ea31b
BP
263struct pcl818_private {
264
0109253d 265 unsigned int dma; /* used DMA, 0=don't use DMA */
4da6a1d8 266 unsigned int io_range;
0109253d
BP
267 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
268 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
269 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
270 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
0109253d
BP
271 int next_dma_buf; /* which DMA buffer will be used next round */
272 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
273 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
274 unsigned char neverending_ai; /* if=1, then we do neverending record (you must use cancel()) */
39eaedb6 275 unsigned int ns_min; /* manimal allowed delay between samples (in us) for actual card */
0109253d 276 int i8253_osc_base; /* 1/frequency of on board oscilator in ns */
0109253d
BP
277 int irq_blocked; /* 1=IRQ now uses any subdev */
278 int irq_was_now_closed; /* when IRQ finish, there's stored int818_mode for last interrupt */
279 int ai_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
280 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
281 int ai_act_scan; /* how many scans we finished */
282 int ai_act_chan; /* actual position in actual scan */
283 unsigned int act_chanlist[16]; /* MUX setting for actual AI operations */
284 unsigned int act_chanlist_len; /* how long is actual MUX list */
285 unsigned int act_chanlist_pos; /* actual position in MUX list */
286 unsigned int ai_scans; /* len of scanlist */
287 unsigned int ai_n_chan; /* how many channels is measured */
288 unsigned int *ai_chanlist; /* actaul chanlist */
289 unsigned int ai_flags; /* flaglist */
290 unsigned int ai_data_len; /* len of data buffer */
0109253d 291 unsigned int ai_timer1; /* timers */
4da6a1d8 292 unsigned int ai_timer2;
0109253d
BP
293 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
294 unsigned char usefifo; /* 1=use fifo */
790c5541 295 unsigned int ao_readback[2];
087ea31b
BP
296};
297
0109253d 298static const unsigned int muxonechan[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, /* used for gain list programming */
4da6a1d8
MD
299 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
300};
301
4da6a1d8
MD
302/*
303==============================================================================
304*/
0a85b6f0
MT
305static void setup_channel_list(struct comedi_device *dev,
306 struct comedi_subdevice *s,
307 unsigned int *chanlist, unsigned int n_chan,
308 unsigned int seglen);
309static int check_channel_list(struct comedi_device *dev,
310 struct comedi_subdevice *s,
311 unsigned int *chanlist, unsigned int n_chan);
312
313static int pcl818_ai_cancel(struct comedi_device *dev,
314 struct comedi_subdevice *s);
315static void start_pacer(struct comedi_device *dev, int mode,
316 unsigned int divisor1, unsigned int divisor2);
4da6a1d8 317
4da6a1d8
MD
318/*
319==============================================================================
320 ANALOG INPUT MODE0, 818 cards, slow version
321*/
0a85b6f0
MT
322static int pcl818_ai_insn_read(struct comedi_device *dev,
323 struct comedi_subdevice *s,
324 struct comedi_insn *insn, unsigned int *data)
4da6a1d8
MD
325{
326 int n;
327 int timeout;
328
329 /* software trigger, DMA and INT off */
330 outb(0, dev->iobase + PCL818_CONTROL);
331
332 /* select channel */
333 outb(muxonechan[CR_CHAN(insn->chanspec)], dev->iobase + PCL818_MUX);
334
335 /* select gain */
336 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL818_RANGE);
337
338 for (n = 0; n < insn->n; n++) {
339
340 /* clear INT (conversion end) flag */
341 outb(0, dev->iobase + PCL818_CLRINT);
342
343 /* start conversion */
344 outb(0, dev->iobase + PCL818_AD_LO);
345
346 timeout = 100;
347 while (timeout--) {
348 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
349 goto conv_finish;
5f74ea14 350 udelay(1);
4da6a1d8
MD
351 }
352 comedi_error(dev, "A/D insn timeout");
353 /* clear INT (conversion end) flag */
354 outb(0, dev->iobase + PCL818_CLRINT);
355 return -EIO;
356
0a85b6f0 357conv_finish:
4da6a1d8 358 data[n] = ((inb(dev->iobase + PCL818_AD_HI) << 4) |
0a85b6f0 359 (inb(dev->iobase + PCL818_AD_LO) >> 4));
4da6a1d8
MD
360 }
361
362 return n;
363}
364
365/*
366==============================================================================
367 ANALOG OUTPUT MODE0, 818 cards
368 only one sample per call is supported
369*/
0a85b6f0
MT
370static int pcl818_ao_insn_read(struct comedi_device *dev,
371 struct comedi_subdevice *s,
372 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 373{
9a1a6cf8 374 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
375 int n;
376 int chan = CR_CHAN(insn->chanspec);
377
fc950139 378 for (n = 0; n < insn->n; n++)
4da6a1d8 379 data[n] = devpriv->ao_readback[chan];
4da6a1d8
MD
380
381 return n;
382}
383
0a85b6f0
MT
384static int pcl818_ao_insn_write(struct comedi_device *dev,
385 struct comedi_subdevice *s,
386 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 387{
9a1a6cf8 388 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
389 int n;
390 int chan = CR_CHAN(insn->chanspec);
391
392 for (n = 0; n < insn->n; n++) {
393 devpriv->ao_readback[chan] = data[n];
394 outb((data[n] & 0x000f) << 4, dev->iobase +
0a85b6f0 395 (chan ? PCL718_DA2_LO : PCL818_DA_LO));
4da6a1d8 396 outb((data[n] & 0x0ff0) >> 4, dev->iobase +
0a85b6f0 397 (chan ? PCL718_DA2_HI : PCL818_DA_HI));
4da6a1d8
MD
398 }
399
400 return n;
401}
402
403/*
404==============================================================================
405 DIGITAL INPUT MODE0, 818 cards
406
407 only one sample per call is supported
408*/
0a85b6f0
MT
409static int pcl818_di_insn_bits(struct comedi_device *dev,
410 struct comedi_subdevice *s,
411 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 412{
4da6a1d8 413 data[1] = inb(dev->iobase + PCL818_DI_LO) |
0a85b6f0 414 (inb(dev->iobase + PCL818_DI_HI) << 8);
4da6a1d8 415
a2714e3e 416 return insn->n;
4da6a1d8
MD
417}
418
0a85b6f0
MT
419static int pcl818_do_insn_bits(struct comedi_device *dev,
420 struct comedi_subdevice *s,
97f4289a
HS
421 struct comedi_insn *insn,
422 unsigned int *data)
4da6a1d8 423{
97f4289a
HS
424 if (comedi_dio_update_state(s, data)) {
425 outb(s->state & 0xff, dev->iobase + PCL818_DO_LO);
426 outb((s->state >> 8), dev->iobase + PCL818_DO_HI);
427 }
4da6a1d8
MD
428
429 data[1] = s->state;
430
a2714e3e 431 return insn->n;
4da6a1d8
MD
432}
433
434/*
435==============================================================================
436 analog input interrupt mode 1 & 3, 818 cards
437 one sample per interrupt version
438*/
439static irqreturn_t interrupt_pcl818_ai_mode13_int(int irq, void *d)
440{
71b5f4f1 441 struct comedi_device *dev = d;
9a1a6cf8 442 struct pcl818_private *devpriv = dev->private;
9fab6123 443 struct comedi_subdevice *s = &dev->subdevices[0];
4bf59ce2 444 unsigned char low;
4da6a1d8
MD
445 int timeout = 50; /* wait max 50us */
446
447 while (timeout--) {
448 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
449 goto conv_finish;
5f74ea14 450 udelay(1);
4da6a1d8
MD
451 }
452 outb(0, dev->iobase + PCL818_STATUS); /* clear INT request */
453 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
454 pcl818_ai_cancel(dev, s);
455 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
456 comedi_event(dev, s);
457 return IRQ_HANDLED;
458
0a85b6f0 459conv_finish:
4da6a1d8 460 low = inb(dev->iobase + PCL818_AD_LO);
0109253d 461 comedi_buf_put(s->async, ((inb(dev->iobase + PCL818_AD_HI) << 4) | (low >> 4))); /* get one sample */
4da6a1d8
MD
462 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
463
0109253d 464 if ((low & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
d65e5b9d
HS
465 dev_dbg(dev->class_dev,
466 "A/D mode1/3 IRQ - channel dropout %x!=%x !\n",
467 (low & 0xf),
468 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
4da6a1d8
MD
469 pcl818_ai_cancel(dev, s);
470 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
471 comedi_event(dev, s);
472 return IRQ_HANDLED;
473 }
b3559cb1 474 devpriv->act_chanlist_pos++;
fc950139 475 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
b3559cb1 476 devpriv->act_chanlist_pos = 0;
fc950139 477
b3559cb1
IA
478 s->async->cur_chan++;
479 if (s->async->cur_chan >= devpriv->ai_n_chan) {
b3559cb1 480 s->async->cur_chan = 0;
4da6a1d8
MD
481 devpriv->ai_act_scan--;
482 }
483
484 if (!devpriv->neverending_ai) {
485 if (devpriv->ai_act_scan == 0) { /* all data sampled */
486 pcl818_ai_cancel(dev, s);
487 s->async->events |= COMEDI_CB_EOA;
488 }
489 }
490 comedi_event(dev, s);
491 return IRQ_HANDLED;
492}
493
494/*
495==============================================================================
496 analog input dma mode 1 & 3, 818 cards
497*/
498static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
499{
71b5f4f1 500 struct comedi_device *dev = d;
9a1a6cf8 501 struct pcl818_private *devpriv = dev->private;
9fab6123 502 struct comedi_subdevice *s = &dev->subdevices[0];
4da6a1d8
MD
503 int i, len, bufptr;
504 unsigned long flags;
4bf59ce2 505 unsigned short *ptr;
4da6a1d8
MD
506
507 disable_dma(devpriv->dma);
508 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
0109253d 509 if ((devpriv->dma_runs_to_end) > -1 || devpriv->neverending_ai) { /* switch dma bufs */
4da6a1d8
MD
510 set_dma_mode(devpriv->dma, DMA_MODE_READ);
511 flags = claim_dma_lock();
512 set_dma_addr(devpriv->dma,
0a85b6f0 513 devpriv->hwdmaptr[devpriv->next_dma_buf]);
4da6a1d8
MD
514 if (devpriv->dma_runs_to_end || devpriv->neverending_ai) {
515 set_dma_count(devpriv->dma,
0a85b6f0
MT
516 devpriv->hwdmasize[devpriv->
517 next_dma_buf]);
4da6a1d8
MD
518 } else {
519 set_dma_count(devpriv->dma, devpriv->last_dma_run);
520 }
521 release_dma_lock(flags);
522 enable_dma(devpriv->dma);
523 }
4da6a1d8
MD
524
525 devpriv->dma_runs_to_end--;
526 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
4bf59ce2 527 ptr = (unsigned short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
4da6a1d8
MD
528
529 len = devpriv->hwdmasize[0] >> 1;
530 bufptr = 0;
531
532 for (i = 0; i < len; i++) {
0109253d 533 if ((ptr[bufptr] & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
d65e5b9d
HS
534 dev_dbg(dev->class_dev,
535 "A/D mode1/3 DMA - channel dropout %d(card)!=%d(chanlist) at %d !\n",
536 (ptr[bufptr] & 0xf),
537 devpriv->act_chanlist[devpriv->act_chanlist_pos],
538 devpriv->act_chanlist_pos);
4da6a1d8
MD
539 pcl818_ai_cancel(dev, s);
540 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
541 comedi_event(dev, s);
542 return IRQ_HANDLED;
543 }
544
0109253d 545 comedi_buf_put(s->async, ptr[bufptr++] >> 4); /* get one sample */
4da6a1d8
MD
546
547 devpriv->act_chanlist_pos++;
fc950139 548 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
4da6a1d8 549 devpriv->act_chanlist_pos = 0;
fc950139 550
b3559cb1
IA
551 s->async->cur_chan++;
552 if (s->async->cur_chan >= devpriv->ai_n_chan) {
553 s->async->cur_chan = 0;
554 devpriv->ai_act_scan--;
555 }
4da6a1d8
MD
556
557 if (!devpriv->neverending_ai)
558 if (devpriv->ai_act_scan == 0) { /* all data sampled */
559 pcl818_ai_cancel(dev, s);
560 s->async->events |= COMEDI_CB_EOA;
561 comedi_event(dev, s);
4da6a1d8
MD
562 return IRQ_HANDLED;
563 }
564 }
565
566 if (len > 0)
567 comedi_event(dev, s);
568 return IRQ_HANDLED;
569}
570
4da6a1d8
MD
571/*
572==============================================================================
573 analog input interrupt mode 1 & 3, 818HD/HG cards
574*/
575static irqreturn_t interrupt_pcl818_ai_mode13_fifo(int irq, void *d)
576{
71b5f4f1 577 struct comedi_device *dev = d;
9a1a6cf8 578 struct pcl818_private *devpriv = dev->private;
9fab6123 579 struct comedi_subdevice *s = &dev->subdevices[0];
4bf59ce2
IA
580 int i, len;
581 unsigned char lo;
4da6a1d8 582
0109253d 583 outb(0, dev->iobase + PCL818_FI_INTCLR); /* clear fifo int request */
4da6a1d8
MD
584
585 lo = inb(dev->iobase + PCL818_FI_STATUS);
586
587 if (lo & 4) {
588 comedi_error(dev, "A/D mode1/3 FIFO overflow!");
589 pcl818_ai_cancel(dev, s);
590 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
591 comedi_event(dev, s);
592 return IRQ_HANDLED;
593 }
594
595 if (lo & 1) {
596 comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
597 pcl818_ai_cancel(dev, s);
598 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
599 comedi_event(dev, s);
600 return IRQ_HANDLED;
601 }
602
fc950139 603 if (lo & 2)
4da6a1d8 604 len = 512;
fc950139 605 else
4da6a1d8 606 len = 0;
4da6a1d8
MD
607
608 for (i = 0; i < len; i++) {
609 lo = inb(dev->iobase + PCL818_FI_DATALO);
0109253d 610 if ((lo & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
d65e5b9d
HS
611 dev_dbg(dev->class_dev,
612 "A/D mode1/3 FIFO - channel dropout %d!=%d !\n",
613 (lo & 0xf),
614 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
4da6a1d8
MD
615 pcl818_ai_cancel(dev, s);
616 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
617 comedi_event(dev, s);
618 return IRQ_HANDLED;
619 }
620
0109253d 621 comedi_buf_put(s->async, (lo >> 4) | (inb(dev->iobase + PCL818_FI_DATAHI) << 4)); /* get one sample */
4da6a1d8 622
b3559cb1 623 devpriv->act_chanlist_pos++;
fc950139 624 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
b3559cb1 625 devpriv->act_chanlist_pos = 0;
fc950139 626
b3559cb1
IA
627 s->async->cur_chan++;
628 if (s->async->cur_chan >= devpriv->ai_n_chan) {
629 s->async->cur_chan = 0;
4da6a1d8
MD
630 devpriv->ai_act_scan--;
631 }
632
633 if (!devpriv->neverending_ai)
634 if (devpriv->ai_act_scan == 0) { /* all data sampled */
635 pcl818_ai_cancel(dev, s);
636 s->async->events |= COMEDI_CB_EOA;
637 comedi_event(dev, s);
638 return IRQ_HANDLED;
639 }
640 }
641
642 if (len > 0)
643 comedi_event(dev, s);
644 return IRQ_HANDLED;
645}
646
647/*
648==============================================================================
649 INT procedure
650*/
70265d24 651static irqreturn_t interrupt_pcl818(int irq, void *d)
4da6a1d8 652{
71b5f4f1 653 struct comedi_device *dev = d;
9a1a6cf8 654 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
655
656 if (!dev->attached) {
657 comedi_error(dev, "premature interrupt");
658 return IRQ_HANDLED;
659 }
4da6a1d8 660
e21de1a8
IA
661 if (devpriv->irq_blocked && devpriv->irq_was_now_closed) {
662 if ((devpriv->neverending_ai || (!devpriv->neverending_ai &&
0a85b6f0
MT
663 devpriv->ai_act_scan > 0)) &&
664 (devpriv->ai_mode == INT_TYPE_AI1_DMA ||
665 devpriv->ai_mode == INT_TYPE_AI3_DMA)) {
e21de1a8
IA
666 /* The cleanup from ai_cancel() has been delayed
667 until now because the card doesn't seem to like
668 being reprogrammed while a DMA transfer is in
669 progress.
670 */
9fab6123 671 struct comedi_subdevice *s = &dev->subdevices[0];
e21de1a8
IA
672 devpriv->ai_act_scan = 0;
673 devpriv->neverending_ai = 0;
674 pcl818_ai_cancel(dev, s);
675 }
676
677 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
678
679 return IRQ_HANDLED;
680 }
681
4da6a1d8
MD
682 switch (devpriv->ai_mode) {
683 case INT_TYPE_AI1_DMA:
684 case INT_TYPE_AI3_DMA:
685 return interrupt_pcl818_ai_mode13_dma(irq, d);
686 case INT_TYPE_AI1_INT:
687 case INT_TYPE_AI3_INT:
688 return interrupt_pcl818_ai_mode13_int(irq, d);
689 case INT_TYPE_AI1_FIFO:
690 case INT_TYPE_AI3_FIFO:
691 return interrupt_pcl818_ai_mode13_fifo(irq, d);
692#ifdef PCL818_MODE13_AO
693 case INT_TYPE_AO1_INT:
694 case INT_TYPE_AO3_INT:
695 return interrupt_pcl818_ao_mode13_int(irq, d);
696#endif
697 default:
698 break;
699 }
700
701 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
702
1dcea26a 703 if (!devpriv->irq_blocked || !devpriv->ai_mode) {
4da6a1d8
MD
704 comedi_error(dev, "bad IRQ!");
705 return IRQ_NONE;
706 }
707
bbc9a991 708 comedi_error(dev, "IRQ from unknown source!");
4da6a1d8
MD
709 return IRQ_NONE;
710}
711
712/*
713==============================================================================
714 ANALOG INPUT MODE 1 or 3 DMA , 818 cards
715*/
da91b269 716static void pcl818_ai_mode13dma_int(int mode, struct comedi_device *dev,
0a85b6f0 717 struct comedi_subdevice *s)
4da6a1d8 718{
9a1a6cf8 719 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
720 unsigned int flags;
721 unsigned int bytes;
722
0109253d 723 disable_dma(devpriv->dma); /* disable dma */
4da6a1d8
MD
724 bytes = devpriv->hwdmasize[0];
725 if (!devpriv->neverending_ai) {
0109253d
BP
726 bytes = devpriv->ai_n_chan * devpriv->ai_scans * sizeof(short); /* how many */
727 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fiil */
728 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
4da6a1d8
MD
729 devpriv->dma_runs_to_end--;
730 if (devpriv->dma_runs_to_end >= 0)
731 bytes = devpriv->hwdmasize[0];
732 }
733
734 devpriv->next_dma_buf = 0;
735 set_dma_mode(devpriv->dma, DMA_MODE_READ);
736 flags = claim_dma_lock();
737 clear_dma_ff(devpriv->dma);
738 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
739 set_dma_count(devpriv->dma, bytes);
740 release_dma_lock(flags);
741 enable_dma(devpriv->dma);
742
743 if (mode == 1) {
744 devpriv->ai_mode = INT_TYPE_AI1_DMA;
745 outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+IRQ+DMA */
746 } else {
747 devpriv->ai_mode = INT_TYPE_AI3_DMA;
748 outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+IRQ+DMA */
4b5c08e8 749 }
4da6a1d8
MD
750}
751
4da6a1d8
MD
752/*
753==============================================================================
754 ANALOG INPUT MODE 1 or 3, 818 cards
755*/
da91b269 756static int pcl818_ai_cmd_mode(int mode, struct comedi_device *dev,
0a85b6f0 757 struct comedi_subdevice *s)
4da6a1d8 758{
9a1a6cf8 759 struct pcl818_private *devpriv = dev->private;
ea6d0d4c 760 struct comedi_cmd *cmd = &s->async->cmd;
48b1aff5 761 int divisor1 = 0, divisor2 = 0;
4da6a1d8
MD
762 unsigned int seglen;
763
4da6a1d8
MD
764 if (devpriv->irq_blocked)
765 return -EBUSY;
766
0109253d 767 start_pacer(dev, -1, 0, 0); /* stop pacer */
4da6a1d8
MD
768
769 seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
0a85b6f0 770 devpriv->ai_n_chan);
4da6a1d8
MD
771 if (seglen < 1)
772 return -EINVAL;
773 setup_channel_list(dev, s, devpriv->ai_chanlist,
0a85b6f0 774 devpriv->ai_n_chan, seglen);
4da6a1d8 775
5f74ea14 776 udelay(1);
4da6a1d8
MD
777
778 devpriv->ai_act_scan = devpriv->ai_scans;
779 devpriv->ai_act_chan = 0;
780 devpriv->irq_blocked = 1;
781 devpriv->irq_was_now_closed = 0;
782 devpriv->neverending_ai = 0;
783 devpriv->act_chanlist_pos = 0;
784 devpriv->dma_runs_to_end = 0;
785
786 if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
0109253d 787 devpriv->neverending_ai = 1; /* well, user want neverending */
4da6a1d8
MD
788
789 if (mode == 1) {
cb9cfd7e
HS
790 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
791 &divisor1, &divisor2,
792 &cmd->convert_arg,
0a85b6f0 793 TRIG_ROUND_NEAREST);
4da6a1d8
MD
794 if (divisor1 == 1) { /* PCL718/818 crash if any divisor is set to 1 */
795 divisor1 = 2;
796 divisor2 /= 2;
797 }
798 if (divisor2 == 1) {
799 divisor2 = 2;
800 divisor1 /= 2;
801 }
802 }
803
804 outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
805
806 switch (devpriv->dma) {
0109253d 807 case 1: /* DMA */
4da6a1d8 808 case 3:
aecfd1ec 809 pcl818_ai_mode13dma_int(mode, dev, s);
4da6a1d8 810 break;
a71f18d2
IA
811 case 0:
812 if (!devpriv->usefifo) {
813 /* IRQ */
a71f18d2
IA
814 if (mode == 1) {
815 devpriv->ai_mode = INT_TYPE_AI1_INT;
816 /* Pacer+IRQ */
0a85b6f0
MT
817 outb(0x83 | (dev->irq << 4),
818 dev->iobase + PCL818_CONTROL);
a71f18d2
IA
819 } else {
820 devpriv->ai_mode = INT_TYPE_AI3_INT;
821 /* Ext trig+IRQ */
0a85b6f0
MT
822 outb(0x82 | (dev->irq << 4),
823 dev->iobase + PCL818_CONTROL);
a71f18d2 824 }
4da6a1d8 825 } else {
a71f18d2
IA
826 /* FIFO */
827 /* enable FIFO */
828 outb(1, dev->iobase + PCL818_FI_ENABLE);
829 if (mode == 1) {
830 devpriv->ai_mode = INT_TYPE_AI1_FIFO;
831 /* Pacer */
832 outb(0x03, dev->iobase + PCL818_CONTROL);
833 } else {
834 devpriv->ai_mode = INT_TYPE_AI3_FIFO;
835 outb(0x02, dev->iobase + PCL818_CONTROL);
836 }
837 }
4da6a1d8
MD
838 }
839
840 start_pacer(dev, mode, divisor1, divisor2);
841
4da6a1d8
MD
842 return 0;
843}
844
4da6a1d8
MD
845/*
846==============================================================================
847 Start/stop pacer onboard pacer
848*/
0a85b6f0
MT
849static void start_pacer(struct comedi_device *dev, int mode,
850 unsigned int divisor1, unsigned int divisor2)
4da6a1d8
MD
851{
852 outb(0xb4, dev->iobase + PCL818_CTRCTL);
853 outb(0x74, dev->iobase + PCL818_CTRCTL);
5f74ea14 854 udelay(1);
4da6a1d8
MD
855
856 if (mode == 1) {
857 outb(divisor2 & 0xff, dev->iobase + PCL818_CTR2);
858 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL818_CTR2);
859 outb(divisor1 & 0xff, dev->iobase + PCL818_CTR1);
860 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL818_CTR1);
861 }
862}
863
864/*
865==============================================================================
866 Check if channel list from user is builded correctly
867 If it's ok, then program scan/gain logic
868*/
0a85b6f0
MT
869static int check_channel_list(struct comedi_device *dev,
870 struct comedi_subdevice *s,
871 unsigned int *chanlist, unsigned int n_chan)
4da6a1d8
MD
872{
873 unsigned int chansegment[16];
874 unsigned int i, nowmustbechan, seglen, segpos;
875
876 /* correct channel and range number check itself comedi/range.c */
877 if (n_chan < 1) {
878 comedi_error(dev, "range/channel list is empty!");
879 return 0;
880 }
881
882 if (n_chan > 1) {
25985edc 883 /* first channel is every time ok */
4da6a1d8 884 chansegment[0] = chanlist[0];
0109253d 885 /* build part of chanlist */
4da6a1d8 886 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
0109253d
BP
887 /* we detect loop, this must by finish */
888
4da6a1d8
MD
889 if (chanlist[0] == chanlist[i])
890 break;
891 nowmustbechan =
0a85b6f0 892 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
25985edc 893 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
84f03cf1
HS
894 dev_dbg(dev->class_dev,
895 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
896 i, CR_CHAN(chanlist[i]), nowmustbechan,
897 CR_CHAN(chanlist[0]));
4da6a1d8
MD
898 return 0;
899 }
0109253d 900 /* well, this is next correct channel in list */
4da6a1d8
MD
901 chansegment[i] = chanlist[i];
902 }
903
0109253d 904 /* check whole chanlist */
4da6a1d8 905 for (i = 0, segpos = 0; i < n_chan; i++) {
4da6a1d8 906 if (chanlist[i] != chansegment[i % seglen]) {
84f03cf1
HS
907 dev_dbg(dev->class_dev,
908 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
909 i, CR_CHAN(chansegment[i]),
910 CR_RANGE(chansegment[i]),
911 CR_AREF(chansegment[i]),
912 CR_CHAN(chanlist[i % seglen]),
913 CR_RANGE(chanlist[i % seglen]),
914 CR_AREF(chansegment[i % seglen]));
0109253d 915 return 0; /* chan/gain list is strange */
4da6a1d8
MD
916 }
917 }
918 } else {
919 seglen = 1;
920 }
4da6a1d8
MD
921 return seglen;
922}
923
0a85b6f0
MT
924static void setup_channel_list(struct comedi_device *dev,
925 struct comedi_subdevice *s,
926 unsigned int *chanlist, unsigned int n_chan,
927 unsigned int seglen)
4da6a1d8 928{
9a1a6cf8 929 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
930 int i;
931
932 devpriv->act_chanlist_len = seglen;
933 devpriv->act_chanlist_pos = 0;
934
0109253d 935 for (i = 0; i < seglen; i++) { /* store range list to card */
4da6a1d8
MD
936 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
937 outb(muxonechan[CR_CHAN(chanlist[i])], dev->iobase + PCL818_MUX); /* select channel */
938 outb(CR_RANGE(chanlist[i]), dev->iobase + PCL818_RANGE); /* select gain */
939 }
940
5f74ea14 941 udelay(1);
4da6a1d8
MD
942
943 /* select channel interval to scan */
944 outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen -
0a85b6f0
MT
945 1] << 4),
946 dev->iobase + PCL818_MUX);
4da6a1d8
MD
947}
948
949/*
950==============================================================================
951 Check if board is switched to SE (1) or DIFF(0) mode
952*/
953static int check_single_ended(unsigned int port)
954{
fc950139 955 if (inb(port + PCL818_STATUS) & 0x20)
4da6a1d8 956 return 1;
fc950139 957 return 0;
4da6a1d8
MD
958}
959
960/*
961==============================================================================
962*/
da91b269 963static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 964 struct comedi_cmd *cmd)
4da6a1d8 965{
dd8a4b47 966 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 967 struct pcl818_private *devpriv = dev->private;
4da6a1d8 968 int err = 0;
48b1aff5 969 int tmp, divisor1 = 0, divisor2 = 0;
4da6a1d8 970
27020ffe 971 /* Step 1 : check if triggers are trivially valid */
4da6a1d8 972
27020ffe
HS
973 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
974 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
975 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
976 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
977 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
4da6a1d8 978
fc950139 979 if (err)
4da6a1d8 980 return 1;
4da6a1d8 981
27020ffe 982 /* Step 2a : make sure trigger sources are unique */
4da6a1d8 983
27020ffe
HS
984 err |= cfc_check_trigger_is_unique(cmd->convert_src);
985 err |= cfc_check_trigger_is_unique(cmd->stop_src);
4da6a1d8 986
27020ffe 987 /* Step 2b : and mutually compatible */
4da6a1d8 988
fc950139 989 if (err)
4da6a1d8 990 return 2;
4da6a1d8 991
8efdc1bf 992 /* Step 3: check if arguments are trivially valid */
4da6a1d8 993
8efdc1bf
HS
994 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
995 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
4da6a1d8 996
8efdc1bf
HS
997 if (cmd->convert_src == TRIG_TIMER)
998 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
999 board->ns_min);
1000 else /* TRIG_EXT */
1001 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
4da6a1d8 1002
8efdc1bf 1003 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
4da6a1d8 1004
8efdc1bf
HS
1005 if (cmd->stop_src == TRIG_COUNT)
1006 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
1007 else /* TRIG_NONE */
1008 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
4da6a1d8 1009
fc950139 1010 if (err)
4da6a1d8 1011 return 3;
4da6a1d8
MD
1012
1013 /* step 4: fix up any arguments */
1014
1015 if (cmd->convert_src == TRIG_TIMER) {
1016 tmp = cmd->convert_arg;
cb9cfd7e
HS
1017 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
1018 &divisor1, &divisor2,
1019 &cmd->convert_arg, cmd->flags);
dd8a4b47
HS
1020 if (cmd->convert_arg < board->ns_min)
1021 cmd->convert_arg = board->ns_min;
4da6a1d8
MD
1022 if (tmp != cmd->convert_arg)
1023 err++;
1024 }
1025
fc950139 1026 if (err)
4da6a1d8 1027 return 4;
4da6a1d8
MD
1028
1029 /* step 5: complain about special chanlist considerations */
1030
1031 if (cmd->chanlist) {
1032 if (!check_channel_list(dev, s, cmd->chanlist,
0a85b6f0 1033 cmd->chanlist_len))
0109253d 1034 return 5; /* incorrect channels list */
4da6a1d8
MD
1035 }
1036
1037 return 0;
1038}
1039
1040/*
1041==============================================================================
1042*/
da91b269 1043static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
4da6a1d8 1044{
9a1a6cf8 1045 struct pcl818_private *devpriv = dev->private;
ea6d0d4c 1046 struct comedi_cmd *cmd = &s->async->cmd;
4da6a1d8
MD
1047 int retval;
1048
4da6a1d8
MD
1049 devpriv->ai_n_chan = cmd->chanlist_len;
1050 devpriv->ai_chanlist = cmd->chanlist;
1051 devpriv->ai_flags = cmd->flags;
1052 devpriv->ai_data_len = s->async->prealloc_bufsz;
4da6a1d8
MD
1053 devpriv->ai_timer1 = 0;
1054 devpriv->ai_timer2 = 0;
1055
fc950139 1056 if (cmd->stop_src == TRIG_COUNT)
4da6a1d8 1057 devpriv->ai_scans = cmd->stop_arg;
fc950139 1058 else
4da6a1d8 1059 devpriv->ai_scans = 0;
4da6a1d8 1060
0109253d
BP
1061 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 3 */
1062 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 */
4da6a1d8
MD
1063 devpriv->ai_timer1 = cmd->convert_arg;
1064 retval = pcl818_ai_cmd_mode(1, dev, s);
4da6a1d8
MD
1065 return retval;
1066 }
0109253d 1067 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
4da6a1d8
MD
1068 return pcl818_ai_cmd_mode(3, dev, s);
1069 }
1070 }
1071
1072 return -1;
1073}
1074
1075/*
1076==============================================================================
1077 cancel any mode 1-4 AI
1078*/
0a85b6f0
MT
1079static int pcl818_ai_cancel(struct comedi_device *dev,
1080 struct comedi_subdevice *s)
4da6a1d8 1081{
9a1a6cf8
HS
1082 struct pcl818_private *devpriv = dev->private;
1083
4da6a1d8 1084 if (devpriv->irq_blocked > 0) {
e21de1a8 1085 devpriv->irq_was_now_closed = 1;
4da6a1d8 1086
e21de1a8 1087 switch (devpriv->ai_mode) {
4da6a1d8
MD
1088 case INT_TYPE_AI1_DMA:
1089 case INT_TYPE_AI3_DMA:
e21de1a8 1090 if (devpriv->neverending_ai ||
0a85b6f0
MT
1091 (!devpriv->neverending_ai &&
1092 devpriv->ai_act_scan > 0)) {
4da6a1d8
MD
1093 /* wait for running dma transfer to end, do cleanup in interrupt */
1094 goto end;
1095 }
1096 disable_dma(devpriv->dma);
1097 case INT_TYPE_AI1_INT:
1098 case INT_TYPE_AI3_INT:
1099 case INT_TYPE_AI1_FIFO:
1100 case INT_TYPE_AI3_FIFO:
1101#ifdef PCL818_MODE13_AO
1102 case INT_TYPE_AO1_INT:
1103 case INT_TYPE_AO3_INT:
1104#endif
1105 outb(inb(dev->iobase + PCL818_CONTROL) & 0x73, dev->iobase + PCL818_CONTROL); /* Stop A/D */
5f74ea14 1106 udelay(1);
4da6a1d8
MD
1107 start_pacer(dev, -1, 0, 0);
1108 outb(0, dev->iobase + PCL818_AD_LO);
1109 inb(dev->iobase + PCL818_AD_LO);
1110 inb(dev->iobase + PCL818_AD_HI);
1111 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
1112 outb(0, dev->iobase + PCL818_CONTROL); /* Stop A/D */
0109253d 1113 if (devpriv->usefifo) { /* FIFO shutdown */
4da6a1d8
MD
1114 outb(0, dev->iobase + PCL818_FI_INTCLR);
1115 outb(0, dev->iobase + PCL818_FI_FLUSH);
1116 outb(0, dev->iobase + PCL818_FI_ENABLE);
1117 }
1118 devpriv->irq_blocked = 0;
1119 devpriv->last_int_sub = s;
1120 devpriv->neverending_ai = 0;
e21de1a8
IA
1121 devpriv->ai_mode = 0;
1122 devpriv->irq_was_now_closed = 0;
4da6a1d8
MD
1123 break;
1124 }
1125 }
1126
0a85b6f0 1127end:
4da6a1d8
MD
1128 return 0;
1129}
1130
1131/*
1132==============================================================================
1133 chech for PCL818
1134*/
1135static int pcl818_check(unsigned long iobase)
1136{
1137 outb(0x00, iobase + PCL818_MUX);
5f74ea14 1138 udelay(1);
4da6a1d8 1139 if (inb(iobase + PCL818_MUX) != 0x00)
0109253d 1140 return 1; /* there isn't card */
4da6a1d8 1141 outb(0x55, iobase + PCL818_MUX);
5f74ea14 1142 udelay(1);
4da6a1d8 1143 if (inb(iobase + PCL818_MUX) != 0x55)
0109253d 1144 return 1; /* there isn't card */
4da6a1d8 1145 outb(0x00, iobase + PCL818_MUX);
5f74ea14 1146 udelay(1);
4da6a1d8 1147 outb(0x18, iobase + PCL818_CONTROL);
5f74ea14 1148 udelay(1);
4da6a1d8 1149 if (inb(iobase + PCL818_CONTROL) != 0x18)
0109253d
BP
1150 return 1; /* there isn't card */
1151 return 0; /* ok, card exist */
4da6a1d8
MD
1152}
1153
1154/*
1155==============================================================================
1156 reset whole PCL-818 cards
1157*/
da91b269 1158static void pcl818_reset(struct comedi_device *dev)
4da6a1d8 1159{
dd8a4b47 1160 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 1161 struct pcl818_private *devpriv = dev->private;
dd8a4b47 1162
0109253d 1163 if (devpriv->usefifo) { /* FIFO shutdown */
4da6a1d8
MD
1164 outb(0, dev->iobase + PCL818_FI_INTCLR);
1165 outb(0, dev->iobase + PCL818_FI_FLUSH);
1166 outb(0, dev->iobase + PCL818_FI_ENABLE);
1167 }
0109253d 1168 outb(0, dev->iobase + PCL818_DA_LO); /* DAC=0V */
4da6a1d8 1169 outb(0, dev->iobase + PCL818_DA_HI);
5f74ea14 1170 udelay(1);
0109253d 1171 outb(0, dev->iobase + PCL818_DO_HI); /* DO=$0000 */
4da6a1d8 1172 outb(0, dev->iobase + PCL818_DO_LO);
5f74ea14 1173 udelay(1);
4da6a1d8
MD
1174 outb(0, dev->iobase + PCL818_CONTROL);
1175 outb(0, dev->iobase + PCL818_CNTENABLE);
1176 outb(0, dev->iobase + PCL818_MUX);
1177 outb(0, dev->iobase + PCL818_CLRINT);
1178 outb(0xb0, dev->iobase + PCL818_CTRCTL); /* Stop pacer */
1179 outb(0x70, dev->iobase + PCL818_CTRCTL);
1180 outb(0x30, dev->iobase + PCL818_CTRCTL);
dd8a4b47 1181 if (board->is_818) {
4da6a1d8
MD
1182 outb(0, dev->iobase + PCL818_RANGE);
1183 } else {
1184 outb(0, dev->iobase + PCL718_DA2_LO);
1185 outb(0, dev->iobase + PCL718_DA2_HI);
1186 }
1187}
1188
da91b269 1189static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
4da6a1d8 1190{
dd8a4b47 1191 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 1192 struct pcl818_private *devpriv;
4da6a1d8 1193 int ret;
a71f18d2 1194 int dma;
4da6a1d8 1195 unsigned long pages;
34c43922 1196 struct comedi_subdevice *s;
4da6a1d8 1197
0bdab509 1198 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
1199 if (!devpriv)
1200 return -ENOMEM;
4da6a1d8 1201
dd8a4b47
HS
1202 devpriv->io_range = board->io_range;
1203 if ((board->fifo) && (it->options[2] == -1)) {
1204 /* we've board with FIFO and we want to use FIFO */
4da6a1d8
MD
1205 devpriv->io_range = PCLx1xFIFO_RANGE;
1206 devpriv->usefifo = 1;
1207 }
d6c5ec04
HS
1208 ret = comedi_request_region(dev, it->options[0], devpriv->io_range);
1209 if (ret)
1210 return ret;
4da6a1d8 1211
d6c5ec04 1212 if (pcl818_check(dev->iobase)) {
26ba666c 1213 comedi_error(dev, "I can't detect board. FAIL!\n");
4da6a1d8
MD
1214 return -EIO;
1215 }
1216
e30b22a9
HS
1217 if ((1 << it->options[1]) & board->IRQbits) {
1218 ret = request_irq(it->options[1], interrupt_pcl818, 0,
1219 dev->board_name, dev);
35a8735d 1220 if (ret == 0)
e30b22a9 1221 dev->irq = it->options[1];
4da6a1d8
MD
1222 }
1223
4da6a1d8
MD
1224 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1225 devpriv->ai_mode = 0; /* mode of irq */
1226
4da6a1d8
MD
1227 /* grab our DMA */
1228 dma = 0;
1229 devpriv->dma = dma;
35a8735d 1230 if (!dev->irq)
4da6a1d8 1231 goto no_dma; /* if we haven't IRQ, we can't use DMA */
dd8a4b47 1232 if (board->DMAbits != 0) { /* board support DMA */
4da6a1d8
MD
1233 dma = it->options[2];
1234 if (dma < 1)
1235 goto no_dma; /* DMA disabled */
dd8a4b47 1236 if (((1 << dma) & board->DMAbits) == 0) {
d65e5b9d
HS
1237 dev_err(dev->class_dev,
1238 "DMA is out of allowed range, FAIL!\n");
4da6a1d8
MD
1239 return -EINVAL; /* Bad DMA */
1240 }
b27cc413 1241 ret = request_dma(dma, dev->board_name);
408f6bcd 1242 if (ret)
4da6a1d8 1243 return -EBUSY; /* DMA isn't free */
4da6a1d8 1244 devpriv->dma = dma;
4da6a1d8
MD
1245 pages = 2; /* we need 16KB */
1246 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
408f6bcd 1247 if (!devpriv->dmabuf[0])
4da6a1d8
MD
1248 /* maybe experiment with try_to_free_pages() will help .... */
1249 return -EBUSY; /* no buffer :-( */
4da6a1d8
MD
1250 devpriv->dmapages[0] = pages;
1251 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1252 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
aecfd1ec
HS
1253 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1254 if (!devpriv->dmabuf[1])
1255 return -EBUSY;
1256 devpriv->dmapages[1] = pages;
1257 devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]);
1258 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
4da6a1d8
MD
1259 }
1260
0a85b6f0 1261no_dma:
4da6a1d8 1262
2f0b9d08 1263 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 1264 if (ret)
4da6a1d8
MD
1265 return ret;
1266
9fab6123 1267 s = &dev->subdevices[0];
dd8a4b47 1268 if (!board->n_aichan_se) {
4da6a1d8
MD
1269 s->type = COMEDI_SUBD_UNUSED;
1270 } else {
1271 s->type = COMEDI_SUBD_AI;
1272 devpriv->sub_ai = s;
1273 s->subdev_flags = SDF_READABLE;
1274 if (check_single_ended(dev->iobase)) {
dd8a4b47 1275 s->n_chan = board->n_aichan_se;
4da6a1d8 1276 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
4da6a1d8 1277 } else {
dd8a4b47 1278 s->n_chan = board->n_aichan_diff;
4da6a1d8 1279 s->subdev_flags |= SDF_DIFF;
4da6a1d8 1280 }
dd8a4b47 1281 s->maxdata = board->ai_maxdata;
dd8a4b47 1282 s->range_table = board->ai_range_type;
4da6a1d8 1283 s->insn_read = pcl818_ai_insn_read;
e30b22a9 1284 if (dev->irq) {
4da6a1d8
MD
1285 dev->read_subdev = s;
1286 s->subdev_flags |= SDF_CMD_READ;
5d788d01 1287 s->len_chanlist = s->n_chan;
4da6a1d8
MD
1288 s->do_cmdtest = ai_cmdtest;
1289 s->do_cmd = ai_cmd;
5d788d01 1290 s->cancel = pcl818_ai_cancel;
4da6a1d8 1291 }
dd8a4b47 1292 if (board->is_818) {
4da6a1d8 1293 if ((it->options[4] == 1) || (it->options[4] == 10))
0109253d 1294 s->range_table = &range_pcl818l_h_ai; /* secondary range list jumper selectable */
4da6a1d8
MD
1295 } else {
1296 switch (it->options[4]) {
1297 case 0:
1298 s->range_table = &range_bipolar10;
1299 break;
1300 case 1:
1301 s->range_table = &range_bipolar5;
1302 break;
1303 case 2:
1304 s->range_table = &range_bipolar2_5;
1305 break;
1306 case 3:
1307 s->range_table = &range718_bipolar1;
1308 break;
1309 case 4:
1310 s->range_table = &range718_bipolar0_5;
1311 break;
1312 case 6:
1313 s->range_table = &range_unipolar10;
1314 break;
1315 case 7:
1316 s->range_table = &range_unipolar5;
1317 break;
1318 case 8:
1319 s->range_table = &range718_unipolar2;
1320 break;
1321 case 9:
1322 s->range_table = &range718_unipolar1;
1323 break;
1324 default:
1325 s->range_table = &range_unknown;
1326 break;
1327 }
1328 }
1329 }
1330
9fab6123 1331 s = &dev->subdevices[1];
dd8a4b47 1332 if (!board->n_aochan) {
4da6a1d8
MD
1333 s->type = COMEDI_SUBD_UNUSED;
1334 } else {
1335 s->type = COMEDI_SUBD_AO;
1336 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
dd8a4b47
HS
1337 s->n_chan = board->n_aochan;
1338 s->maxdata = board->ao_maxdata;
1339 s->len_chanlist = board->n_aochan;
1340 s->range_table = board->ao_range_type;
4da6a1d8
MD
1341 s->insn_read = pcl818_ao_insn_read;
1342 s->insn_write = pcl818_ao_insn_write;
dd8a4b47 1343 if (board->is_818) {
4da6a1d8
MD
1344 if ((it->options[4] == 1) || (it->options[4] == 10))
1345 s->range_table = &range_unipolar10;
1346 if (it->options[4] == 2)
1347 s->range_table = &range_unknown;
1348 } else {
1349 if ((it->options[5] == 1) || (it->options[5] == 10))
1350 s->range_table = &range_unipolar10;
1351 if (it->options[5] == 2)
1352 s->range_table = &range_unknown;
1353 }
1354 }
1355
9fab6123 1356 s = &dev->subdevices[2];
dd8a4b47 1357 if (!board->n_dichan) {
4da6a1d8
MD
1358 s->type = COMEDI_SUBD_UNUSED;
1359 } else {
1360 s->type = COMEDI_SUBD_DI;
1361 s->subdev_flags = SDF_READABLE;
dd8a4b47 1362 s->n_chan = board->n_dichan;
4da6a1d8 1363 s->maxdata = 1;
dd8a4b47 1364 s->len_chanlist = board->n_dichan;
4da6a1d8
MD
1365 s->range_table = &range_digital;
1366 s->insn_bits = pcl818_di_insn_bits;
1367 }
1368
9fab6123 1369 s = &dev->subdevices[3];
dd8a4b47 1370 if (!board->n_dochan) {
4da6a1d8
MD
1371 s->type = COMEDI_SUBD_UNUSED;
1372 } else {
1373 s->type = COMEDI_SUBD_DO;
1374 s->subdev_flags = SDF_WRITABLE;
dd8a4b47 1375 s->n_chan = board->n_dochan;
4da6a1d8 1376 s->maxdata = 1;
dd8a4b47 1377 s->len_chanlist = board->n_dochan;
4da6a1d8
MD
1378 s->range_table = &range_digital;
1379 s->insn_bits = pcl818_do_insn_bits;
1380 }
1381
1382 /* select 1/10MHz oscilator */
fc950139 1383 if ((it->options[3] == 0) || (it->options[3] == 10))
cb9cfd7e 1384 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
fc950139 1385 else
cb9cfd7e 1386 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
4da6a1d8
MD
1387
1388 /* max sampling speed */
dd8a4b47 1389 devpriv->ns_min = board->ns_min;
4da6a1d8 1390
dd8a4b47 1391 if (!board->is_818) {
4da6a1d8
MD
1392 if ((it->options[6] == 1) || (it->options[6] == 100))
1393 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1394 }
1395
1396 pcl818_reset(dev);
1397
4da6a1d8
MD
1398 return 0;
1399}
1400
484ecc95 1401static void pcl818_detach(struct comedi_device *dev)
4da6a1d8 1402{
9a1a6cf8
HS
1403 struct pcl818_private *devpriv = dev->private;
1404
1405 if (devpriv) {
484ecc95
HS
1406 pcl818_ai_cancel(dev, devpriv->sub_ai);
1407 pcl818_reset(dev);
1408 if (devpriv->dma)
1409 free_dma(devpriv->dma);
1410 if (devpriv->dmabuf[0])
1411 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1412 if (devpriv->dmabuf[1])
1413 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
484ecc95 1414 }
a32c6d00 1415 comedi_legacy_detach(dev);
4da6a1d8 1416}
90f703d3 1417
f6aafa10
HS
1418static const struct pcl818_board boardtypes[] = {
1419 {"pcl818l", 4, 16, 8, 25000, 1, 16, 16, &range_pcl818l_l_ai,
1420 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1421 0x0a, 0xfff, 0xfff, 0, 1},
1422 {"pcl818h", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
1423 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1424 0x0a, 0xfff, 0xfff, 0, 1},
1425 {"pcl818hd", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
1426 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1427 0x0a, 0xfff, 0xfff, 1, 1},
1428 {"pcl818hg", 12, 16, 8, 10000, 1, 16, 16, &range_pcl818hg_ai,
1429 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1430 0x0a, 0xfff, 0xfff, 1, 1},
1431 {"pcl818", 9, 16, 8, 10000, 2, 16, 16, &range_pcl818h_ai,
1432 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1433 0x0a, 0xfff, 0xfff, 0, 1},
1434 {"pcl718", 1, 16, 8, 16000, 2, 16, 16, &range_unipolar5,
1435 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1436 0x0a, 0xfff, 0xfff, 0, 0},
1437 /* pcm3718 */
1438 {"pcm3718", 9, 16, 8, 10000, 0, 16, 16, &range_pcl818h_ai,
1439 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1440 0x0a, 0xfff, 0xfff, 0, 1 /* XXX ? */ },
1441};
1442
294f930d 1443static struct comedi_driver pcl818_driver = {
f6aafa10
HS
1444 .driver_name = "pcl818",
1445 .module = THIS_MODULE,
1446 .attach = pcl818_attach,
1447 .detach = pcl818_detach,
1448 .board_name = &boardtypes[0].name,
1449 .num_names = ARRAY_SIZE(boardtypes),
1450 .offset = sizeof(struct pcl818_board),
1451};
294f930d 1452module_comedi_driver(pcl818_driver);
f6aafa10 1453
90f703d3
AT
1454MODULE_AUTHOR("Comedi http://www.comedi.org");
1455MODULE_DESCRIPTION("Comedi low-level driver");
1456MODULE_LICENSE("GPL");
This page took 0.630496 seconds and 5 git commands to generate.