]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/staging/comedi/drivers/pcl818.c
staging: comedi: pcl816: sample types are unsigned
[mirror_ubuntu-hirsute-kernel.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
BP
276 int i8253_osc_base; /* 1/frequency of on board oscilator in ns */
277 int irq_free; /* 1=have allocated IRQ */
278 int irq_blocked; /* 1=IRQ now uses any subdev */
279 int irq_was_now_closed; /* when IRQ finish, there's stored int818_mode for last interrupt */
280 int ai_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
281 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
282 int ai_act_scan; /* how many scans we finished */
283 int ai_act_chan; /* actual position in actual scan */
284 unsigned int act_chanlist[16]; /* MUX setting for actual AI operations */
285 unsigned int act_chanlist_len; /* how long is actual MUX list */
286 unsigned int act_chanlist_pos; /* actual position in MUX list */
287 unsigned int ai_scans; /* len of scanlist */
288 unsigned int ai_n_chan; /* how many channels is measured */
289 unsigned int *ai_chanlist; /* actaul chanlist */
290 unsigned int ai_flags; /* flaglist */
291 unsigned int ai_data_len; /* len of data buffer */
0a85b6f0 292 short *ai_data; /* data buffer */
0109253d 293 unsigned int ai_timer1; /* timers */
4da6a1d8 294 unsigned int ai_timer2;
0109253d
BP
295 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
296 unsigned char usefifo; /* 1=use fifo */
790c5541 297 unsigned int ao_readback[2];
087ea31b
BP
298};
299
0109253d 300static const unsigned int muxonechan[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, /* used for gain list programming */
4da6a1d8
MD
301 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
302};
303
4da6a1d8
MD
304/*
305==============================================================================
306*/
0a85b6f0
MT
307static void setup_channel_list(struct comedi_device *dev,
308 struct comedi_subdevice *s,
309 unsigned int *chanlist, unsigned int n_chan,
310 unsigned int seglen);
311static int check_channel_list(struct comedi_device *dev,
312 struct comedi_subdevice *s,
313 unsigned int *chanlist, unsigned int n_chan);
314
315static int pcl818_ai_cancel(struct comedi_device *dev,
316 struct comedi_subdevice *s);
317static void start_pacer(struct comedi_device *dev, int mode,
318 unsigned int divisor1, unsigned int divisor2);
4da6a1d8 319
4da6a1d8
MD
320/*
321==============================================================================
322 ANALOG INPUT MODE0, 818 cards, slow version
323*/
0a85b6f0
MT
324static int pcl818_ai_insn_read(struct comedi_device *dev,
325 struct comedi_subdevice *s,
326 struct comedi_insn *insn, unsigned int *data)
4da6a1d8
MD
327{
328 int n;
329 int timeout;
330
331 /* software trigger, DMA and INT off */
332 outb(0, dev->iobase + PCL818_CONTROL);
333
334 /* select channel */
335 outb(muxonechan[CR_CHAN(insn->chanspec)], dev->iobase + PCL818_MUX);
336
337 /* select gain */
338 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL818_RANGE);
339
340 for (n = 0; n < insn->n; n++) {
341
342 /* clear INT (conversion end) flag */
343 outb(0, dev->iobase + PCL818_CLRINT);
344
345 /* start conversion */
346 outb(0, dev->iobase + PCL818_AD_LO);
347
348 timeout = 100;
349 while (timeout--) {
350 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
351 goto conv_finish;
5f74ea14 352 udelay(1);
4da6a1d8
MD
353 }
354 comedi_error(dev, "A/D insn timeout");
355 /* clear INT (conversion end) flag */
356 outb(0, dev->iobase + PCL818_CLRINT);
357 return -EIO;
358
0a85b6f0 359conv_finish:
4da6a1d8 360 data[n] = ((inb(dev->iobase + PCL818_AD_HI) << 4) |
0a85b6f0 361 (inb(dev->iobase + PCL818_AD_LO) >> 4));
4da6a1d8
MD
362 }
363
364 return n;
365}
366
367/*
368==============================================================================
369 ANALOG OUTPUT MODE0, 818 cards
370 only one sample per call is supported
371*/
0a85b6f0
MT
372static int pcl818_ao_insn_read(struct comedi_device *dev,
373 struct comedi_subdevice *s,
374 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 375{
9a1a6cf8 376 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
377 int n;
378 int chan = CR_CHAN(insn->chanspec);
379
fc950139 380 for (n = 0; n < insn->n; n++)
4da6a1d8 381 data[n] = devpriv->ao_readback[chan];
4da6a1d8
MD
382
383 return n;
384}
385
0a85b6f0
MT
386static int pcl818_ao_insn_write(struct comedi_device *dev,
387 struct comedi_subdevice *s,
388 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 389{
9a1a6cf8 390 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
391 int n;
392 int chan = CR_CHAN(insn->chanspec);
393
394 for (n = 0; n < insn->n; n++) {
395 devpriv->ao_readback[chan] = data[n];
396 outb((data[n] & 0x000f) << 4, dev->iobase +
0a85b6f0 397 (chan ? PCL718_DA2_LO : PCL818_DA_LO));
4da6a1d8 398 outb((data[n] & 0x0ff0) >> 4, dev->iobase +
0a85b6f0 399 (chan ? PCL718_DA2_HI : PCL818_DA_HI));
4da6a1d8
MD
400 }
401
402 return n;
403}
404
405/*
406==============================================================================
407 DIGITAL INPUT MODE0, 818 cards
408
409 only one sample per call is supported
410*/
0a85b6f0
MT
411static int pcl818_di_insn_bits(struct comedi_device *dev,
412 struct comedi_subdevice *s,
413 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 414{
4da6a1d8 415 data[1] = inb(dev->iobase + PCL818_DI_LO) |
0a85b6f0 416 (inb(dev->iobase + PCL818_DI_HI) << 8);
4da6a1d8 417
a2714e3e 418 return insn->n;
4da6a1d8
MD
419}
420
0a85b6f0
MT
421static int pcl818_do_insn_bits(struct comedi_device *dev,
422 struct comedi_subdevice *s,
97f4289a
HS
423 struct comedi_insn *insn,
424 unsigned int *data)
4da6a1d8 425{
97f4289a
HS
426 if (comedi_dio_update_state(s, data)) {
427 outb(s->state & 0xff, dev->iobase + PCL818_DO_LO);
428 outb((s->state >> 8), dev->iobase + PCL818_DO_HI);
429 }
4da6a1d8
MD
430
431 data[1] = s->state;
432
a2714e3e 433 return insn->n;
4da6a1d8
MD
434}
435
436/*
437==============================================================================
438 analog input interrupt mode 1 & 3, 818 cards
439 one sample per interrupt version
440*/
441static irqreturn_t interrupt_pcl818_ai_mode13_int(int irq, void *d)
442{
71b5f4f1 443 struct comedi_device *dev = d;
9a1a6cf8 444 struct pcl818_private *devpriv = dev->private;
9fab6123 445 struct comedi_subdevice *s = &dev->subdevices[0];
4da6a1d8
MD
446 int low;
447 int timeout = 50; /* wait max 50us */
448
449 while (timeout--) {
450 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
451 goto conv_finish;
5f74ea14 452 udelay(1);
4da6a1d8
MD
453 }
454 outb(0, dev->iobase + PCL818_STATUS); /* clear INT request */
455 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
456 pcl818_ai_cancel(dev, s);
457 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
458 comedi_event(dev, s);
459 return IRQ_HANDLED;
460
0a85b6f0 461conv_finish:
4da6a1d8 462 low = inb(dev->iobase + PCL818_AD_LO);
0109253d 463 comedi_buf_put(s->async, ((inb(dev->iobase + PCL818_AD_HI) << 4) | (low >> 4))); /* get one sample */
4da6a1d8
MD
464 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
465
0109253d 466 if ((low & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
5f74ea14 467 printk
0a85b6f0
MT
468 ("comedi: A/D mode1/3 IRQ - channel dropout %x!=%x !\n",
469 (low & 0xf),
470 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
4da6a1d8
MD
471 pcl818_ai_cancel(dev, s);
472 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
473 comedi_event(dev, s);
474 return IRQ_HANDLED;
475 }
b3559cb1 476 devpriv->act_chanlist_pos++;
fc950139 477 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
b3559cb1 478 devpriv->act_chanlist_pos = 0;
fc950139 479
b3559cb1
IA
480 s->async->cur_chan++;
481 if (s->async->cur_chan >= devpriv->ai_n_chan) {
5f74ea14 482 /* printk("E"); */
b3559cb1 483 s->async->cur_chan = 0;
4da6a1d8
MD
484 devpriv->ai_act_scan--;
485 }
486
487 if (!devpriv->neverending_ai) {
488 if (devpriv->ai_act_scan == 0) { /* all data sampled */
489 pcl818_ai_cancel(dev, s);
490 s->async->events |= COMEDI_CB_EOA;
491 }
492 }
493 comedi_event(dev, s);
494 return IRQ_HANDLED;
495}
496
497/*
498==============================================================================
499 analog input dma mode 1 & 3, 818 cards
500*/
501static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
502{
71b5f4f1 503 struct comedi_device *dev = d;
9a1a6cf8 504 struct pcl818_private *devpriv = dev->private;
9fab6123 505 struct comedi_subdevice *s = &dev->subdevices[0];
4da6a1d8
MD
506 int i, len, bufptr;
507 unsigned long flags;
790c5541 508 short *ptr;
4da6a1d8
MD
509
510 disable_dma(devpriv->dma);
511 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
0109253d 512 if ((devpriv->dma_runs_to_end) > -1 || devpriv->neverending_ai) { /* switch dma bufs */
4da6a1d8
MD
513 set_dma_mode(devpriv->dma, DMA_MODE_READ);
514 flags = claim_dma_lock();
515 set_dma_addr(devpriv->dma,
0a85b6f0 516 devpriv->hwdmaptr[devpriv->next_dma_buf]);
4da6a1d8
MD
517 if (devpriv->dma_runs_to_end || devpriv->neverending_ai) {
518 set_dma_count(devpriv->dma,
0a85b6f0
MT
519 devpriv->hwdmasize[devpriv->
520 next_dma_buf]);
4da6a1d8
MD
521 } else {
522 set_dma_count(devpriv->dma, devpriv->last_dma_run);
523 }
524 release_dma_lock(flags);
525 enable_dma(devpriv->dma);
526 }
5f74ea14 527 printk("comedi: A/D mode1/3 IRQ \n");
4da6a1d8
MD
528
529 devpriv->dma_runs_to_end--;
530 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
0a85b6f0 531 ptr = (short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
4da6a1d8
MD
532
533 len = devpriv->hwdmasize[0] >> 1;
534 bufptr = 0;
535
536 for (i = 0; i < len; i++) {
0109253d 537 if ((ptr[bufptr] & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
5f74ea14 538 printk
0a85b6f0
MT
539 ("comedi: A/D mode1/3 DMA - channel dropout %d(card)!=%d(chanlist) at %d !\n",
540 (ptr[bufptr] & 0xf),
541 devpriv->act_chanlist[devpriv->act_chanlist_pos],
542 devpriv->act_chanlist_pos);
4da6a1d8
MD
543 pcl818_ai_cancel(dev, s);
544 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
545 comedi_event(dev, s);
546 return IRQ_HANDLED;
547 }
548
0109253d 549 comedi_buf_put(s->async, ptr[bufptr++] >> 4); /* get one sample */
4da6a1d8
MD
550
551 devpriv->act_chanlist_pos++;
fc950139 552 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
4da6a1d8 553 devpriv->act_chanlist_pos = 0;
fc950139 554
b3559cb1
IA
555 s->async->cur_chan++;
556 if (s->async->cur_chan >= devpriv->ai_n_chan) {
557 s->async->cur_chan = 0;
558 devpriv->ai_act_scan--;
559 }
4da6a1d8
MD
560
561 if (!devpriv->neverending_ai)
562 if (devpriv->ai_act_scan == 0) { /* all data sampled */
563 pcl818_ai_cancel(dev, s);
564 s->async->events |= COMEDI_CB_EOA;
565 comedi_event(dev, s);
0109253d 566 /* printk("done int ai13 dma\n"); */
4da6a1d8
MD
567 return IRQ_HANDLED;
568 }
569 }
570
571 if (len > 0)
572 comedi_event(dev, s);
573 return IRQ_HANDLED;
574}
575
4da6a1d8
MD
576/*
577==============================================================================
578 analog input interrupt mode 1 & 3, 818HD/HG cards
579*/
580static irqreturn_t interrupt_pcl818_ai_mode13_fifo(int irq, void *d)
581{
71b5f4f1 582 struct comedi_device *dev = d;
9a1a6cf8 583 struct pcl818_private *devpriv = dev->private;
9fab6123 584 struct comedi_subdevice *s = &dev->subdevices[0];
4da6a1d8
MD
585 int i, len, lo;
586
0109253d 587 outb(0, dev->iobase + PCL818_FI_INTCLR); /* clear fifo int request */
4da6a1d8
MD
588
589 lo = inb(dev->iobase + PCL818_FI_STATUS);
590
591 if (lo & 4) {
592 comedi_error(dev, "A/D mode1/3 FIFO overflow!");
593 pcl818_ai_cancel(dev, s);
594 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
595 comedi_event(dev, s);
596 return IRQ_HANDLED;
597 }
598
599 if (lo & 1) {
600 comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
601 pcl818_ai_cancel(dev, s);
602 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
603 comedi_event(dev, s);
604 return IRQ_HANDLED;
605 }
606
fc950139 607 if (lo & 2)
4da6a1d8 608 len = 512;
fc950139 609 else
4da6a1d8 610 len = 0;
4da6a1d8
MD
611
612 for (i = 0; i < len; i++) {
613 lo = inb(dev->iobase + PCL818_FI_DATALO);
0109253d 614 if ((lo & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
5f74ea14 615 printk
0a85b6f0
MT
616 ("comedi: A/D mode1/3 FIFO - channel dropout %d!=%d !\n",
617 (lo & 0xf),
618 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
4da6a1d8
MD
619 pcl818_ai_cancel(dev, s);
620 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
621 comedi_event(dev, s);
622 return IRQ_HANDLED;
623 }
624
0109253d 625 comedi_buf_put(s->async, (lo >> 4) | (inb(dev->iobase + PCL818_FI_DATAHI) << 4)); /* get one sample */
4da6a1d8 626
b3559cb1 627 devpriv->act_chanlist_pos++;
fc950139 628 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
b3559cb1 629 devpriv->act_chanlist_pos = 0;
fc950139 630
b3559cb1
IA
631 s->async->cur_chan++;
632 if (s->async->cur_chan >= devpriv->ai_n_chan) {
633 s->async->cur_chan = 0;
4da6a1d8
MD
634 devpriv->ai_act_scan--;
635 }
636
637 if (!devpriv->neverending_ai)
638 if (devpriv->ai_act_scan == 0) { /* all data sampled */
639 pcl818_ai_cancel(dev, s);
640 s->async->events |= COMEDI_CB_EOA;
641 comedi_event(dev, s);
642 return IRQ_HANDLED;
643 }
644 }
645
646 if (len > 0)
647 comedi_event(dev, s);
648 return IRQ_HANDLED;
649}
650
651/*
652==============================================================================
653 INT procedure
654*/
70265d24 655static irqreturn_t interrupt_pcl818(int irq, void *d)
4da6a1d8 656{
71b5f4f1 657 struct comedi_device *dev = d;
9a1a6cf8 658 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
659
660 if (!dev->attached) {
661 comedi_error(dev, "premature interrupt");
662 return IRQ_HANDLED;
663 }
5f74ea14 664 /* printk("I\n"); */
4da6a1d8 665
e21de1a8
IA
666 if (devpriv->irq_blocked && devpriv->irq_was_now_closed) {
667 if ((devpriv->neverending_ai || (!devpriv->neverending_ai &&
0a85b6f0
MT
668 devpriv->ai_act_scan > 0)) &&
669 (devpriv->ai_mode == INT_TYPE_AI1_DMA ||
670 devpriv->ai_mode == INT_TYPE_AI3_DMA)) {
e21de1a8
IA
671 /* The cleanup from ai_cancel() has been delayed
672 until now because the card doesn't seem to like
673 being reprogrammed while a DMA transfer is in
674 progress.
675 */
9fab6123 676 struct comedi_subdevice *s = &dev->subdevices[0];
e21de1a8
IA
677 devpriv->ai_act_scan = 0;
678 devpriv->neverending_ai = 0;
679 pcl818_ai_cancel(dev, s);
680 }
681
682 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
683
684 return IRQ_HANDLED;
685 }
686
4da6a1d8
MD
687 switch (devpriv->ai_mode) {
688 case INT_TYPE_AI1_DMA:
689 case INT_TYPE_AI3_DMA:
690 return interrupt_pcl818_ai_mode13_dma(irq, d);
691 case INT_TYPE_AI1_INT:
692 case INT_TYPE_AI3_INT:
693 return interrupt_pcl818_ai_mode13_int(irq, d);
694 case INT_TYPE_AI1_FIFO:
695 case INT_TYPE_AI3_FIFO:
696 return interrupt_pcl818_ai_mode13_fifo(irq, d);
697#ifdef PCL818_MODE13_AO
698 case INT_TYPE_AO1_INT:
699 case INT_TYPE_AO3_INT:
700 return interrupt_pcl818_ao_mode13_int(irq, d);
701#endif
702 default:
703 break;
704 }
705
706 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
707
708 if ((!dev->irq) || (!devpriv->irq_free) || (!devpriv->irq_blocked)
0a85b6f0 709 || (!devpriv->ai_mode)) {
4da6a1d8
MD
710 comedi_error(dev, "bad IRQ!");
711 return IRQ_NONE;
712 }
713
bbc9a991 714 comedi_error(dev, "IRQ from unknown source!");
4da6a1d8
MD
715 return IRQ_NONE;
716}
717
718/*
719==============================================================================
720 ANALOG INPUT MODE 1 or 3 DMA , 818 cards
721*/
da91b269 722static void pcl818_ai_mode13dma_int(int mode, struct comedi_device *dev,
0a85b6f0 723 struct comedi_subdevice *s)
4da6a1d8 724{
9a1a6cf8 725 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
726 unsigned int flags;
727 unsigned int bytes;
728
5f74ea14 729 printk("mode13dma_int, mode: %d\n", mode);
0109253d 730 disable_dma(devpriv->dma); /* disable dma */
4da6a1d8
MD
731 bytes = devpriv->hwdmasize[0];
732 if (!devpriv->neverending_ai) {
0109253d
BP
733 bytes = devpriv->ai_n_chan * devpriv->ai_scans * sizeof(short); /* how many */
734 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fiil */
735 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
4da6a1d8
MD
736 devpriv->dma_runs_to_end--;
737 if (devpriv->dma_runs_to_end >= 0)
738 bytes = devpriv->hwdmasize[0];
739 }
740
741 devpriv->next_dma_buf = 0;
742 set_dma_mode(devpriv->dma, DMA_MODE_READ);
743 flags = claim_dma_lock();
744 clear_dma_ff(devpriv->dma);
745 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
746 set_dma_count(devpriv->dma, bytes);
747 release_dma_lock(flags);
748 enable_dma(devpriv->dma);
749
750 if (mode == 1) {
751 devpriv->ai_mode = INT_TYPE_AI1_DMA;
752 outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+IRQ+DMA */
753 } else {
754 devpriv->ai_mode = INT_TYPE_AI3_DMA;
755 outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+IRQ+DMA */
756 };
757}
758
4da6a1d8
MD
759/*
760==============================================================================
761 ANALOG INPUT MODE 1 or 3, 818 cards
762*/
da91b269 763static int pcl818_ai_cmd_mode(int mode, struct comedi_device *dev,
0a85b6f0 764 struct comedi_subdevice *s)
4da6a1d8 765{
9a1a6cf8 766 struct pcl818_private *devpriv = dev->private;
ea6d0d4c 767 struct comedi_cmd *cmd = &s->async->cmd;
48b1aff5 768 int divisor1 = 0, divisor2 = 0;
4da6a1d8
MD
769 unsigned int seglen;
770
f41ad667 771 dev_dbg(dev->class_dev, "pcl818_ai_cmd_mode()\n");
aecfd1ec 772 if (!dev->irq) {
4da6a1d8
MD
773 comedi_error(dev, "IRQ not defined!");
774 return -EINVAL;
775 }
776
777 if (devpriv->irq_blocked)
778 return -EBUSY;
779
0109253d 780 start_pacer(dev, -1, 0, 0); /* stop pacer */
4da6a1d8
MD
781
782 seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
0a85b6f0 783 devpriv->ai_n_chan);
4da6a1d8
MD
784 if (seglen < 1)
785 return -EINVAL;
786 setup_channel_list(dev, s, devpriv->ai_chanlist,
0a85b6f0 787 devpriv->ai_n_chan, seglen);
4da6a1d8 788
5f74ea14 789 udelay(1);
4da6a1d8
MD
790
791 devpriv->ai_act_scan = devpriv->ai_scans;
792 devpriv->ai_act_chan = 0;
793 devpriv->irq_blocked = 1;
794 devpriv->irq_was_now_closed = 0;
795 devpriv->neverending_ai = 0;
796 devpriv->act_chanlist_pos = 0;
797 devpriv->dma_runs_to_end = 0;
798
799 if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
0109253d 800 devpriv->neverending_ai = 1; /* well, user want neverending */
4da6a1d8
MD
801
802 if (mode == 1) {
cb9cfd7e
HS
803 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
804 &divisor1, &divisor2,
805 &cmd->convert_arg,
0a85b6f0 806 TRIG_ROUND_NEAREST);
4da6a1d8
MD
807 if (divisor1 == 1) { /* PCL718/818 crash if any divisor is set to 1 */
808 divisor1 = 2;
809 divisor2 /= 2;
810 }
811 if (divisor2 == 1) {
812 divisor2 = 2;
813 divisor1 /= 2;
814 }
815 }
816
817 outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
818
819 switch (devpriv->dma) {
0109253d 820 case 1: /* DMA */
4da6a1d8 821 case 3:
aecfd1ec 822 pcl818_ai_mode13dma_int(mode, dev, s);
4da6a1d8 823 break;
a71f18d2
IA
824 case 0:
825 if (!devpriv->usefifo) {
826 /* IRQ */
5f74ea14 827 /* printk("IRQ\n"); */
a71f18d2
IA
828 if (mode == 1) {
829 devpriv->ai_mode = INT_TYPE_AI1_INT;
830 /* Pacer+IRQ */
0a85b6f0
MT
831 outb(0x83 | (dev->irq << 4),
832 dev->iobase + PCL818_CONTROL);
a71f18d2
IA
833 } else {
834 devpriv->ai_mode = INT_TYPE_AI3_INT;
835 /* Ext trig+IRQ */
0a85b6f0
MT
836 outb(0x82 | (dev->irq << 4),
837 dev->iobase + PCL818_CONTROL);
a71f18d2 838 }
4da6a1d8 839 } else {
a71f18d2
IA
840 /* FIFO */
841 /* enable FIFO */
842 outb(1, dev->iobase + PCL818_FI_ENABLE);
843 if (mode == 1) {
844 devpriv->ai_mode = INT_TYPE_AI1_FIFO;
845 /* Pacer */
846 outb(0x03, dev->iobase + PCL818_CONTROL);
847 } else {
848 devpriv->ai_mode = INT_TYPE_AI3_FIFO;
849 outb(0x02, dev->iobase + PCL818_CONTROL);
850 }
851 }
4da6a1d8
MD
852 }
853
854 start_pacer(dev, mode, divisor1, divisor2);
855
f41ad667 856 dev_dbg(dev->class_dev, "pcl818_ai_cmd_mode() end\n");
4da6a1d8
MD
857 return 0;
858}
859
4da6a1d8
MD
860/*
861==============================================================================
862 Start/stop pacer onboard pacer
863*/
0a85b6f0
MT
864static void start_pacer(struct comedi_device *dev, int mode,
865 unsigned int divisor1, unsigned int divisor2)
4da6a1d8
MD
866{
867 outb(0xb4, dev->iobase + PCL818_CTRCTL);
868 outb(0x74, dev->iobase + PCL818_CTRCTL);
5f74ea14 869 udelay(1);
4da6a1d8
MD
870
871 if (mode == 1) {
872 outb(divisor2 & 0xff, dev->iobase + PCL818_CTR2);
873 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL818_CTR2);
874 outb(divisor1 & 0xff, dev->iobase + PCL818_CTR1);
875 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL818_CTR1);
876 }
877}
878
879/*
880==============================================================================
881 Check if channel list from user is builded correctly
882 If it's ok, then program scan/gain logic
883*/
0a85b6f0
MT
884static int check_channel_list(struct comedi_device *dev,
885 struct comedi_subdevice *s,
886 unsigned int *chanlist, unsigned int n_chan)
4da6a1d8
MD
887{
888 unsigned int chansegment[16];
889 unsigned int i, nowmustbechan, seglen, segpos;
890
891 /* correct channel and range number check itself comedi/range.c */
892 if (n_chan < 1) {
893 comedi_error(dev, "range/channel list is empty!");
894 return 0;
895 }
896
897 if (n_chan > 1) {
25985edc 898 /* first channel is every time ok */
4da6a1d8 899 chansegment[0] = chanlist[0];
0109253d 900 /* build part of chanlist */
4da6a1d8 901 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
0109253d 902
5f74ea14 903 /* printk("%d. %d * %d\n",i,
0109253d
BP
904 * CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i]));*/
905
906 /* we detect loop, this must by finish */
907
4da6a1d8
MD
908 if (chanlist[0] == chanlist[i])
909 break;
910 nowmustbechan =
0a85b6f0 911 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
25985edc 912 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
5f74ea14 913 printk
25985edc 914 ("comedi%d: pcl818: channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
0a85b6f0
MT
915 dev->minor, i, CR_CHAN(chanlist[i]),
916 nowmustbechan, CR_CHAN(chanlist[0]));
4da6a1d8
MD
917 return 0;
918 }
0109253d 919 /* well, this is next correct channel in list */
4da6a1d8
MD
920 chansegment[i] = chanlist[i];
921 }
922
0109253d 923 /* check whole chanlist */
4da6a1d8 924 for (i = 0, segpos = 0; i < n_chan; i++) {
5f74ea14 925 /* printk("%d %d=%d %d\n",CR_CHAN(chansegment[i%seglen]),CR_RANGE(chansegment[i%seglen]),CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i])); */
4da6a1d8 926 if (chanlist[i] != chansegment[i % seglen]) {
5f74ea14 927 printk
0a85b6f0
MT
928 ("comedi%d: pcl818: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
929 dev->minor, i, CR_CHAN(chansegment[i]),
930 CR_RANGE(chansegment[i]),
931 CR_AREF(chansegment[i]),
932 CR_CHAN(chanlist[i % seglen]),
933 CR_RANGE(chanlist[i % seglen]),
934 CR_AREF(chansegment[i % seglen]));
0109253d 935 return 0; /* chan/gain list is strange */
4da6a1d8
MD
936 }
937 }
938 } else {
939 seglen = 1;
940 }
5f74ea14 941 printk("check_channel_list: seglen %d\n", seglen);
4da6a1d8
MD
942 return seglen;
943}
944
0a85b6f0
MT
945static void setup_channel_list(struct comedi_device *dev,
946 struct comedi_subdevice *s,
947 unsigned int *chanlist, unsigned int n_chan,
948 unsigned int seglen)
4da6a1d8 949{
9a1a6cf8 950 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
951 int i;
952
953 devpriv->act_chanlist_len = seglen;
954 devpriv->act_chanlist_pos = 0;
955
0109253d 956 for (i = 0; i < seglen; i++) { /* store range list to card */
4da6a1d8
MD
957 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
958 outb(muxonechan[CR_CHAN(chanlist[i])], dev->iobase + PCL818_MUX); /* select channel */
959 outb(CR_RANGE(chanlist[i]), dev->iobase + PCL818_RANGE); /* select gain */
960 }
961
5f74ea14 962 udelay(1);
4da6a1d8
MD
963
964 /* select channel interval to scan */
965 outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen -
0a85b6f0
MT
966 1] << 4),
967 dev->iobase + PCL818_MUX);
4da6a1d8
MD
968}
969
970/*
971==============================================================================
972 Check if board is switched to SE (1) or DIFF(0) mode
973*/
974static int check_single_ended(unsigned int port)
975{
fc950139 976 if (inb(port + PCL818_STATUS) & 0x20)
4da6a1d8 977 return 1;
fc950139 978 return 0;
4da6a1d8
MD
979}
980
981/*
982==============================================================================
983*/
da91b269 984static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 985 struct comedi_cmd *cmd)
4da6a1d8 986{
dd8a4b47 987 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 988 struct pcl818_private *devpriv = dev->private;
4da6a1d8 989 int err = 0;
48b1aff5 990 int tmp, divisor1 = 0, divisor2 = 0;
4da6a1d8 991
27020ffe 992 /* Step 1 : check if triggers are trivially valid */
4da6a1d8 993
27020ffe
HS
994 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
995 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
996 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
997 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
998 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
4da6a1d8 999
fc950139 1000 if (err)
4da6a1d8 1001 return 1;
4da6a1d8 1002
27020ffe 1003 /* Step 2a : make sure trigger sources are unique */
4da6a1d8 1004
27020ffe
HS
1005 err |= cfc_check_trigger_is_unique(cmd->convert_src);
1006 err |= cfc_check_trigger_is_unique(cmd->stop_src);
4da6a1d8 1007
27020ffe 1008 /* Step 2b : and mutually compatible */
4da6a1d8 1009
fc950139 1010 if (err)
4da6a1d8 1011 return 2;
4da6a1d8 1012
8efdc1bf 1013 /* Step 3: check if arguments are trivially valid */
4da6a1d8 1014
8efdc1bf
HS
1015 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
1016 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
4da6a1d8 1017
8efdc1bf
HS
1018 if (cmd->convert_src == TRIG_TIMER)
1019 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
1020 board->ns_min);
1021 else /* TRIG_EXT */
1022 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
4da6a1d8 1023
8efdc1bf 1024 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
4da6a1d8 1025
8efdc1bf
HS
1026 if (cmd->stop_src == TRIG_COUNT)
1027 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
1028 else /* TRIG_NONE */
1029 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
4da6a1d8 1030
fc950139 1031 if (err)
4da6a1d8 1032 return 3;
4da6a1d8
MD
1033
1034 /* step 4: fix up any arguments */
1035
1036 if (cmd->convert_src == TRIG_TIMER) {
1037 tmp = cmd->convert_arg;
cb9cfd7e
HS
1038 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
1039 &divisor1, &divisor2,
1040 &cmd->convert_arg, cmd->flags);
dd8a4b47
HS
1041 if (cmd->convert_arg < board->ns_min)
1042 cmd->convert_arg = board->ns_min;
4da6a1d8
MD
1043 if (tmp != cmd->convert_arg)
1044 err++;
1045 }
1046
fc950139 1047 if (err)
4da6a1d8 1048 return 4;
4da6a1d8
MD
1049
1050 /* step 5: complain about special chanlist considerations */
1051
1052 if (cmd->chanlist) {
1053 if (!check_channel_list(dev, s, cmd->chanlist,
0a85b6f0 1054 cmd->chanlist_len))
0109253d 1055 return 5; /* incorrect channels list */
4da6a1d8
MD
1056 }
1057
1058 return 0;
1059}
1060
1061/*
1062==============================================================================
1063*/
da91b269 1064static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
4da6a1d8 1065{
9a1a6cf8 1066 struct pcl818_private *devpriv = dev->private;
ea6d0d4c 1067 struct comedi_cmd *cmd = &s->async->cmd;
4da6a1d8
MD
1068 int retval;
1069
f41ad667 1070 dev_dbg(dev->class_dev, "pcl818_ai_cmd()\n");
4da6a1d8
MD
1071 devpriv->ai_n_chan = cmd->chanlist_len;
1072 devpriv->ai_chanlist = cmd->chanlist;
1073 devpriv->ai_flags = cmd->flags;
1074 devpriv->ai_data_len = s->async->prealloc_bufsz;
1075 devpriv->ai_data = s->async->prealloc_buf;
1076 devpriv->ai_timer1 = 0;
1077 devpriv->ai_timer2 = 0;
1078
fc950139 1079 if (cmd->stop_src == TRIG_COUNT)
4da6a1d8 1080 devpriv->ai_scans = cmd->stop_arg;
fc950139 1081 else
4da6a1d8 1082 devpriv->ai_scans = 0;
4da6a1d8 1083
0109253d
BP
1084 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 3 */
1085 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 */
4da6a1d8
MD
1086 devpriv->ai_timer1 = cmd->convert_arg;
1087 retval = pcl818_ai_cmd_mode(1, dev, s);
f41ad667 1088 dev_dbg(dev->class_dev, "pcl818_ai_cmd() end\n");
4da6a1d8
MD
1089 return retval;
1090 }
0109253d 1091 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
4da6a1d8
MD
1092 return pcl818_ai_cmd_mode(3, dev, s);
1093 }
1094 }
1095
1096 return -1;
1097}
1098
1099/*
1100==============================================================================
1101 cancel any mode 1-4 AI
1102*/
0a85b6f0
MT
1103static int pcl818_ai_cancel(struct comedi_device *dev,
1104 struct comedi_subdevice *s)
4da6a1d8 1105{
9a1a6cf8
HS
1106 struct pcl818_private *devpriv = dev->private;
1107
4da6a1d8 1108 if (devpriv->irq_blocked > 0) {
f41ad667 1109 dev_dbg(dev->class_dev, "pcl818_ai_cancel()\n");
e21de1a8 1110 devpriv->irq_was_now_closed = 1;
4da6a1d8 1111
e21de1a8 1112 switch (devpriv->ai_mode) {
4da6a1d8
MD
1113 case INT_TYPE_AI1_DMA:
1114 case INT_TYPE_AI3_DMA:
e21de1a8 1115 if (devpriv->neverending_ai ||
0a85b6f0
MT
1116 (!devpriv->neverending_ai &&
1117 devpriv->ai_act_scan > 0)) {
4da6a1d8
MD
1118 /* wait for running dma transfer to end, do cleanup in interrupt */
1119 goto end;
1120 }
1121 disable_dma(devpriv->dma);
1122 case INT_TYPE_AI1_INT:
1123 case INT_TYPE_AI3_INT:
1124 case INT_TYPE_AI1_FIFO:
1125 case INT_TYPE_AI3_FIFO:
1126#ifdef PCL818_MODE13_AO
1127 case INT_TYPE_AO1_INT:
1128 case INT_TYPE_AO3_INT:
1129#endif
1130 outb(inb(dev->iobase + PCL818_CONTROL) & 0x73, dev->iobase + PCL818_CONTROL); /* Stop A/D */
5f74ea14 1131 udelay(1);
4da6a1d8
MD
1132 start_pacer(dev, -1, 0, 0);
1133 outb(0, dev->iobase + PCL818_AD_LO);
1134 inb(dev->iobase + PCL818_AD_LO);
1135 inb(dev->iobase + PCL818_AD_HI);
1136 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
1137 outb(0, dev->iobase + PCL818_CONTROL); /* Stop A/D */
0109253d 1138 if (devpriv->usefifo) { /* FIFO shutdown */
4da6a1d8
MD
1139 outb(0, dev->iobase + PCL818_FI_INTCLR);
1140 outb(0, dev->iobase + PCL818_FI_FLUSH);
1141 outb(0, dev->iobase + PCL818_FI_ENABLE);
1142 }
1143 devpriv->irq_blocked = 0;
1144 devpriv->last_int_sub = s;
1145 devpriv->neverending_ai = 0;
e21de1a8
IA
1146 devpriv->ai_mode = 0;
1147 devpriv->irq_was_now_closed = 0;
4da6a1d8
MD
1148 break;
1149 }
1150 }
1151
0a85b6f0 1152end:
f41ad667 1153 dev_dbg(dev->class_dev, "pcl818_ai_cancel() end\n");
4da6a1d8
MD
1154 return 0;
1155}
1156
1157/*
1158==============================================================================
1159 chech for PCL818
1160*/
1161static int pcl818_check(unsigned long iobase)
1162{
1163 outb(0x00, iobase + PCL818_MUX);
5f74ea14 1164 udelay(1);
4da6a1d8 1165 if (inb(iobase + PCL818_MUX) != 0x00)
0109253d 1166 return 1; /* there isn't card */
4da6a1d8 1167 outb(0x55, iobase + PCL818_MUX);
5f74ea14 1168 udelay(1);
4da6a1d8 1169 if (inb(iobase + PCL818_MUX) != 0x55)
0109253d 1170 return 1; /* there isn't card */
4da6a1d8 1171 outb(0x00, iobase + PCL818_MUX);
5f74ea14 1172 udelay(1);
4da6a1d8 1173 outb(0x18, iobase + PCL818_CONTROL);
5f74ea14 1174 udelay(1);
4da6a1d8 1175 if (inb(iobase + PCL818_CONTROL) != 0x18)
0109253d
BP
1176 return 1; /* there isn't card */
1177 return 0; /* ok, card exist */
4da6a1d8
MD
1178}
1179
1180/*
1181==============================================================================
1182 reset whole PCL-818 cards
1183*/
da91b269 1184static void pcl818_reset(struct comedi_device *dev)
4da6a1d8 1185{
dd8a4b47 1186 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 1187 struct pcl818_private *devpriv = dev->private;
dd8a4b47 1188
0109253d 1189 if (devpriv->usefifo) { /* FIFO shutdown */
4da6a1d8
MD
1190 outb(0, dev->iobase + PCL818_FI_INTCLR);
1191 outb(0, dev->iobase + PCL818_FI_FLUSH);
1192 outb(0, dev->iobase + PCL818_FI_ENABLE);
1193 }
0109253d 1194 outb(0, dev->iobase + PCL818_DA_LO); /* DAC=0V */
4da6a1d8 1195 outb(0, dev->iobase + PCL818_DA_HI);
5f74ea14 1196 udelay(1);
0109253d 1197 outb(0, dev->iobase + PCL818_DO_HI); /* DO=$0000 */
4da6a1d8 1198 outb(0, dev->iobase + PCL818_DO_LO);
5f74ea14 1199 udelay(1);
4da6a1d8
MD
1200 outb(0, dev->iobase + PCL818_CONTROL);
1201 outb(0, dev->iobase + PCL818_CNTENABLE);
1202 outb(0, dev->iobase + PCL818_MUX);
1203 outb(0, dev->iobase + PCL818_CLRINT);
1204 outb(0xb0, dev->iobase + PCL818_CTRCTL); /* Stop pacer */
1205 outb(0x70, dev->iobase + PCL818_CTRCTL);
1206 outb(0x30, dev->iobase + PCL818_CTRCTL);
dd8a4b47 1207 if (board->is_818) {
4da6a1d8
MD
1208 outb(0, dev->iobase + PCL818_RANGE);
1209 } else {
1210 outb(0, dev->iobase + PCL718_DA2_LO);
1211 outb(0, dev->iobase + PCL718_DA2_HI);
1212 }
1213}
1214
da91b269 1215static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
4da6a1d8 1216{
dd8a4b47 1217 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 1218 struct pcl818_private *devpriv;
4da6a1d8 1219 int ret;
a71f18d2
IA
1220 unsigned int irq;
1221 int dma;
4da6a1d8 1222 unsigned long pages;
34c43922 1223 struct comedi_subdevice *s;
4da6a1d8 1224
0bdab509 1225 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
1226 if (!devpriv)
1227 return -ENOMEM;
4da6a1d8 1228
dd8a4b47
HS
1229 devpriv->io_range = board->io_range;
1230 if ((board->fifo) && (it->options[2] == -1)) {
1231 /* we've board with FIFO and we want to use FIFO */
4da6a1d8
MD
1232 devpriv->io_range = PCLx1xFIFO_RANGE;
1233 devpriv->usefifo = 1;
1234 }
d6c5ec04
HS
1235 ret = comedi_request_region(dev, it->options[0], devpriv->io_range);
1236 if (ret)
1237 return ret;
4da6a1d8 1238
d6c5ec04 1239 if (pcl818_check(dev->iobase)) {
26ba666c 1240 comedi_error(dev, "I can't detect board. FAIL!\n");
4da6a1d8
MD
1241 return -EIO;
1242 }
1243
4da6a1d8
MD
1244 /* grab our IRQ */
1245 irq = 0;
dd8a4b47 1246 if (board->IRQbits != 0) { /* board support IRQ */
4da6a1d8
MD
1247 irq = it->options[1];
1248 if (irq) { /* we want to use IRQ */
dd8a4b47 1249 if (((1 << irq) & board->IRQbits) == 0) {
5f74ea14 1250 printk
0a85b6f0
MT
1251 (", IRQ %u is out of allowed range, DISABLING IT",
1252 irq);
4da6a1d8
MD
1253 irq = 0; /* Bad IRQ */
1254 } else {
b27cc413
HS
1255 if (request_irq(irq, interrupt_pcl818, 0,
1256 dev->board_name, dev)) {
5f74ea14 1257 printk
0a85b6f0
MT
1258 (", unable to allocate IRQ %u, DISABLING IT",
1259 irq);
4da6a1d8
MD
1260 irq = 0; /* Can't use IRQ */
1261 } else {
26ba666c 1262 printk(KERN_DEBUG "irq=%u", irq);
4da6a1d8
MD
1263 }
1264 }
1265 }
1266 }
1267
1268 dev->irq = irq;
fc950139
RM
1269 if (irq)
1270 devpriv->irq_free = 1; /* 1=we have allocated irq */
1271 else
4da6a1d8 1272 devpriv->irq_free = 0;
fc950139 1273
4da6a1d8
MD
1274 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1275 devpriv->ai_mode = 0; /* mode of irq */
1276
4da6a1d8
MD
1277 /* grab our DMA */
1278 dma = 0;
1279 devpriv->dma = dma;
aecfd1ec 1280 if (!devpriv->irq_free)
4da6a1d8 1281 goto no_dma; /* if we haven't IRQ, we can't use DMA */
dd8a4b47 1282 if (board->DMAbits != 0) { /* board support DMA */
4da6a1d8
MD
1283 dma = it->options[2];
1284 if (dma < 1)
1285 goto no_dma; /* DMA disabled */
dd8a4b47 1286 if (((1 << dma) & board->DMAbits) == 0) {
408f6bcd 1287 printk(KERN_ERR "DMA is out of allowed range, FAIL!\n");
4da6a1d8
MD
1288 return -EINVAL; /* Bad DMA */
1289 }
b27cc413 1290 ret = request_dma(dma, dev->board_name);
408f6bcd 1291 if (ret)
4da6a1d8 1292 return -EBUSY; /* DMA isn't free */
4da6a1d8 1293 devpriv->dma = dma;
4da6a1d8
MD
1294 pages = 2; /* we need 16KB */
1295 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
408f6bcd 1296 if (!devpriv->dmabuf[0])
4da6a1d8
MD
1297 /* maybe experiment with try_to_free_pages() will help .... */
1298 return -EBUSY; /* no buffer :-( */
4da6a1d8
MD
1299 devpriv->dmapages[0] = pages;
1300 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1301 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
5f74ea14 1302 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
aecfd1ec
HS
1303 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1304 if (!devpriv->dmabuf[1])
1305 return -EBUSY;
1306 devpriv->dmapages[1] = pages;
1307 devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]);
1308 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
4da6a1d8
MD
1309 }
1310
0a85b6f0 1311no_dma:
4da6a1d8 1312
2f0b9d08 1313 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 1314 if (ret)
4da6a1d8
MD
1315 return ret;
1316
9fab6123 1317 s = &dev->subdevices[0];
dd8a4b47 1318 if (!board->n_aichan_se) {
4da6a1d8
MD
1319 s->type = COMEDI_SUBD_UNUSED;
1320 } else {
1321 s->type = COMEDI_SUBD_AI;
1322 devpriv->sub_ai = s;
1323 s->subdev_flags = SDF_READABLE;
1324 if (check_single_ended(dev->iobase)) {
dd8a4b47 1325 s->n_chan = board->n_aichan_se;
4da6a1d8
MD
1326 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1327 printk(", %dchans S.E. DAC", s->n_chan);
1328 } else {
dd8a4b47 1329 s->n_chan = board->n_aichan_diff;
4da6a1d8
MD
1330 s->subdev_flags |= SDF_DIFF;
1331 printk(", %dchans DIFF DAC", s->n_chan);
1332 }
dd8a4b47 1333 s->maxdata = board->ai_maxdata;
4da6a1d8 1334 s->len_chanlist = s->n_chan;
dd8a4b47 1335 s->range_table = board->ai_range_type;
4da6a1d8
MD
1336 s->cancel = pcl818_ai_cancel;
1337 s->insn_read = pcl818_ai_insn_read;
aecfd1ec 1338 if (irq) {
4da6a1d8
MD
1339 dev->read_subdev = s;
1340 s->subdev_flags |= SDF_CMD_READ;
1341 s->do_cmdtest = ai_cmdtest;
1342 s->do_cmd = ai_cmd;
1343 }
dd8a4b47 1344 if (board->is_818) {
4da6a1d8 1345 if ((it->options[4] == 1) || (it->options[4] == 10))
0109253d 1346 s->range_table = &range_pcl818l_h_ai; /* secondary range list jumper selectable */
4da6a1d8
MD
1347 } else {
1348 switch (it->options[4]) {
1349 case 0:
1350 s->range_table = &range_bipolar10;
1351 break;
1352 case 1:
1353 s->range_table = &range_bipolar5;
1354 break;
1355 case 2:
1356 s->range_table = &range_bipolar2_5;
1357 break;
1358 case 3:
1359 s->range_table = &range718_bipolar1;
1360 break;
1361 case 4:
1362 s->range_table = &range718_bipolar0_5;
1363 break;
1364 case 6:
1365 s->range_table = &range_unipolar10;
1366 break;
1367 case 7:
1368 s->range_table = &range_unipolar5;
1369 break;
1370 case 8:
1371 s->range_table = &range718_unipolar2;
1372 break;
1373 case 9:
1374 s->range_table = &range718_unipolar1;
1375 break;
1376 default:
1377 s->range_table = &range_unknown;
1378 break;
1379 }
1380 }
1381 }
1382
9fab6123 1383 s = &dev->subdevices[1];
dd8a4b47 1384 if (!board->n_aochan) {
4da6a1d8
MD
1385 s->type = COMEDI_SUBD_UNUSED;
1386 } else {
1387 s->type = COMEDI_SUBD_AO;
1388 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
dd8a4b47
HS
1389 s->n_chan = board->n_aochan;
1390 s->maxdata = board->ao_maxdata;
1391 s->len_chanlist = board->n_aochan;
1392 s->range_table = board->ao_range_type;
4da6a1d8
MD
1393 s->insn_read = pcl818_ao_insn_read;
1394 s->insn_write = pcl818_ao_insn_write;
dd8a4b47 1395 if (board->is_818) {
4da6a1d8
MD
1396 if ((it->options[4] == 1) || (it->options[4] == 10))
1397 s->range_table = &range_unipolar10;
1398 if (it->options[4] == 2)
1399 s->range_table = &range_unknown;
1400 } else {
1401 if ((it->options[5] == 1) || (it->options[5] == 10))
1402 s->range_table = &range_unipolar10;
1403 if (it->options[5] == 2)
1404 s->range_table = &range_unknown;
1405 }
1406 }
1407
9fab6123 1408 s = &dev->subdevices[2];
dd8a4b47 1409 if (!board->n_dichan) {
4da6a1d8
MD
1410 s->type = COMEDI_SUBD_UNUSED;
1411 } else {
1412 s->type = COMEDI_SUBD_DI;
1413 s->subdev_flags = SDF_READABLE;
dd8a4b47 1414 s->n_chan = board->n_dichan;
4da6a1d8 1415 s->maxdata = 1;
dd8a4b47 1416 s->len_chanlist = board->n_dichan;
4da6a1d8
MD
1417 s->range_table = &range_digital;
1418 s->insn_bits = pcl818_di_insn_bits;
1419 }
1420
9fab6123 1421 s = &dev->subdevices[3];
dd8a4b47 1422 if (!board->n_dochan) {
4da6a1d8
MD
1423 s->type = COMEDI_SUBD_UNUSED;
1424 } else {
1425 s->type = COMEDI_SUBD_DO;
1426 s->subdev_flags = SDF_WRITABLE;
dd8a4b47 1427 s->n_chan = board->n_dochan;
4da6a1d8 1428 s->maxdata = 1;
dd8a4b47 1429 s->len_chanlist = board->n_dochan;
4da6a1d8
MD
1430 s->range_table = &range_digital;
1431 s->insn_bits = pcl818_do_insn_bits;
1432 }
1433
1434 /* select 1/10MHz oscilator */
fc950139 1435 if ((it->options[3] == 0) || (it->options[3] == 10))
cb9cfd7e 1436 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
fc950139 1437 else
cb9cfd7e 1438 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
4da6a1d8
MD
1439
1440 /* max sampling speed */
dd8a4b47 1441 devpriv->ns_min = board->ns_min;
4da6a1d8 1442
dd8a4b47 1443 if (!board->is_818) {
4da6a1d8
MD
1444 if ((it->options[6] == 1) || (it->options[6] == 100))
1445 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1446 }
1447
1448 pcl818_reset(dev);
1449
5f74ea14 1450 printk("\n");
4da6a1d8
MD
1451
1452 return 0;
1453}
1454
484ecc95 1455static void pcl818_detach(struct comedi_device *dev)
4da6a1d8 1456{
9a1a6cf8
HS
1457 struct pcl818_private *devpriv = dev->private;
1458
1459 if (devpriv) {
484ecc95
HS
1460 pcl818_ai_cancel(dev, devpriv->sub_ai);
1461 pcl818_reset(dev);
1462 if (devpriv->dma)
1463 free_dma(devpriv->dma);
1464 if (devpriv->dmabuf[0])
1465 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1466 if (devpriv->dmabuf[1])
1467 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
484ecc95 1468 }
a32c6d00 1469 comedi_legacy_detach(dev);
4da6a1d8 1470}
90f703d3 1471
f6aafa10
HS
1472static const struct pcl818_board boardtypes[] = {
1473 {"pcl818l", 4, 16, 8, 25000, 1, 16, 16, &range_pcl818l_l_ai,
1474 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1475 0x0a, 0xfff, 0xfff, 0, 1},
1476 {"pcl818h", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
1477 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1478 0x0a, 0xfff, 0xfff, 0, 1},
1479 {"pcl818hd", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
1480 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1481 0x0a, 0xfff, 0xfff, 1, 1},
1482 {"pcl818hg", 12, 16, 8, 10000, 1, 16, 16, &range_pcl818hg_ai,
1483 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1484 0x0a, 0xfff, 0xfff, 1, 1},
1485 {"pcl818", 9, 16, 8, 10000, 2, 16, 16, &range_pcl818h_ai,
1486 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1487 0x0a, 0xfff, 0xfff, 0, 1},
1488 {"pcl718", 1, 16, 8, 16000, 2, 16, 16, &range_unipolar5,
1489 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1490 0x0a, 0xfff, 0xfff, 0, 0},
1491 /* pcm3718 */
1492 {"pcm3718", 9, 16, 8, 10000, 0, 16, 16, &range_pcl818h_ai,
1493 &range_unipolar5, PCLx1x_RANGE, 0x00fc,
1494 0x0a, 0xfff, 0xfff, 0, 1 /* XXX ? */ },
1495};
1496
294f930d 1497static struct comedi_driver pcl818_driver = {
f6aafa10
HS
1498 .driver_name = "pcl818",
1499 .module = THIS_MODULE,
1500 .attach = pcl818_attach,
1501 .detach = pcl818_detach,
1502 .board_name = &boardtypes[0].name,
1503 .num_names = ARRAY_SIZE(boardtypes),
1504 .offset = sizeof(struct pcl818_board),
1505};
294f930d 1506module_comedi_driver(pcl818_driver);
f6aafa10 1507
90f703d3
AT
1508MODULE_AUTHOR("Comedi http://www.comedi.org");
1509MODULE_DESCRIPTION("Comedi low-level driver");
1510MODULE_LICENSE("GPL");