]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/staging/comedi/drivers/pcl818.c
staging: comedi: pcl812: tidy up the analog output subdevice
[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/* boards constants */
4da6a1d8
MD
114
115#define boardPCL818L 0
116#define boardPCL818H 1
117#define boardPCL818HD 2
118#define boardPCL818HG 3
119#define boardPCL818 4
120#define boardPCL718 5
121
0109253d 122/* W: clear INT request */
4da6a1d8 123#define PCL818_CLRINT 8
0109253d 124/* R: return status byte */
4da6a1d8 125#define PCL818_STATUS 8
0109253d 126/* R: A/D high byte W: A/D range control */
4da6a1d8 127#define PCL818_RANGE 1
0109253d 128/* R: next mux scan channel W: mux scan channel & range control pointer */
4da6a1d8 129#define PCL818_MUX 2
0109253d 130/* R/W: operation control register */
4da6a1d8 131#define PCL818_CONTROL 9
0109253d 132/* W: counter enable */
4da6a1d8
MD
133#define PCL818_CNTENABLE 10
134
0109253d 135/* R: low byte of A/D W: soft A/D trigger */
4da6a1d8 136#define PCL818_AD_LO 0
0109253d 137/* R: high byte of A/D W: A/D range control */
4da6a1d8 138#define PCL818_AD_HI 1
0109253d 139/* W: D/A low&high byte */
4da6a1d8
MD
140#define PCL818_DA_LO 4
141#define PCL818_DA_HI 5
4ab490b3
HS
142#define PCL818_DO_DI_LSB_REG 0x03
143#define PCL818_DO_DI_MSB_REG 0x0b
0109253d 144/* W: PCL718 second D/A */
4da6a1d8
MD
145#define PCL718_DA2_LO 6
146#define PCL718_DA2_HI 7
833b458a
HS
147
148#define PCL818_TIMER_BASE 0x0c
4da6a1d8 149
0109253d 150/* W: fifo enable/disable */
4da6a1d8 151#define PCL818_FI_ENABLE 6
0109253d 152/* W: fifo interrupt clear */
4da6a1d8 153#define PCL818_FI_INTCLR 20
0109253d 154/* W: fifo interrupt clear */
4da6a1d8 155#define PCL818_FI_FLUSH 25
0109253d 156/* R: fifo status */
4da6a1d8 157#define PCL818_FI_STATUS 25
0109253d 158/* R: one record from FIFO */
4da6a1d8 159#define PCL818_FI_DATALO 23
61ef4719 160#define PCL818_FI_DATAHI 24
4da6a1d8 161
4da6a1d8
MD
162#define MAGIC_DMA_WORD 0x5a5a
163
4cdd4eb2
HS
164static const struct comedi_lrange range_pcl818h_ai = {
165 9, {
166 BIP_RANGE(5),
167 BIP_RANGE(2.5),
168 BIP_RANGE(1.25),
169 BIP_RANGE(0.625),
170 UNI_RANGE(10),
171 UNI_RANGE(5),
172 UNI_RANGE(2.5),
173 UNI_RANGE(1.25),
174 BIP_RANGE(10)
175 }
4da6a1d8
MD
176};
177
4cdd4eb2
HS
178static const struct comedi_lrange range_pcl818hg_ai = {
179 10, {
180 BIP_RANGE(5),
181 BIP_RANGE(0.5),
182 BIP_RANGE(0.05),
183 BIP_RANGE(0.005),
184 UNI_RANGE(10),
185 UNI_RANGE(1),
186 UNI_RANGE(0.1),
187 UNI_RANGE(0.01),
188 BIP_RANGE(10),
189 BIP_RANGE(1),
190 BIP_RANGE(0.1),
191 BIP_RANGE(0.01)
192 }
4da6a1d8
MD
193};
194
4cdd4eb2
HS
195static const struct comedi_lrange range_pcl818l_l_ai = {
196 4, {
197 BIP_RANGE(5),
198 BIP_RANGE(2.5),
199 BIP_RANGE(1.25),
200 BIP_RANGE(0.625)
201 }
4da6a1d8
MD
202};
203
4cdd4eb2
HS
204static const struct comedi_lrange range_pcl818l_h_ai = {
205 4, {
206 BIP_RANGE(10),
207 BIP_RANGE(5),
208 BIP_RANGE(2.5),
209 BIP_RANGE(1.25)
210 }
211};
212
213static const struct comedi_lrange range718_bipolar1 = {
214 1, {
215 BIP_RANGE(1)
216 }
4da6a1d8
MD
217};
218
74c7c503 219static const struct comedi_lrange range718_bipolar0_5 = {
4cdd4eb2
HS
220 1, {
221 BIP_RANGE(0.5)
222 }
223};
224
225static const struct comedi_lrange range718_unipolar2 = {
226 1, {
227 UNI_RANGE(2)
228 }
229};
230
231static const struct comedi_lrange range718_unipolar1 = {
232 1, {
233 BIP_RANGE(1)
234 }
235};
4da6a1d8 236
4634b815 237struct pcl818_board {
43f1b6e9 238 const char *name;
43f1b6e9
HS
239 unsigned int ns_min;
240 int n_aochan;
43f1b6e9 241 const struct comedi_lrange *ai_range_type;
4ba4a2d3 242 unsigned int has_dma:1;
d6125588 243 unsigned int has_fifo:1;
9acf56f2 244 unsigned int is_818:1;
4634b815
BP
245};
246
43f1b6e9
HS
247static const struct pcl818_board boardtypes[] = {
248 {
249 .name = "pcl818l",
43f1b6e9
HS
250 .ns_min = 25000,
251 .n_aochan = 1,
43f1b6e9 252 .ai_range_type = &range_pcl818l_l_ai,
4ba4a2d3 253 .has_dma = 1,
43f1b6e9
HS
254 .is_818 = 1,
255 }, {
256 .name = "pcl818h",
43f1b6e9
HS
257 .ns_min = 10000,
258 .n_aochan = 1,
43f1b6e9 259 .ai_range_type = &range_pcl818h_ai,
4ba4a2d3 260 .has_dma = 1,
43f1b6e9
HS
261 .is_818 = 1,
262 }, {
263 .name = "pcl818hd",
43f1b6e9
HS
264 .ns_min = 10000,
265 .n_aochan = 1,
43f1b6e9 266 .ai_range_type = &range_pcl818h_ai,
4ba4a2d3 267 .has_dma = 1,
d6125588 268 .has_fifo = 1,
43f1b6e9
HS
269 .is_818 = 1,
270 }, {
271 .name = "pcl818hg",
43f1b6e9
HS
272 .ns_min = 10000,
273 .n_aochan = 1,
43f1b6e9 274 .ai_range_type = &range_pcl818hg_ai,
4ba4a2d3 275 .has_dma = 1,
d6125588 276 .has_fifo = 1,
43f1b6e9
HS
277 .is_818 = 1,
278 }, {
279 .name = "pcl818",
43f1b6e9
HS
280 .ns_min = 10000,
281 .n_aochan = 2,
43f1b6e9 282 .ai_range_type = &range_pcl818h_ai,
4ba4a2d3 283 .has_dma = 1,
43f1b6e9
HS
284 .is_818 = 1,
285 }, {
286 .name = "pcl718",
43f1b6e9
HS
287 .ns_min = 16000,
288 .n_aochan = 2,
43f1b6e9 289 .ai_range_type = &range_unipolar5,
4ba4a2d3 290 .has_dma = 1,
43f1b6e9
HS
291 }, {
292 .name = "pcm3718",
43f1b6e9 293 .ns_min = 10000,
43f1b6e9 294 .ai_range_type = &range_pcl818h_ai,
4ba4a2d3 295 .has_dma = 1,
43f1b6e9
HS
296 .is_818 = 1,
297 },
298};
299
087ea31b 300struct pcl818_private {
0109253d 301 unsigned int dma; /* used DMA, 0=don't use DMA */
f5cc425a
HS
302 unsigned int dmapages;
303 unsigned int hwdmasize;
0109253d 304 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
0109253d 305 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
0109253d
BP
306 int next_dma_buf; /* which DMA buffer will be used next round */
307 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
308 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
39eaedb6 309 unsigned int ns_min; /* manimal allowed delay between samples (in us) for actual card */
0109253d 310 int i8253_osc_base; /* 1/frequency of on board oscilator in ns */
0109253d
BP
311 int ai_act_scan; /* how many scans we finished */
312 int ai_act_chan; /* actual position in actual scan */
313 unsigned int act_chanlist[16]; /* MUX setting for actual AI operations */
314 unsigned int act_chanlist_len; /* how long is actual MUX list */
315 unsigned int act_chanlist_pos; /* actual position in MUX list */
0109253d 316 unsigned int ai_data_len; /* len of data buffer */
790c5541 317 unsigned int ao_readback[2];
f4985a79
HS
318 unsigned int divisor1;
319 unsigned int divisor2;
e5143adb 320 unsigned int usefifo:1;
c8bc43ec 321 unsigned int ai_cmd_running:1;
905a8321 322 unsigned int ai_cmd_canceled:1;
087ea31b
BP
323};
324
0109253d 325static const unsigned int muxonechan[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, /* used for gain list programming */
4da6a1d8
MD
326 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
327};
328
0a85b6f0
MT
329static void setup_channel_list(struct comedi_device *dev,
330 struct comedi_subdevice *s,
331 unsigned int *chanlist, unsigned int n_chan,
332 unsigned int seglen);
333static int check_channel_list(struct comedi_device *dev,
334 struct comedi_subdevice *s,
335 unsigned int *chanlist, unsigned int n_chan);
336
f4985a79 337static void pcl818_start_pacer(struct comedi_device *dev, bool load_counters)
833b458a 338{
f4985a79 339 struct pcl818_private *devpriv = dev->private;
833b458a
HS
340 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
341
342 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
343 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
344 udelay(1);
345
346 if (load_counters) {
f4985a79
HS
347 i8254_write(timer_base, 0, 2, devpriv->divisor2);
348 i8254_write(timer_base, 0, 1, devpriv->divisor1);
833b458a
HS
349 }
350}
4da6a1d8 351
ab72ef30
HS
352static void pcl818_ai_setup_dma(struct comedi_device *dev,
353 struct comedi_subdevice *s)
354{
355 struct pcl818_private *devpriv = dev->private;
356 struct comedi_cmd *cmd = &s->async->cmd;
357 unsigned int flags;
358 unsigned int bytes;
359
360 disable_dma(devpriv->dma); /* disable dma */
361 bytes = devpriv->hwdmasize;
362 if (cmd->stop_src == TRIG_COUNT) {
363 bytes = cmd->chanlist_len * cmd->stop_arg * sizeof(short);
364 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
365 devpriv->last_dma_run = bytes % devpriv->hwdmasize;
366 devpriv->dma_runs_to_end--;
367 if (devpriv->dma_runs_to_end >= 0)
368 bytes = devpriv->hwdmasize;
369 }
370
371 devpriv->next_dma_buf = 0;
372 set_dma_mode(devpriv->dma, DMA_MODE_READ);
373 flags = claim_dma_lock();
374 clear_dma_ff(devpriv->dma);
375 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
376 set_dma_count(devpriv->dma, bytes);
377 release_dma_lock(flags);
378 enable_dma(devpriv->dma);
379}
380
381static void pcl818_ai_setup_next_dma(struct comedi_device *dev,
382 struct comedi_subdevice *s)
383{
384 struct pcl818_private *devpriv = dev->private;
385 struct comedi_cmd *cmd = &s->async->cmd;
386 unsigned long flags;
387
388 disable_dma(devpriv->dma);
389 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
390 if (devpriv->dma_runs_to_end > -1 || cmd->stop_src == TRIG_NONE) {
391 /* switch dma bufs */
392 set_dma_mode(devpriv->dma, DMA_MODE_READ);
393 flags = claim_dma_lock();
394 set_dma_addr(devpriv->dma,
395 devpriv->hwdmaptr[devpriv->next_dma_buf]);
396 if (devpriv->dma_runs_to_end || cmd->stop_src == TRIG_NONE)
397 set_dma_count(devpriv->dma, devpriv->hwdmasize);
398 else
399 set_dma_count(devpriv->dma, devpriv->last_dma_run);
400 release_dma_lock(flags);
401 enable_dma(devpriv->dma);
402 }
403
404 devpriv->dma_runs_to_end--;
405}
406
4206e1be
HS
407static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device *dev,
408 struct comedi_subdevice *s,
409 unsigned int *chan)
410{
411 unsigned int val;
412
413 val = inb(dev->iobase + PCL818_FI_DATALO);
414 val |= (inb(dev->iobase + PCL818_FI_DATAHI) << 8);
415
416 if (chan)
417 *chan = val & 0xf;
418
419 return (val >> 4) & s->maxdata;
420}
421
8fc9f652
HS
422static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
423 struct comedi_subdevice *s,
424 unsigned int *chan)
425{
426 unsigned int val;
427
428 val = inb(dev->iobase + PCL818_AD_HI) << 8;
429 val |= inb(dev->iobase + PCL818_AD_LO);
430
431 if (chan)
432 *chan = val & 0xf;
433
434 return (val >> 4) & s->maxdata;
435}
436
1d6f4af9
HS
437static int pcl818_ai_eoc(struct comedi_device *dev,
438 struct comedi_subdevice *s,
439 struct comedi_insn *insn,
440 unsigned long context)
441{
442 unsigned int status;
443
444 status = inb(dev->iobase + PCL818_STATUS);
445 if (status & 0x10)
446 return 0;
447 return -EBUSY;
448}
449
0a85b6f0
MT
450static int pcl818_ai_insn_read(struct comedi_device *dev,
451 struct comedi_subdevice *s,
452 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 453{
1d6f4af9 454 int ret;
4da6a1d8 455 int n;
4da6a1d8
MD
456
457 /* software trigger, DMA and INT off */
458 outb(0, dev->iobase + PCL818_CONTROL);
459
460 /* select channel */
461 outb(muxonechan[CR_CHAN(insn->chanspec)], dev->iobase + PCL818_MUX);
462
463 /* select gain */
464 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL818_RANGE);
465
466 for (n = 0; n < insn->n; n++) {
467
468 /* clear INT (conversion end) flag */
469 outb(0, dev->iobase + PCL818_CLRINT);
470
471 /* start conversion */
472 outb(0, dev->iobase + PCL818_AD_LO);
473
1d6f4af9
HS
474 ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
475 if (ret) {
1d6f4af9
HS
476 /* clear INT (conversion end) flag */
477 outb(0, dev->iobase + PCL818_CLRINT);
478 return ret;
4da6a1d8 479 }
4da6a1d8 480
8fc9f652 481 data[n] = pcl818_ai_get_sample(dev, s, NULL);
4da6a1d8
MD
482 }
483
484 return n;
485}
486
0a85b6f0
MT
487static int pcl818_ao_insn_read(struct comedi_device *dev,
488 struct comedi_subdevice *s,
489 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 490{
9a1a6cf8 491 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
492 int n;
493 int chan = CR_CHAN(insn->chanspec);
494
fc950139 495 for (n = 0; n < insn->n; n++)
4da6a1d8 496 data[n] = devpriv->ao_readback[chan];
4da6a1d8
MD
497
498 return n;
499}
500
0a85b6f0
MT
501static int pcl818_ao_insn_write(struct comedi_device *dev,
502 struct comedi_subdevice *s,
503 struct comedi_insn *insn, unsigned int *data)
4da6a1d8 504{
9a1a6cf8 505 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
506 int n;
507 int chan = CR_CHAN(insn->chanspec);
508
509 for (n = 0; n < insn->n; n++) {
510 devpriv->ao_readback[chan] = data[n];
511 outb((data[n] & 0x000f) << 4, dev->iobase +
0a85b6f0 512 (chan ? PCL718_DA2_LO : PCL818_DA_LO));
4da6a1d8 513 outb((data[n] & 0x0ff0) >> 4, dev->iobase +
0a85b6f0 514 (chan ? PCL718_DA2_HI : PCL818_DA_HI));
4da6a1d8
MD
515 }
516
517 return n;
518}
519
a8f461c2
HS
520static bool pcl818_ai_dropout(struct comedi_device *dev,
521 struct comedi_subdevice *s,
522 unsigned int chan)
523{
524 struct pcl818_private *devpriv = dev->private;
525 unsigned int expected_chan;
526
527 expected_chan = devpriv->act_chanlist[devpriv->act_chanlist_pos];
528 if (chan != expected_chan) {
529 dev_dbg(dev->class_dev,
530 "A/D mode1/3 %s - channel dropout %d!=%d !\n",
531 (devpriv->dma) ? "DMA" :
532 (devpriv->usefifo) ? "FIFO" : "IRQ",
533 chan, expected_chan);
534 s->cancel(dev, s);
535 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
a8f461c2
HS
536 return true;
537 }
538 return false;
539}
540
6d5b7858
HS
541static bool pcl818_ai_next_chan(struct comedi_device *dev,
542 struct comedi_subdevice *s)
543{
544 struct pcl818_private *devpriv = dev->private;
545 struct comedi_cmd *cmd = &s->async->cmd;
546
547 s->async->events |= COMEDI_CB_BLOCK;
548
549 devpriv->act_chanlist_pos++;
550 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
551 devpriv->act_chanlist_pos = 0;
552
553 s->async->cur_chan++;
554 if (s->async->cur_chan >= cmd->chanlist_len) {
555 s->async->cur_chan = 0;
556 devpriv->ai_act_scan--;
557 s->async->events |= COMEDI_CB_EOS;
558 }
559
560 if (cmd->stop_src == TRIG_COUNT && devpriv->ai_act_scan == 0) {
561 /* all data sampled */
562 s->cancel(dev, s);
563 s->async->events |= COMEDI_CB_EOA;
6d5b7858
HS
564 return false;
565 }
566
567 return true;
568}
569
e0946940
HS
570static void pcl818_handle_eoc(struct comedi_device *dev,
571 struct comedi_subdevice *s)
4da6a1d8 572{
8fc9f652 573 unsigned int chan;
a8f461c2 574 unsigned int val;
4da6a1d8 575
12700571
HS
576 if (pcl818_ai_eoc(dev, s, NULL, 0)) {
577 outb(0, dev->iobase + PCL818_STATUS); /* clear INT request */
578 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
579 s->cancel(dev, s);
580 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
e0946940 581 return;
4da6a1d8 582 }
12700571 583
a8f461c2 584 val = pcl818_ai_get_sample(dev, s, &chan);
4da6a1d8
MD
585 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
586
a8f461c2 587 if (pcl818_ai_dropout(dev, s, chan))
e0946940 588 return;
a8f461c2
HS
589
590 comedi_buf_put(s->async, val);
fc950139 591
e0946940 592 pcl818_ai_next_chan(dev, s);
4da6a1d8
MD
593}
594
e0946940
HS
595static void pcl818_handle_dma(struct comedi_device *dev,
596 struct comedi_subdevice *s)
4da6a1d8 597{
9a1a6cf8 598 struct pcl818_private *devpriv = dev->private;
4bf59ce2 599 unsigned short *ptr;
a8f461c2
HS
600 unsigned int chan;
601 unsigned int val;
602 int i, len, bufptr;
4da6a1d8 603
ab72ef30 604 pcl818_ai_setup_next_dma(dev, s);
4da6a1d8 605
4bf59ce2 606 ptr = (unsigned short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
4da6a1d8 607
f5cc425a 608 len = devpriv->hwdmasize >> 1;
4da6a1d8
MD
609 bufptr = 0;
610
611 for (i = 0; i < len; i++) {
a8f461c2
HS
612 val = ptr[bufptr++];
613 chan = val & 0xf;
614 val = (val >> 4) & s->maxdata;
615
616 if (pcl818_ai_dropout(dev, s, chan))
e0946940 617 break;
4da6a1d8 618
a8f461c2 619 comedi_buf_put(s->async, val);
4da6a1d8 620
6d5b7858 621 if (!pcl818_ai_next_chan(dev, s))
e0946940 622 break;
4da6a1d8 623 }
4da6a1d8
MD
624}
625
e0946940
HS
626static void pcl818_handle_fifo(struct comedi_device *dev,
627 struct comedi_subdevice *s)
4da6a1d8 628{
4206e1be
HS
629 unsigned int status;
630 unsigned int chan;
631 unsigned int val;
4bf59ce2 632 int i, len;
4da6a1d8 633
4206e1be 634 status = inb(dev->iobase + PCL818_FI_STATUS);
4da6a1d8 635
4206e1be 636 if (status & 4) {
4da6a1d8 637 comedi_error(dev, "A/D mode1/3 FIFO overflow!");
6c42119d 638 s->cancel(dev, s);
4da6a1d8 639 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
e0946940 640 return;
4da6a1d8
MD
641 }
642
4206e1be 643 if (status & 1) {
4da6a1d8 644 comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
6c42119d 645 s->cancel(dev, s);
4da6a1d8 646 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
e0946940 647 return;
4da6a1d8
MD
648 }
649
4206e1be 650 if (status & 2)
4da6a1d8 651 len = 512;
fc950139 652 else
4da6a1d8 653 len = 0;
4da6a1d8
MD
654
655 for (i = 0; i < len; i++) {
4206e1be 656 val = pcl818_ai_get_fifo_sample(dev, s, &chan);
a8f461c2
HS
657
658 if (pcl818_ai_dropout(dev, s, chan))
e0946940 659 break;
4da6a1d8 660
4206e1be 661 comedi_buf_put(s->async, val);
4da6a1d8 662
6d5b7858 663 if (!pcl818_ai_next_chan(dev, s))
e0946940 664 break;
4da6a1d8 665 }
4da6a1d8
MD
666}
667
e0946940 668static irqreturn_t pcl818_interrupt(int irq, void *d)
4da6a1d8 669{
71b5f4f1 670 struct comedi_device *dev = d;
9a1a6cf8 671 struct pcl818_private *devpriv = dev->private;
6c42119d 672 struct comedi_subdevice *s = dev->read_subdev;
4da6a1d8 673
799f89ce 674 if (!dev->attached || !devpriv->ai_cmd_running) {
10933a9c 675 outb(0, dev->iobase + PCL818_CLRINT);
4da6a1d8
MD
676 return IRQ_HANDLED;
677 }
4da6a1d8 678
905a8321
HS
679 if (devpriv->ai_cmd_canceled) {
680 /*
681 * The cleanup from ai_cancel() has been delayed
682 * until now because the card doesn't seem to like
683 * being reprogrammed while a DMA transfer is in
684 * progress.
685 */
686 devpriv->ai_act_scan = 0;
687 s->cancel(dev, s);
e0946940 688 outb(0, dev->iobase + PCL818_CLRINT);
e21de1a8
IA
689 return IRQ_HANDLED;
690 }
691
799f89ce 692 if (devpriv->dma)
e0946940 693 pcl818_handle_dma(dev, s);
799f89ce 694 else if (devpriv->usefifo)
e0946940 695 pcl818_handle_fifo(dev, s);
799f89ce 696 else
e0946940
HS
697 pcl818_handle_eoc(dev, s);
698
699 outb(0, dev->iobase + PCL818_CLRINT);
700
701 comedi_event(dev, s);
702 return IRQ_HANDLED;
4da6a1d8
MD
703}
704
da91b269 705static void pcl818_ai_mode13dma_int(int mode, struct comedi_device *dev,
0a85b6f0 706 struct comedi_subdevice *s)
4da6a1d8 707{
ab72ef30 708 pcl818_ai_setup_dma(dev, s);
4da6a1d8 709
799f89ce
HS
710 if (mode == 1) /* Pacer+IRQ+DMA */
711 outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);
712 else /* Ext trig+IRQ+DMA */
713 outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL);
4da6a1d8
MD
714}
715
da91b269 716static int pcl818_ai_cmd_mode(int mode, struct comedi_device *dev,
0a85b6f0 717 struct comedi_subdevice *s)
4da6a1d8 718{
9a1a6cf8 719 struct pcl818_private *devpriv = dev->private;
ea6d0d4c 720 struct comedi_cmd *cmd = &s->async->cmd;
4da6a1d8
MD
721 unsigned int seglen;
722
c8bc43ec 723 if (devpriv->ai_cmd_running)
4da6a1d8
MD
724 return -EBUSY;
725
f4985a79 726 pcl818_start_pacer(dev, false);
4da6a1d8 727
1784f305 728 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
4da6a1d8
MD
729 if (seglen < 1)
730 return -EINVAL;
1784f305 731 setup_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len, seglen);
4da6a1d8 732
5f74ea14 733 udelay(1);
4da6a1d8 734
c2e519dd 735 devpriv->ai_act_scan = cmd->stop_arg;
4da6a1d8 736 devpriv->ai_act_chan = 0;
c8bc43ec 737 devpriv->ai_cmd_running = 1;
905a8321 738 devpriv->ai_cmd_canceled = 0;
4da6a1d8
MD
739 devpriv->act_chanlist_pos = 0;
740 devpriv->dma_runs_to_end = 0;
741
4da6a1d8
MD
742 outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
743
744 switch (devpriv->dma) {
0109253d 745 case 1: /* DMA */
4da6a1d8 746 case 3:
aecfd1ec 747 pcl818_ai_mode13dma_int(mode, dev, s);
4da6a1d8 748 break;
a71f18d2
IA
749 case 0:
750 if (!devpriv->usefifo) {
751 /* IRQ */
799f89ce 752 if (mode == 1) /* Pacer+IRQ */
0a85b6f0
MT
753 outb(0x83 | (dev->irq << 4),
754 dev->iobase + PCL818_CONTROL);
799f89ce 755 else /* Ext trig+IRQ */
0a85b6f0
MT
756 outb(0x82 | (dev->irq << 4),
757 dev->iobase + PCL818_CONTROL);
4da6a1d8 758 } else {
a71f18d2
IA
759 /* FIFO */
760 /* enable FIFO */
761 outb(1, dev->iobase + PCL818_FI_ENABLE);
799f89ce 762 if (mode == 1) /* Pacer */
a71f18d2 763 outb(0x03, dev->iobase + PCL818_CONTROL);
799f89ce 764 else /* Ext trig */
a71f18d2 765 outb(0x02, dev->iobase + PCL818_CONTROL);
a71f18d2 766 }
4da6a1d8
MD
767 }
768
f4985a79 769 pcl818_start_pacer(dev, mode == 1);
4da6a1d8 770
4da6a1d8
MD
771 return 0;
772}
773
0a85b6f0
MT
774static int check_channel_list(struct comedi_device *dev,
775 struct comedi_subdevice *s,
776 unsigned int *chanlist, unsigned int n_chan)
4da6a1d8
MD
777{
778 unsigned int chansegment[16];
779 unsigned int i, nowmustbechan, seglen, segpos;
780
781 /* correct channel and range number check itself comedi/range.c */
782 if (n_chan < 1) {
783 comedi_error(dev, "range/channel list is empty!");
784 return 0;
785 }
786
787 if (n_chan > 1) {
25985edc 788 /* first channel is every time ok */
4da6a1d8 789 chansegment[0] = chanlist[0];
0109253d 790 /* build part of chanlist */
4da6a1d8 791 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
0109253d
BP
792 /* we detect loop, this must by finish */
793
4da6a1d8
MD
794 if (chanlist[0] == chanlist[i])
795 break;
796 nowmustbechan =
0a85b6f0 797 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
25985edc 798 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
84f03cf1
HS
799 dev_dbg(dev->class_dev,
800 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
801 i, CR_CHAN(chanlist[i]), nowmustbechan,
802 CR_CHAN(chanlist[0]));
4da6a1d8
MD
803 return 0;
804 }
0109253d 805 /* well, this is next correct channel in list */
4da6a1d8
MD
806 chansegment[i] = chanlist[i];
807 }
808
0109253d 809 /* check whole chanlist */
4da6a1d8 810 for (i = 0, segpos = 0; i < n_chan; i++) {
4da6a1d8 811 if (chanlist[i] != chansegment[i % seglen]) {
84f03cf1
HS
812 dev_dbg(dev->class_dev,
813 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
814 i, CR_CHAN(chansegment[i]),
815 CR_RANGE(chansegment[i]),
816 CR_AREF(chansegment[i]),
817 CR_CHAN(chanlist[i % seglen]),
818 CR_RANGE(chanlist[i % seglen]),
819 CR_AREF(chansegment[i % seglen]));
0109253d 820 return 0; /* chan/gain list is strange */
4da6a1d8
MD
821 }
822 }
823 } else {
824 seglen = 1;
825 }
4da6a1d8
MD
826 return seglen;
827}
828
0a85b6f0
MT
829static void setup_channel_list(struct comedi_device *dev,
830 struct comedi_subdevice *s,
831 unsigned int *chanlist, unsigned int n_chan,
832 unsigned int seglen)
4da6a1d8 833{
9a1a6cf8 834 struct pcl818_private *devpriv = dev->private;
4da6a1d8
MD
835 int i;
836
837 devpriv->act_chanlist_len = seglen;
838 devpriv->act_chanlist_pos = 0;
839
0109253d 840 for (i = 0; i < seglen; i++) { /* store range list to card */
4da6a1d8
MD
841 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
842 outb(muxonechan[CR_CHAN(chanlist[i])], dev->iobase + PCL818_MUX); /* select channel */
843 outb(CR_RANGE(chanlist[i]), dev->iobase + PCL818_RANGE); /* select gain */
844 }
845
5f74ea14 846 udelay(1);
4da6a1d8
MD
847
848 /* select channel interval to scan */
849 outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen -
0a85b6f0
MT
850 1] << 4),
851 dev->iobase + PCL818_MUX);
4da6a1d8
MD
852}
853
4da6a1d8
MD
854static int check_single_ended(unsigned int port)
855{
fc950139 856 if (inb(port + PCL818_STATUS) & 0x20)
4da6a1d8 857 return 1;
fc950139 858 return 0;
4da6a1d8
MD
859}
860
da91b269 861static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 862 struct comedi_cmd *cmd)
4da6a1d8 863{
dd8a4b47 864 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 865 struct pcl818_private *devpriv = dev->private;
4da6a1d8 866 int err = 0;
f4985a79 867 int tmp;
4da6a1d8 868
27020ffe 869 /* Step 1 : check if triggers are trivially valid */
4da6a1d8 870
27020ffe
HS
871 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
872 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
873 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
874 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
875 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
4da6a1d8 876
fc950139 877 if (err)
4da6a1d8 878 return 1;
4da6a1d8 879
27020ffe 880 /* Step 2a : make sure trigger sources are unique */
4da6a1d8 881
27020ffe
HS
882 err |= cfc_check_trigger_is_unique(cmd->convert_src);
883 err |= cfc_check_trigger_is_unique(cmd->stop_src);
4da6a1d8 884
27020ffe 885 /* Step 2b : and mutually compatible */
4da6a1d8 886
fc950139 887 if (err)
4da6a1d8 888 return 2;
4da6a1d8 889
8efdc1bf 890 /* Step 3: check if arguments are trivially valid */
4da6a1d8 891
8efdc1bf
HS
892 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
893 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
4da6a1d8 894
8efdc1bf
HS
895 if (cmd->convert_src == TRIG_TIMER)
896 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
897 board->ns_min);
898 else /* TRIG_EXT */
899 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
4da6a1d8 900
8efdc1bf 901 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
4da6a1d8 902
8efdc1bf
HS
903 if (cmd->stop_src == TRIG_COUNT)
904 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
905 else /* TRIG_NONE */
906 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
4da6a1d8 907
fc950139 908 if (err)
4da6a1d8 909 return 3;
4da6a1d8
MD
910
911 /* step 4: fix up any arguments */
912
913 if (cmd->convert_src == TRIG_TIMER) {
914 tmp = cmd->convert_arg;
cb9cfd7e 915 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
f4985a79
HS
916 &devpriv->divisor1,
917 &devpriv->divisor2,
cb9cfd7e 918 &cmd->convert_arg, cmd->flags);
dd8a4b47
HS
919 if (cmd->convert_arg < board->ns_min)
920 cmd->convert_arg = board->ns_min;
4da6a1d8
MD
921 if (tmp != cmd->convert_arg)
922 err++;
923 }
924
fc950139 925 if (err)
4da6a1d8 926 return 4;
4da6a1d8
MD
927
928 /* step 5: complain about special chanlist considerations */
929
930 if (cmd->chanlist) {
931 if (!check_channel_list(dev, s, cmd->chanlist,
0a85b6f0 932 cmd->chanlist_len))
0109253d 933 return 5; /* incorrect channels list */
4da6a1d8
MD
934 }
935
936 return 0;
937}
938
da91b269 939static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
4da6a1d8 940{
9a1a6cf8 941 struct pcl818_private *devpriv = dev->private;
ea6d0d4c 942 struct comedi_cmd *cmd = &s->async->cmd;
4da6a1d8
MD
943 int retval;
944
4da6a1d8 945 devpriv->ai_data_len = s->async->prealloc_bufsz;
4da6a1d8 946
0109253d
BP
947 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 3 */
948 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 */
4da6a1d8 949 retval = pcl818_ai_cmd_mode(1, dev, s);
4da6a1d8
MD
950 return retval;
951 }
0109253d 952 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
4da6a1d8
MD
953 return pcl818_ai_cmd_mode(3, dev, s);
954 }
955 }
956
957 return -1;
958}
959
0a85b6f0
MT
960static int pcl818_ai_cancel(struct comedi_device *dev,
961 struct comedi_subdevice *s)
4da6a1d8 962{
9a1a6cf8 963 struct pcl818_private *devpriv = dev->private;
00aba6e7 964 struct comedi_cmd *cmd = &s->async->cmd;
9a1a6cf8 965
2850b1c7
HS
966 if (!devpriv->ai_cmd_running)
967 return 0;
968
799f89ce 969 if (devpriv->dma) {
2850b1c7
HS
970 if (cmd->stop_src == TRIG_NONE ||
971 (cmd->stop_src == TRIG_COUNT && devpriv->ai_act_scan > 0)) {
905a8321
HS
972 if (!devpriv->ai_cmd_canceled) {
973 /*
974 * Wait for running dma transfer to end,
975 * do cleanup in interrupt.
976 */
977 devpriv->ai_cmd_canceled = 1;
978 return 0;
979 }
4da6a1d8 980 }
2850b1c7 981 disable_dma(devpriv->dma);
4da6a1d8
MD
982 }
983
799f89ce
HS
984 outb(inb(dev->iobase + PCL818_CONTROL) & 0x73,
985 dev->iobase + PCL818_CONTROL); /* Stop A/D */
986 udelay(1);
987 pcl818_start_pacer(dev, false);
988 outb(0, dev->iobase + PCL818_AD_LO);
989 pcl818_ai_get_sample(dev, s, NULL);
990 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
991 outb(0, dev->iobase + PCL818_CONTROL); /* Stop A/D */
992 if (devpriv->usefifo) { /* FIFO shutdown */
993 outb(0, dev->iobase + PCL818_FI_INTCLR);
994 outb(0, dev->iobase + PCL818_FI_FLUSH);
995 outb(0, dev->iobase + PCL818_FI_ENABLE);
996 }
997 devpriv->ai_cmd_running = 0;
998 devpriv->ai_cmd_canceled = 0;
999
4da6a1d8
MD
1000 return 0;
1001}
1002
4ab490b3
HS
1003static int pcl818_di_insn_bits(struct comedi_device *dev,
1004 struct comedi_subdevice *s,
1005 struct comedi_insn *insn,
1006 unsigned int *data)
1007{
1008 data[1] = inb(dev->iobase + PCL818_DO_DI_LSB_REG) |
1009 (inb(dev->iobase + PCL818_DO_DI_MSB_REG) << 8);
1010
1011 return insn->n;
1012}
1013
1014static int pcl818_do_insn_bits(struct comedi_device *dev,
1015 struct comedi_subdevice *s,
1016 struct comedi_insn *insn,
1017 unsigned int *data)
1018{
1019 if (comedi_dio_update_state(s, data)) {
1020 outb(s->state & 0xff, dev->iobase + PCL818_DO_DI_LSB_REG);
1021 outb((s->state >> 8), dev->iobase + PCL818_DO_DI_MSB_REG);
1022 }
1023
1024 data[1] = s->state;
1025
1026 return insn->n;
1027}
1028
da91b269 1029static void pcl818_reset(struct comedi_device *dev)
4da6a1d8 1030{
dd8a4b47 1031 const struct pcl818_board *board = comedi_board(dev);
833b458a 1032 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
dd8a4b47 1033
80cc6486
HS
1034 /* flush and disable the FIFO */
1035 if (board->has_fifo) {
4da6a1d8
MD
1036 outb(0, dev->iobase + PCL818_FI_INTCLR);
1037 outb(0, dev->iobase + PCL818_FI_FLUSH);
1038 outb(0, dev->iobase + PCL818_FI_ENABLE);
1039 }
0109253d 1040 outb(0, dev->iobase + PCL818_DA_LO); /* DAC=0V */
4da6a1d8 1041 outb(0, dev->iobase + PCL818_DA_HI);
5f74ea14 1042 udelay(1);
4ab490b3
HS
1043 outb(0, dev->iobase + PCL818_DO_DI_MSB_REG);
1044 outb(0, dev->iobase + PCL818_DO_DI_LSB_REG);
5f74ea14 1045 udelay(1);
4da6a1d8
MD
1046 outb(0, dev->iobase + PCL818_CONTROL);
1047 outb(0, dev->iobase + PCL818_CNTENABLE);
1048 outb(0, dev->iobase + PCL818_MUX);
1049 outb(0, dev->iobase + PCL818_CLRINT);
833b458a
HS
1050
1051 /* Stop pacer */
1052 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
1053 i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
1054 i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
1055
dd8a4b47 1056 if (board->is_818) {
4da6a1d8
MD
1057 outb(0, dev->iobase + PCL818_RANGE);
1058 } else {
1059 outb(0, dev->iobase + PCL718_DA2_LO);
1060 outb(0, dev->iobase + PCL718_DA2_HI);
1061 }
1062}
1063
f39b8ccf
HS
1064static void pcl818_set_ai_range_table(struct comedi_device *dev,
1065 struct comedi_subdevice *s,
1066 struct comedi_devconfig *it)
1067{
1068 const struct pcl818_board *board = comedi_board(dev);
1069
1070 /* default to the range table from the boardinfo */
1071 s->range_table = board->ai_range_type;
1072
1073 /* now check the user config option based on the boardtype */
1074 if (board->is_818) {
1075 if (it->options[4] == 1 || it->options[4] == 10) {
1076 /* secondary range list jumper selectable */
1077 s->range_table = &range_pcl818l_h_ai;
1078 }
1079 } else {
1080 switch (it->options[4]) {
1081 case 0:
1082 s->range_table = &range_bipolar10;
1083 break;
1084 case 1:
1085 s->range_table = &range_bipolar5;
1086 break;
1087 case 2:
1088 s->range_table = &range_bipolar2_5;
1089 break;
1090 case 3:
1091 s->range_table = &range718_bipolar1;
1092 break;
1093 case 4:
1094 s->range_table = &range718_bipolar0_5;
1095 break;
1096 case 6:
1097 s->range_table = &range_unipolar10;
1098 break;
1099 case 7:
1100 s->range_table = &range_unipolar5;
1101 break;
1102 case 8:
1103 s->range_table = &range718_unipolar2;
1104 break;
1105 case 9:
1106 s->range_table = &range718_unipolar1;
1107 break;
1108 default:
1109 s->range_table = &range_unknown;
1110 break;
1111 }
1112 }
1113}
1114
da91b269 1115static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
4da6a1d8 1116{
dd8a4b47 1117 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 1118 struct pcl818_private *devpriv;
34c43922 1119 struct comedi_subdevice *s;
f5cc425a
HS
1120 int ret;
1121 int i;
4da6a1d8 1122
0bdab509 1123 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
1124 if (!devpriv)
1125 return -ENOMEM;
4da6a1d8 1126
d6125588 1127 ret = comedi_request_region(dev, it->options[0],
80cc6486 1128 board->has_fifo ? 0x20 : 0x10);
d6c5ec04
HS
1129 if (ret)
1130 return ret;
4da6a1d8 1131
8356d4b4
HS
1132 /* we can use IRQ 2-7 for async command support */
1133 if (it->options[1] >= 2 && it->options[1] <= 7) {
e0946940 1134 ret = request_irq(it->options[1], pcl818_interrupt, 0,
e30b22a9 1135 dev->board_name, dev);
35a8735d 1136 if (ret == 0)
e30b22a9 1137 dev->irq = it->options[1];
4da6a1d8
MD
1138 }
1139
80cc6486
HS
1140 /* should we use the FIFO? */
1141 if (dev->irq && board->has_fifo && it->options[2] == -1)
1142 devpriv->usefifo = 1;
1143
4ba4a2d3
HS
1144 /* we need an IRQ to do DMA on channel 3 or 1 */
1145 if (dev->irq && board->has_dma &&
1146 (it->options[2] == 3 || it->options[2] == 1)) {
1147 ret = request_dma(it->options[2], dev->board_name);
1148 if (ret) {
d65e5b9d 1149 dev_err(dev->class_dev,
4ba4a2d3
HS
1150 "unable to request DMA channel %d\n",
1151 it->options[2]);
1152 return -EBUSY;
4da6a1d8 1153 }
4ba4a2d3
HS
1154 devpriv->dma = it->options[2];
1155
f5cc425a
HS
1156 devpriv->dmapages = 2; /* we need 16KB */
1157 devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
1158
1159 for (i = 0; i < 2; i++) {
1160 unsigned long dmabuf;
1161
1162 dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
1163 if (!dmabuf)
1164 return -ENOMEM;
1165
1166 devpriv->dmabuf[i] = dmabuf;
1167 devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
1168 }
4da6a1d8
MD
1169 }
1170
2f0b9d08 1171 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 1172 if (ret)
4da6a1d8
MD
1173 return ret;
1174
9fab6123 1175 s = &dev->subdevices[0];
9c06c4e3
HS
1176 s->type = COMEDI_SUBD_AI;
1177 s->subdev_flags = SDF_READABLE;
1178 if (check_single_ended(dev->iobase)) {
1179 s->n_chan = 16;
1180 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
4da6a1d8 1181 } else {
9c06c4e3
HS
1182 s->n_chan = 8;
1183 s->subdev_flags |= SDF_DIFF;
1184 }
bca1b594 1185 s->maxdata = 0x0fff;
9c06c4e3
HS
1186
1187 pcl818_set_ai_range_table(dev, s, it);
1188
1189 s->insn_read = pcl818_ai_insn_read;
1190 if (dev->irq) {
1191 dev->read_subdev = s;
1192 s->subdev_flags |= SDF_CMD_READ;
1193 s->len_chanlist = s->n_chan;
1194 s->do_cmdtest = ai_cmdtest;
1195 s->do_cmd = ai_cmd;
1196 s->cancel = pcl818_ai_cancel;
4da6a1d8
MD
1197 }
1198
9fab6123 1199 s = &dev->subdevices[1];
dd8a4b47 1200 if (!board->n_aochan) {
4da6a1d8
MD
1201 s->type = COMEDI_SUBD_UNUSED;
1202 } else {
1203 s->type = COMEDI_SUBD_AO;
1204 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
dd8a4b47 1205 s->n_chan = board->n_aochan;
2bfe3eb7 1206 s->maxdata = 0x0fff;
ba93331e 1207 s->range_table = &range_unipolar5;
4da6a1d8
MD
1208 s->insn_read = pcl818_ao_insn_read;
1209 s->insn_write = pcl818_ao_insn_write;
dd8a4b47 1210 if (board->is_818) {
4da6a1d8
MD
1211 if ((it->options[4] == 1) || (it->options[4] == 10))
1212 s->range_table = &range_unipolar10;
1213 if (it->options[4] == 2)
1214 s->range_table = &range_unknown;
1215 } else {
1216 if ((it->options[5] == 1) || (it->options[5] == 10))
1217 s->range_table = &range_unipolar10;
1218 if (it->options[5] == 2)
1219 s->range_table = &range_unknown;
1220 }
1221 }
1222
03d98e6c 1223 /* Digital Input subdevice */
9fab6123 1224 s = &dev->subdevices[2];
03d98e6c
HS
1225 s->type = COMEDI_SUBD_DI;
1226 s->subdev_flags = SDF_READABLE;
1227 s->n_chan = 16;
1228 s->maxdata = 1;
1229 s->range_table = &range_digital;
1230 s->insn_bits = pcl818_di_insn_bits;
1231
1232 /* Digital Output subdevice */
9fab6123 1233 s = &dev->subdevices[3];
03d98e6c
HS
1234 s->type = COMEDI_SUBD_DO;
1235 s->subdev_flags = SDF_WRITABLE;
1236 s->n_chan = 16;
1237 s->maxdata = 1;
1238 s->range_table = &range_digital;
1239 s->insn_bits = pcl818_do_insn_bits;
4da6a1d8
MD
1240
1241 /* select 1/10MHz oscilator */
fc950139 1242 if ((it->options[3] == 0) || (it->options[3] == 10))
cb9cfd7e 1243 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
fc950139 1244 else
cb9cfd7e 1245 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
4da6a1d8
MD
1246
1247 /* max sampling speed */
dd8a4b47 1248 devpriv->ns_min = board->ns_min;
4da6a1d8 1249
dd8a4b47 1250 if (!board->is_818) {
4da6a1d8
MD
1251 if ((it->options[6] == 1) || (it->options[6] == 100))
1252 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1253 }
1254
1255 pcl818_reset(dev);
1256
4da6a1d8
MD
1257 return 0;
1258}
1259
484ecc95 1260static void pcl818_detach(struct comedi_device *dev)
4da6a1d8 1261{
9a1a6cf8
HS
1262 struct pcl818_private *devpriv = dev->private;
1263
1264 if (devpriv) {
89dac49e 1265 pcl818_ai_cancel(dev, dev->read_subdev);
484ecc95
HS
1266 pcl818_reset(dev);
1267 if (devpriv->dma)
1268 free_dma(devpriv->dma);
1269 if (devpriv->dmabuf[0])
f5cc425a 1270 free_pages(devpriv->dmabuf[0], devpriv->dmapages);
484ecc95 1271 if (devpriv->dmabuf[1])
f5cc425a 1272 free_pages(devpriv->dmabuf[1], devpriv->dmapages);
484ecc95 1273 }
a32c6d00 1274 comedi_legacy_detach(dev);
4da6a1d8 1275}
90f703d3 1276
294f930d 1277static struct comedi_driver pcl818_driver = {
f6aafa10
HS
1278 .driver_name = "pcl818",
1279 .module = THIS_MODULE,
1280 .attach = pcl818_attach,
1281 .detach = pcl818_detach,
1282 .board_name = &boardtypes[0].name,
1283 .num_names = ARRAY_SIZE(boardtypes),
1284 .offset = sizeof(struct pcl818_board),
1285};
294f930d 1286module_comedi_driver(pcl818_driver);
f6aafa10 1287
90f703d3
AT
1288MODULE_AUTHOR("Comedi http://www.comedi.org");
1289MODULE_DESCRIPTION("Comedi low-level driver");
1290MODULE_LICENSE("GPL");