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