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