staging: comedi: pcl818: absorb pcl818_ai_mode13dma_int()
[deliverable/linux.git] / drivers / staging / comedi / drivers / pcl818.c
CommitLineData
4da6a1d8
MD
1/*
2 comedi/drivers/pcl818.c
3
4 Author: Michal Dobes <dobes@tesnet.cz>
5
6 hardware driver for Advantech cards:
7 card: PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818, PCL-718
8 driver: pcl818l, pcl818h, pcl818hd, pcl818hg, pcl818, pcl718
9*/
10/*
11Driver: pcl818
12Description: Advantech PCL-818 cards, PCL-718
13Author: Michal Dobes <dobes@tesnet.cz>
14Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
15 PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
16 PCL-718 (pcl718)
17Status: works
18
19All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
20Differences are only at maximal sample speed, range list and FIFO
21support.
22The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
23only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
24PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
25but this code is untested.
26A word or two about DMA. Driver support DMA operations at two ways:
271) DMA uses two buffers and after one is filled then is generated
28 INT and DMA restart with second buffer. With this mode I'm unable run
29 more that 80Ksamples/secs without data dropouts on K6/233.
302) DMA uses one buffer and run in autoinit mode and the data are
31 from DMA buffer moved on the fly with 2kHz interrupts from RTC.
32 This mode is used if the interrupt 8 is available for allocation.
33 If not, then first DMA mode is used. With this I can run at
34 full speed one card (100ksamples/secs) or two cards with
35 60ksamples/secs each (more is problem on account of ISA limitations).
36 To use this mode you must have compiled kernel with disabled
37 "Enhanced Real Time Clock Support".
38 Maybe you can have problems if you use xntpd or similar.
39 If you've data dropouts with DMA mode 2 then:
40 a) disable IDE DMA
41 b) switch text mode console to fb.
42
43 Options for PCL-818L:
44 [0] - IO Base
45 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
46 [2] - DMA (0=disable, 1, 3)
47 [3] - 0, 10=10MHz clock for 8254
48 1= 1MHz clock for 8254
49 [4] - 0, 5=A/D input -5V.. +5V
50 1, 10=A/D input -10V..+10V
51 [5] - 0, 5=D/A output 0-5V (internal reference -5V)
52 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 53 2 =D/A output unknown (external reference)
4da6a1d8
MD
54
55 Options for PCL-818, PCL-818H:
56 [0] - IO Base
57 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
58 [2] - DMA (0=disable, 1, 3)
59 [3] - 0, 10=10MHz clock for 8254
60 1= 1MHz clock for 8254
61 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
62 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 63 2 =D/A output unknown (external reference)
4da6a1d8
MD
64
65 Options for PCL-818HD, PCL-818HG:
66 [0] - IO Base
67 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
68 [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
69 1=use DMA ch 1, 3=use DMA ch 3)
70 [3] - 0, 10=10MHz clock for 8254
71 1= 1MHz clock for 8254
72 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
73 1, 10=D/A output 0-10V (internal reference -10V)
bbc9a991 74 2 =D/A output unknown (external reference)
4da6a1d8
MD
75
76 Options for PCL-718:
77 [0] - IO Base
78 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
79 [2] - DMA (0=disable, 1, 3)
80 [3] - 0, 10=10MHz clock for 8254
81 1= 1MHz clock for 8254
82 [4] - 0=A/D Range is +/-10V
83 1= +/-5V
84 2= +/-2.5V
85 3= +/-1V
86 4= +/-0.5V
87 5= user defined bipolar
88 6= 0-10V
89 7= 0-5V
90 8= 0-2V
91 9= 0-1V
92 10= user defined unipolar
93 [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
94 1, 10=D/A outputs 0-10V (internal reference -10V)
bbc9a991 95 2=D/A outputs unknown (external reference)
4da6a1d8
MD
96 [6] - 0, 60=max 60kHz A/D sampling
97 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
98
99*/
100
ce157f80 101#include <linux/module.h>
5a0e3ad6 102#include <linux/gfp.h>
4da6a1d8 103#include <linux/delay.h>
845d131e 104#include <linux/io.h>
aecfd1ec 105#include <linux/interrupt.h>
4da6a1d8
MD
106#include <asm/dma.h>
107
aecfd1ec
HS
108#include "../comedidev.h"
109
27020ffe 110#include "comedi_fc.h"
4da6a1d8
MD
111#include "8253.h"
112
0109253d 113/* 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: counter enable */
4da6a1d8
MD
123#define PCL818_CNTENABLE 10
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)
4ab490b3 147#define PCL818_DO_DI_MSB_REG 0x0b
833b458a 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
0a85b6f0
MT
325static int check_channel_list(struct comedi_device *dev,
326 struct comedi_subdevice *s,
327 unsigned int *chanlist, unsigned int n_chan);
328
f4985a79 329static void pcl818_start_pacer(struct comedi_device *dev, bool load_counters)
833b458a 330{
f4985a79 331 struct pcl818_private *devpriv = dev->private;
833b458a
HS
332 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
333
334 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
335 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
336 udelay(1);
337
338 if (load_counters) {
f4985a79
HS
339 i8254_write(timer_base, 0, 2, devpriv->divisor2);
340 i8254_write(timer_base, 0, 1, devpriv->divisor1);
833b458a
HS
341 }
342}
4da6a1d8 343
ab72ef30
HS
344static void pcl818_ai_setup_dma(struct comedi_device *dev,
345 struct comedi_subdevice *s)
346{
347 struct pcl818_private *devpriv = dev->private;
348 struct comedi_cmd *cmd = &s->async->cmd;
349 unsigned int flags;
350 unsigned int bytes;
351
352 disable_dma(devpriv->dma); /* disable dma */
353 bytes = devpriv->hwdmasize;
354 if (cmd->stop_src == TRIG_COUNT) {
355 bytes = cmd->chanlist_len * cmd->stop_arg * sizeof(short);
356 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
357 devpriv->last_dma_run = bytes % devpriv->hwdmasize;
358 devpriv->dma_runs_to_end--;
359 if (devpriv->dma_runs_to_end >= 0)
360 bytes = devpriv->hwdmasize;
361 }
362
363 devpriv->next_dma_buf = 0;
364 set_dma_mode(devpriv->dma, DMA_MODE_READ);
365 flags = claim_dma_lock();
366 clear_dma_ff(devpriv->dma);
367 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
368 set_dma_count(devpriv->dma, bytes);
369 release_dma_lock(flags);
370 enable_dma(devpriv->dma);
371}
372
373static void pcl818_ai_setup_next_dma(struct comedi_device *dev,
374 struct comedi_subdevice *s)
375{
376 struct pcl818_private *devpriv = dev->private;
377 struct comedi_cmd *cmd = &s->async->cmd;
378 unsigned long flags;
379
380 disable_dma(devpriv->dma);
381 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
382 if (devpriv->dma_runs_to_end > -1 || cmd->stop_src == TRIG_NONE) {
383 /* switch dma bufs */
384 set_dma_mode(devpriv->dma, DMA_MODE_READ);
385 flags = claim_dma_lock();
386 set_dma_addr(devpriv->dma,
387 devpriv->hwdmaptr[devpriv->next_dma_buf]);
388 if (devpriv->dma_runs_to_end || cmd->stop_src == TRIG_NONE)
389 set_dma_count(devpriv->dma, devpriv->hwdmasize);
390 else
391 set_dma_count(devpriv->dma, devpriv->last_dma_run);
392 release_dma_lock(flags);
393 enable_dma(devpriv->dma);
394 }
395
396 devpriv->dma_runs_to_end--;
397}
398
933ccd82
HS
399static void pcl818_ai_set_chan_range(struct comedi_device *dev,
400 unsigned int chan,
401 unsigned int range)
402{
403 outb(chan, dev->iobase + PCL818_MUX_REG);
404 outb(range, dev->iobase + PCL818_RANGE_REG);
405}
406
407static void pcl818_ai_set_chan_scan(struct comedi_device *dev,
408 unsigned int first_chan,
409 unsigned int last_chan)
410{
411 outb(PCL818_MUX_SCAN(first_chan, last_chan),
412 dev->iobase + PCL818_MUX_REG);
413}
414
415static void pcl818_ai_setup_chanlist(struct comedi_device *dev,
416 unsigned int *chanlist,
417 unsigned int seglen)
418{
419 struct pcl818_private *devpriv = dev->private;
420 unsigned int first_chan = CR_CHAN(chanlist[0]);
421 unsigned int last_chan;
422 unsigned int range;
423 int i;
424
425 devpriv->act_chanlist_len = seglen;
426 devpriv->act_chanlist_pos = 0;
427
428 /* store range list to card */
429 for (i = 0; i < seglen; i++) {
430 last_chan = CR_CHAN(chanlist[i]);
431 range = CR_RANGE(chanlist[i]);
432
433 devpriv->act_chanlist[i] = last_chan;
434
435 pcl818_ai_set_chan_range(dev, last_chan, range);
436 }
437
438 udelay(1);
439
440 pcl818_ai_set_chan_scan(dev, first_chan, last_chan);
441}
442
9fd3effa
HS
443static void pcl818_ai_clear_eoc(struct comedi_device *dev)
444{
445 /* writing any value clears the interrupt request */
8d0b5e15 446 outb(0, dev->iobase + PCL818_STATUS_REG);
9fd3effa
HS
447}
448
9fdef9c8
HS
449static void pcl818_ai_soft_trig(struct comedi_device *dev)
450{
451 /* writing any value triggers a software conversion */
906a183f 452 outb(0, dev->iobase + PCL818_AI_LSB_REG);
9fdef9c8 453}
9fd3effa 454
4206e1be
HS
455static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device *dev,
456 struct comedi_subdevice *s,
457 unsigned int *chan)
458{
459 unsigned int val;
460
461 val = inb(dev->iobase + PCL818_FI_DATALO);
462 val |= (inb(dev->iobase + PCL818_FI_DATAHI) << 8);
463
464 if (chan)
465 *chan = val & 0xf;
466
467 return (val >> 4) & s->maxdata;
468}
469
8fc9f652
HS
470static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
471 struct comedi_subdevice *s,
472 unsigned int *chan)
473{
474 unsigned int val;
475
906a183f
HS
476 val = inb(dev->iobase + PCL818_AI_MSB_REG) << 8;
477 val |= inb(dev->iobase + PCL818_AI_LSB_REG);
8fc9f652
HS
478
479 if (chan)
480 *chan = val & 0xf;
481
482 return (val >> 4) & s->maxdata;
483}
484
1d6f4af9
HS
485static int pcl818_ai_eoc(struct comedi_device *dev,
486 struct comedi_subdevice *s,
487 struct comedi_insn *insn,
488 unsigned long context)
489{
490 unsigned int status;
491
8d0b5e15
HS
492 status = inb(dev->iobase + PCL818_STATUS_REG);
493 if (status & PCL818_STATUS_INT)
1d6f4af9
HS
494 return 0;
495 return -EBUSY;
496}
497
a8f461c2
HS
498static bool pcl818_ai_dropout(struct comedi_device *dev,
499 struct comedi_subdevice *s,
500 unsigned int chan)
501{
502 struct pcl818_private *devpriv = dev->private;
503 unsigned int expected_chan;
504
505 expected_chan = devpriv->act_chanlist[devpriv->act_chanlist_pos];
506 if (chan != expected_chan) {
507 dev_dbg(dev->class_dev,
508 "A/D mode1/3 %s - channel dropout %d!=%d !\n",
509 (devpriv->dma) ? "DMA" :
510 (devpriv->usefifo) ? "FIFO" : "IRQ",
511 chan, expected_chan);
512 s->cancel(dev, s);
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 */
540 s->cancel(dev, s);
541 s->async->events |= COMEDI_CB_EOA;
6d5b7858
HS
542 return false;
543 }
544
545 return true;
546}
547
e0946940
HS
548static void pcl818_handle_eoc(struct comedi_device *dev,
549 struct comedi_subdevice *s)
4da6a1d8 550{
8fc9f652 551 unsigned int chan;
a8f461c2 552 unsigned int val;
4da6a1d8 553
12700571 554 if (pcl818_ai_eoc(dev, s, NULL, 0)) {
12700571
HS
555 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
556 s->cancel(dev, s);
557 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
e0946940 558 return;
4da6a1d8 559 }
12700571 560
a8f461c2 561 val = pcl818_ai_get_sample(dev, s, &chan);
4da6a1d8 562
a8f461c2 563 if (pcl818_ai_dropout(dev, s, chan))
e0946940 564 return;
a8f461c2
HS
565
566 comedi_buf_put(s->async, val);
fc950139 567
e0946940 568 pcl818_ai_next_chan(dev, s);
4da6a1d8
MD
569}
570
e0946940
HS
571static void pcl818_handle_dma(struct comedi_device *dev,
572 struct comedi_subdevice *s)
4da6a1d8 573{
9a1a6cf8 574 struct pcl818_private *devpriv = dev->private;
4bf59ce2 575 unsigned short *ptr;
a8f461c2
HS
576 unsigned int chan;
577 unsigned int val;
578 int i, len, bufptr;
4da6a1d8 579
ab72ef30 580 pcl818_ai_setup_next_dma(dev, s);
4da6a1d8 581
4bf59ce2 582 ptr = (unsigned short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
4da6a1d8 583
f5cc425a 584 len = devpriv->hwdmasize >> 1;
4da6a1d8
MD
585 bufptr = 0;
586
587 for (i = 0; i < len; i++) {
a8f461c2
HS
588 val = ptr[bufptr++];
589 chan = val & 0xf;
590 val = (val >> 4) & s->maxdata;
591
592 if (pcl818_ai_dropout(dev, s, chan))
e0946940 593 break;
4da6a1d8 594
a8f461c2 595 comedi_buf_put(s->async, val);
4da6a1d8 596
6d5b7858 597 if (!pcl818_ai_next_chan(dev, s))
e0946940 598 break;
4da6a1d8 599 }
4da6a1d8
MD
600}
601
e0946940
HS
602static void pcl818_handle_fifo(struct comedi_device *dev,
603 struct comedi_subdevice *s)
4da6a1d8 604{
4206e1be
HS
605 unsigned int status;
606 unsigned int chan;
607 unsigned int val;
4bf59ce2 608 int i, len;
4da6a1d8 609
4206e1be 610 status = inb(dev->iobase + PCL818_FI_STATUS);
4da6a1d8 611
4206e1be 612 if (status & 4) {
4da6a1d8 613 comedi_error(dev, "A/D mode1/3 FIFO overflow!");
6c42119d 614 s->cancel(dev, s);
4da6a1d8 615 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
e0946940 616 return;
4da6a1d8
MD
617 }
618
4206e1be 619 if (status & 1) {
4da6a1d8 620 comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
6c42119d 621 s->cancel(dev, s);
4da6a1d8 622 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
e0946940 623 return;
4da6a1d8
MD
624 }
625
4206e1be 626 if (status & 2)
4da6a1d8 627 len = 512;
fc950139 628 else
4da6a1d8 629 len = 0;
4da6a1d8
MD
630
631 for (i = 0; i < len; i++) {
4206e1be 632 val = pcl818_ai_get_fifo_sample(dev, s, &chan);
a8f461c2
HS
633
634 if (pcl818_ai_dropout(dev, s, chan))
e0946940 635 break;
4da6a1d8 636
4206e1be 637 comedi_buf_put(s->async, val);
4da6a1d8 638
6d5b7858 639 if (!pcl818_ai_next_chan(dev, s))
e0946940 640 break;
4da6a1d8 641 }
4da6a1d8
MD
642}
643
e0946940 644static irqreturn_t pcl818_interrupt(int irq, void *d)
4da6a1d8 645{
71b5f4f1 646 struct comedi_device *dev = d;
9a1a6cf8 647 struct pcl818_private *devpriv = dev->private;
6c42119d 648 struct comedi_subdevice *s = dev->read_subdev;
4da6a1d8 649
799f89ce 650 if (!dev->attached || !devpriv->ai_cmd_running) {
9fd3effa 651 pcl818_ai_clear_eoc(dev);
4da6a1d8
MD
652 return IRQ_HANDLED;
653 }
4da6a1d8 654
905a8321
HS
655 if (devpriv->ai_cmd_canceled) {
656 /*
657 * The cleanup from ai_cancel() has been delayed
658 * until now because the card doesn't seem to like
659 * being reprogrammed while a DMA transfer is in
660 * progress.
661 */
662 devpriv->ai_act_scan = 0;
663 s->cancel(dev, s);
e21de1a8
IA
664 return IRQ_HANDLED;
665 }
666
799f89ce 667 if (devpriv->dma)
e0946940 668 pcl818_handle_dma(dev, s);
799f89ce 669 else if (devpriv->usefifo)
e0946940 670 pcl818_handle_fifo(dev, s);
799f89ce 671 else
e0946940
HS
672 pcl818_handle_eoc(dev, s);
673
9fd3effa 674 pcl818_ai_clear_eoc(dev);
e0946940
HS
675
676 comedi_event(dev, s);
677 return IRQ_HANDLED;
4da6a1d8
MD
678}
679
da91b269 680static int pcl818_ai_cmd_mode(int mode, struct comedi_device *dev,
0a85b6f0 681 struct comedi_subdevice *s)
4da6a1d8 682{
9a1a6cf8 683 struct pcl818_private *devpriv = dev->private;
ea6d0d4c 684 struct comedi_cmd *cmd = &s->async->cmd;
5af366ff 685 unsigned int ctrl = 0;
4da6a1d8
MD
686 unsigned int seglen;
687
c8bc43ec 688 if (devpriv->ai_cmd_running)
4da6a1d8
MD
689 return -EBUSY;
690
f4985a79 691 pcl818_start_pacer(dev, false);
4da6a1d8 692
1784f305 693 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
4da6a1d8
MD
694 if (seglen < 1)
695 return -EINVAL;
933ccd82 696 pcl818_ai_setup_chanlist(dev, cmd->chanlist, seglen);
4da6a1d8 697
5f74ea14 698 udelay(1);
4da6a1d8 699
c2e519dd 700 devpriv->ai_act_scan = cmd->stop_arg;
4da6a1d8 701 devpriv->ai_act_chan = 0;
c8bc43ec 702 devpriv->ai_cmd_running = 1;
905a8321 703 devpriv->ai_cmd_canceled = 0;
4da6a1d8
MD
704 devpriv->act_chanlist_pos = 0;
705 devpriv->dma_runs_to_end = 0;
706
4da6a1d8
MD
707 outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
708
b8d17c4a
HS
709 if (mode == 1)
710 ctrl |= PCL818_CTRL_PACER_TRIG;
711 else
712 ctrl |= PCL818_CTRL_EXT_TRIG;
713
714 if (devpriv->dma) {
715 pcl818_ai_setup_dma(dev, s);
716
717 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq) |
718 PCL818_CTRL_DMAE;
719 } else if (devpriv->usefifo) {
720 /* enable FIFO */
721 outb(1, dev->iobase + PCL818_FI_ENABLE);
722 } else {
723 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq);
4da6a1d8 724 }
b8d17c4a 725 outb(ctrl, dev->iobase + PCL818_CTRL_REG);
4da6a1d8 726
f4985a79 727 pcl818_start_pacer(dev, mode == 1);
4da6a1d8 728
4da6a1d8
MD
729 return 0;
730}
731
0a85b6f0
MT
732static int check_channel_list(struct comedi_device *dev,
733 struct comedi_subdevice *s,
734 unsigned int *chanlist, unsigned int n_chan)
4da6a1d8
MD
735{
736 unsigned int chansegment[16];
737 unsigned int i, nowmustbechan, seglen, segpos;
738
739 /* correct channel and range number check itself comedi/range.c */
740 if (n_chan < 1) {
741 comedi_error(dev, "range/channel list is empty!");
742 return 0;
743 }
744
745 if (n_chan > 1) {
25985edc 746 /* first channel is every time ok */
4da6a1d8 747 chansegment[0] = chanlist[0];
0109253d 748 /* build part of chanlist */
4da6a1d8 749 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
0109253d
BP
750 /* we detect loop, this must by finish */
751
4da6a1d8
MD
752 if (chanlist[0] == chanlist[i])
753 break;
754 nowmustbechan =
0a85b6f0 755 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
25985edc 756 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
84f03cf1
HS
757 dev_dbg(dev->class_dev,
758 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
759 i, CR_CHAN(chanlist[i]), nowmustbechan,
760 CR_CHAN(chanlist[0]));
4da6a1d8
MD
761 return 0;
762 }
0109253d 763 /* well, this is next correct channel in list */
4da6a1d8
MD
764 chansegment[i] = chanlist[i];
765 }
766
0109253d 767 /* check whole chanlist */
4da6a1d8 768 for (i = 0, segpos = 0; i < n_chan; i++) {
4da6a1d8 769 if (chanlist[i] != chansegment[i % seglen]) {
84f03cf1
HS
770 dev_dbg(dev->class_dev,
771 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
772 i, CR_CHAN(chansegment[i]),
773 CR_RANGE(chansegment[i]),
774 CR_AREF(chansegment[i]),
775 CR_CHAN(chanlist[i % seglen]),
776 CR_RANGE(chanlist[i % seglen]),
777 CR_AREF(chansegment[i % seglen]));
0109253d 778 return 0; /* chan/gain list is strange */
4da6a1d8
MD
779 }
780 }
781 } else {
782 seglen = 1;
783 }
4da6a1d8
MD
784 return seglen;
785}
786
4da6a1d8
MD
787static int check_single_ended(unsigned int port)
788{
8d0b5e15 789 if (inb(port + PCL818_STATUS_REG) & PCL818_STATUS_MUX)
4da6a1d8 790 return 1;
fc950139 791 return 0;
4da6a1d8
MD
792}
793
da91b269 794static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 795 struct comedi_cmd *cmd)
4da6a1d8 796{
dd8a4b47 797 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 798 struct pcl818_private *devpriv = dev->private;
4da6a1d8 799 int err = 0;
f4985a79 800 int tmp;
4da6a1d8 801
27020ffe 802 /* Step 1 : check if triggers are trivially valid */
4da6a1d8 803
27020ffe
HS
804 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
805 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
806 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
807 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
808 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
4da6a1d8 809
fc950139 810 if (err)
4da6a1d8 811 return 1;
4da6a1d8 812
27020ffe 813 /* Step 2a : make sure trigger sources are unique */
4da6a1d8 814
27020ffe
HS
815 err |= cfc_check_trigger_is_unique(cmd->convert_src);
816 err |= cfc_check_trigger_is_unique(cmd->stop_src);
4da6a1d8 817
27020ffe 818 /* Step 2b : and mutually compatible */
4da6a1d8 819
fc950139 820 if (err)
4da6a1d8 821 return 2;
4da6a1d8 822
8efdc1bf 823 /* Step 3: check if arguments are trivially valid */
4da6a1d8 824
8efdc1bf
HS
825 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
826 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
4da6a1d8 827
8efdc1bf
HS
828 if (cmd->convert_src == TRIG_TIMER)
829 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
830 board->ns_min);
831 else /* TRIG_EXT */
832 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
4da6a1d8 833
8efdc1bf 834 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
4da6a1d8 835
8efdc1bf
HS
836 if (cmd->stop_src == TRIG_COUNT)
837 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
838 else /* TRIG_NONE */
839 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
4da6a1d8 840
fc950139 841 if (err)
4da6a1d8 842 return 3;
4da6a1d8
MD
843
844 /* step 4: fix up any arguments */
845
846 if (cmd->convert_src == TRIG_TIMER) {
847 tmp = cmd->convert_arg;
cb9cfd7e 848 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
f4985a79
HS
849 &devpriv->divisor1,
850 &devpriv->divisor2,
cb9cfd7e 851 &cmd->convert_arg, cmd->flags);
dd8a4b47
HS
852 if (cmd->convert_arg < board->ns_min)
853 cmd->convert_arg = board->ns_min;
4da6a1d8
MD
854 if (tmp != cmd->convert_arg)
855 err++;
856 }
857
fc950139 858 if (err)
4da6a1d8 859 return 4;
4da6a1d8
MD
860
861 /* step 5: complain about special chanlist considerations */
862
863 if (cmd->chanlist) {
864 if (!check_channel_list(dev, s, cmd->chanlist,
0a85b6f0 865 cmd->chanlist_len))
0109253d 866 return 5; /* incorrect channels list */
4da6a1d8
MD
867 }
868
869 return 0;
870}
871
da91b269 872static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
4da6a1d8 873{
9a1a6cf8 874 struct pcl818_private *devpriv = dev->private;
ea6d0d4c 875 struct comedi_cmd *cmd = &s->async->cmd;
4da6a1d8
MD
876 int retval;
877
4da6a1d8 878 devpriv->ai_data_len = s->async->prealloc_bufsz;
4da6a1d8 879
0109253d
BP
880 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 3 */
881 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 */
4da6a1d8 882 retval = pcl818_ai_cmd_mode(1, dev, s);
4da6a1d8
MD
883 return retval;
884 }
0109253d 885 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
4da6a1d8
MD
886 return pcl818_ai_cmd_mode(3, dev, s);
887 }
888 }
889
890 return -1;
891}
892
0a85b6f0
MT
893static int pcl818_ai_cancel(struct comedi_device *dev,
894 struct comedi_subdevice *s)
4da6a1d8 895{
9a1a6cf8 896 struct pcl818_private *devpriv = dev->private;
00aba6e7 897 struct comedi_cmd *cmd = &s->async->cmd;
9a1a6cf8 898
2850b1c7
HS
899 if (!devpriv->ai_cmd_running)
900 return 0;
901
799f89ce 902 if (devpriv->dma) {
2850b1c7
HS
903 if (cmd->stop_src == TRIG_NONE ||
904 (cmd->stop_src == TRIG_COUNT && devpriv->ai_act_scan > 0)) {
905a8321
HS
905 if (!devpriv->ai_cmd_canceled) {
906 /*
907 * Wait for running dma transfer to end,
908 * do cleanup in interrupt.
909 */
910 devpriv->ai_cmd_canceled = 1;
911 return 0;
912 }
4da6a1d8 913 }
2850b1c7 914 disable_dma(devpriv->dma);
4da6a1d8
MD
915 }
916
5af366ff 917 outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
799f89ce 918 pcl818_start_pacer(dev, false);
9fd3effa
HS
919 pcl818_ai_clear_eoc(dev);
920
799f89ce
HS
921 if (devpriv->usefifo) { /* FIFO shutdown */
922 outb(0, dev->iobase + PCL818_FI_INTCLR);
923 outb(0, dev->iobase + PCL818_FI_FLUSH);
924 outb(0, dev->iobase + PCL818_FI_ENABLE);
925 }
926 devpriv->ai_cmd_running = 0;
927 devpriv->ai_cmd_canceled = 0;
928
4da6a1d8
MD
929 return 0;
930}
931
8916f5bc
HS
932static int pcl818_ai_insn_read(struct comedi_device *dev,
933 struct comedi_subdevice *s,
934 struct comedi_insn *insn,
935 unsigned int *data)
936{
937 unsigned int chan = CR_CHAN(insn->chanspec);
938 unsigned int range = CR_RANGE(insn->chanspec);
939 int ret = 0;
940 int i;
941
5af366ff 942 outb(PCL818_CTRL_SOFT_TRIG, dev->iobase + PCL818_CTRL_REG);
8916f5bc 943
933ccd82
HS
944 pcl818_ai_set_chan_range(dev, chan, range);
945 pcl818_ai_set_chan_scan(dev, chan, chan);
8916f5bc
HS
946
947 for (i = 0; i < insn->n; i++) {
9fd3effa 948 pcl818_ai_clear_eoc(dev);
9fdef9c8 949 pcl818_ai_soft_trig(dev);
8916f5bc
HS
950
951 ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
952 if (ret)
953 break;
954
955 data[i] = pcl818_ai_get_sample(dev, s, NULL);
956 }
9fd3effa 957 pcl818_ai_clear_eoc(dev);
8916f5bc
HS
958
959 return ret ? ret : insn->n;
960}
961
93505573
HS
962static int pcl818_ao_insn_write(struct comedi_device *dev,
963 struct comedi_subdevice *s,
964 struct comedi_insn *insn,
965 unsigned int *data)
966{
967 struct pcl818_private *devpriv = dev->private;
968 unsigned int chan = CR_CHAN(insn->chanspec);
969 int i;
970
971 for (i = 0; i < insn->n; i++) {
972 devpriv->ao_readback[chan] = data[i];
973 outb((data[i] & 0x000f) << 4,
974 dev->iobase + PCL818_AO_LSB_REG(chan));
975 outb((data[i] & 0x0ff0) >> 4,
976 dev->iobase + PCL818_AO_MSB_REG(chan));
977 }
978
979 return insn->n;
980}
981
982static int pcl818_ao_insn_read(struct comedi_device *dev,
983 struct comedi_subdevice *s,
984 struct comedi_insn *insn,
985 unsigned int *data)
986{
987 struct pcl818_private *devpriv = dev->private;
988 unsigned int chan = CR_CHAN(insn->chanspec);
989 int i;
990
991 for (i = 0; i < insn->n; i++)
992 data[i] = devpriv->ao_readback[chan];
993
994 return insn->n;
995}
996
4ab490b3
HS
997static int pcl818_di_insn_bits(struct comedi_device *dev,
998 struct comedi_subdevice *s,
999 struct comedi_insn *insn,
1000 unsigned int *data)
1001{
1002 data[1] = inb(dev->iobase + PCL818_DO_DI_LSB_REG) |
1003 (inb(dev->iobase + PCL818_DO_DI_MSB_REG) << 8);
1004
1005 return insn->n;
1006}
1007
1008static int pcl818_do_insn_bits(struct comedi_device *dev,
1009 struct comedi_subdevice *s,
1010 struct comedi_insn *insn,
1011 unsigned int *data)
1012{
1013 if (comedi_dio_update_state(s, data)) {
1014 outb(s->state & 0xff, dev->iobase + PCL818_DO_DI_LSB_REG);
1015 outb((s->state >> 8), dev->iobase + PCL818_DO_DI_MSB_REG);
1016 }
1017
1018 data[1] = s->state;
1019
1020 return insn->n;
1021}
1022
da91b269 1023static void pcl818_reset(struct comedi_device *dev)
4da6a1d8 1024{
dd8a4b47 1025 const struct pcl818_board *board = comedi_board(dev);
833b458a 1026 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
dd8a4b47 1027
80cc6486
HS
1028 /* flush and disable the FIFO */
1029 if (board->has_fifo) {
4da6a1d8
MD
1030 outb(0, dev->iobase + PCL818_FI_INTCLR);
1031 outb(0, dev->iobase + PCL818_FI_FLUSH);
1032 outb(0, dev->iobase + PCL818_FI_ENABLE);
1033 }
93505573
HS
1034 /* set analog output channel 0 to 0V */
1035 outb(0, dev->iobase + PCL818_AO_LSB_REG(0));
1036 outb(0, dev->iobase + PCL818_AO_MSB_REG(0));
5f74ea14 1037 udelay(1);
4ab490b3
HS
1038 outb(0, dev->iobase + PCL818_DO_DI_MSB_REG);
1039 outb(0, dev->iobase + PCL818_DO_DI_LSB_REG);
5f74ea14 1040 udelay(1);
5af366ff 1041 outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
4da6a1d8 1042 outb(0, dev->iobase + PCL818_CNTENABLE);
933ccd82 1043 outb(0, dev->iobase + PCL818_MUX_REG);
9fd3effa 1044 pcl818_ai_clear_eoc(dev);
833b458a
HS
1045
1046 /* Stop pacer */
1047 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
1048 i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
1049 i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
1050
dd8a4b47 1051 if (board->is_818) {
933ccd82 1052 outb(0, dev->iobase + PCL818_RANGE_REG);
4da6a1d8 1053 } else {
93505573
HS
1054 /* set analog output channel 1 to 0V */
1055 outb(0, dev->iobase + PCL818_AO_LSB_REG(1));
1056 outb(0, dev->iobase + PCL818_AO_MSB_REG(1));
4da6a1d8
MD
1057 }
1058}
1059
f39b8ccf
HS
1060static void pcl818_set_ai_range_table(struct comedi_device *dev,
1061 struct comedi_subdevice *s,
1062 struct comedi_devconfig *it)
1063{
1064 const struct pcl818_board *board = comedi_board(dev);
1065
1066 /* default to the range table from the boardinfo */
1067 s->range_table = board->ai_range_type;
1068
1069 /* now check the user config option based on the boardtype */
1070 if (board->is_818) {
1071 if (it->options[4] == 1 || it->options[4] == 10) {
1072 /* secondary range list jumper selectable */
1073 s->range_table = &range_pcl818l_h_ai;
1074 }
1075 } else {
1076 switch (it->options[4]) {
1077 case 0:
1078 s->range_table = &range_bipolar10;
1079 break;
1080 case 1:
1081 s->range_table = &range_bipolar5;
1082 break;
1083 case 2:
1084 s->range_table = &range_bipolar2_5;
1085 break;
1086 case 3:
1087 s->range_table = &range718_bipolar1;
1088 break;
1089 case 4:
1090 s->range_table = &range718_bipolar0_5;
1091 break;
1092 case 6:
1093 s->range_table = &range_unipolar10;
1094 break;
1095 case 7:
1096 s->range_table = &range_unipolar5;
1097 break;
1098 case 8:
1099 s->range_table = &range718_unipolar2;
1100 break;
1101 case 9:
1102 s->range_table = &range718_unipolar1;
1103 break;
1104 default:
1105 s->range_table = &range_unknown;
1106 break;
1107 }
1108 }
1109}
1110
da91b269 1111static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
4da6a1d8 1112{
dd8a4b47 1113 const struct pcl818_board *board = comedi_board(dev);
9a1a6cf8 1114 struct pcl818_private *devpriv;
34c43922 1115 struct comedi_subdevice *s;
f5cc425a
HS
1116 int ret;
1117 int i;
4da6a1d8 1118
0bdab509 1119 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
1120 if (!devpriv)
1121 return -ENOMEM;
4da6a1d8 1122
d6125588 1123 ret = comedi_request_region(dev, it->options[0],
80cc6486 1124 board->has_fifo ? 0x20 : 0x10);
d6c5ec04
HS
1125 if (ret)
1126 return ret;
4da6a1d8 1127
8356d4b4
HS
1128 /* we can use IRQ 2-7 for async command support */
1129 if (it->options[1] >= 2 && it->options[1] <= 7) {
e0946940 1130 ret = request_irq(it->options[1], pcl818_interrupt, 0,
e30b22a9 1131 dev->board_name, dev);
35a8735d 1132 if (ret == 0)
e30b22a9 1133 dev->irq = it->options[1];
4da6a1d8
MD
1134 }
1135
80cc6486
HS
1136 /* should we use the FIFO? */
1137 if (dev->irq && board->has_fifo && it->options[2] == -1)
1138 devpriv->usefifo = 1;
1139
4ba4a2d3
HS
1140 /* we need an IRQ to do DMA on channel 3 or 1 */
1141 if (dev->irq && board->has_dma &&
1142 (it->options[2] == 3 || it->options[2] == 1)) {
1143 ret = request_dma(it->options[2], dev->board_name);
1144 if (ret) {
d65e5b9d 1145 dev_err(dev->class_dev,
4ba4a2d3
HS
1146 "unable to request DMA channel %d\n",
1147 it->options[2]);
1148 return -EBUSY;
4da6a1d8 1149 }
4ba4a2d3
HS
1150 devpriv->dma = it->options[2];
1151
f5cc425a
HS
1152 devpriv->dmapages = 2; /* we need 16KB */
1153 devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
1154
1155 for (i = 0; i < 2; i++) {
1156 unsigned long dmabuf;
1157
1158 dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
1159 if (!dmabuf)
1160 return -ENOMEM;
1161
1162 devpriv->dmabuf[i] = dmabuf;
1163 devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
1164 }
4da6a1d8
MD
1165 }
1166
2f0b9d08 1167 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 1168 if (ret)
4da6a1d8
MD
1169 return ret;
1170
9fab6123 1171 s = &dev->subdevices[0];
9c06c4e3
HS
1172 s->type = COMEDI_SUBD_AI;
1173 s->subdev_flags = SDF_READABLE;
1174 if (check_single_ended(dev->iobase)) {
1175 s->n_chan = 16;
1176 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
4da6a1d8 1177 } else {
9c06c4e3
HS
1178 s->n_chan = 8;
1179 s->subdev_flags |= SDF_DIFF;
1180 }
bca1b594 1181 s->maxdata = 0x0fff;
9c06c4e3
HS
1182
1183 pcl818_set_ai_range_table(dev, s, it);
1184
1185 s->insn_read = pcl818_ai_insn_read;
1186 if (dev->irq) {
1187 dev->read_subdev = s;
1188 s->subdev_flags |= SDF_CMD_READ;
1189 s->len_chanlist = s->n_chan;
1190 s->do_cmdtest = ai_cmdtest;
1191 s->do_cmd = ai_cmd;
1192 s->cancel = pcl818_ai_cancel;
4da6a1d8
MD
1193 }
1194
93505573 1195 /* Analog Output subdevice */
9fab6123 1196 s = &dev->subdevices[1];
93505573
HS
1197 if (board->n_aochan) {
1198 s->type = COMEDI_SUBD_AO;
1199 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1200 s->n_chan = board->n_aochan;
1201 s->maxdata = 0x0fff;
1202 s->range_table = &range_unipolar5;
1203 s->insn_read = pcl818_ao_insn_read;
1204 s->insn_write = pcl818_ao_insn_write;
dd8a4b47 1205 if (board->is_818) {
4da6a1d8
MD
1206 if ((it->options[4] == 1) || (it->options[4] == 10))
1207 s->range_table = &range_unipolar10;
1208 if (it->options[4] == 2)
1209 s->range_table = &range_unknown;
1210 } else {
1211 if ((it->options[5] == 1) || (it->options[5] == 10))
1212 s->range_table = &range_unipolar10;
1213 if (it->options[5] == 2)
1214 s->range_table = &range_unknown;
1215 }
93505573
HS
1216 } else {
1217 s->type = COMEDI_SUBD_UNUSED;
4da6a1d8
MD
1218 }
1219
03d98e6c 1220 /* Digital Input subdevice */
9fab6123 1221 s = &dev->subdevices[2];
03d98e6c
HS
1222 s->type = COMEDI_SUBD_DI;
1223 s->subdev_flags = SDF_READABLE;
1224 s->n_chan = 16;
1225 s->maxdata = 1;
1226 s->range_table = &range_digital;
1227 s->insn_bits = pcl818_di_insn_bits;
1228
1229 /* Digital Output subdevice */
9fab6123 1230 s = &dev->subdevices[3];
03d98e6c
HS
1231 s->type = COMEDI_SUBD_DO;
1232 s->subdev_flags = SDF_WRITABLE;
1233 s->n_chan = 16;
1234 s->maxdata = 1;
1235 s->range_table = &range_digital;
1236 s->insn_bits = pcl818_do_insn_bits;
4da6a1d8
MD
1237
1238 /* select 1/10MHz oscilator */
fc950139 1239 if ((it->options[3] == 0) || (it->options[3] == 10))
cb9cfd7e 1240 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
fc950139 1241 else
cb9cfd7e 1242 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
4da6a1d8
MD
1243
1244 /* max sampling speed */
dd8a4b47 1245 devpriv->ns_min = board->ns_min;
4da6a1d8 1246
dd8a4b47 1247 if (!board->is_818) {
4da6a1d8
MD
1248 if ((it->options[6] == 1) || (it->options[6] == 100))
1249 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1250 }
1251
1252 pcl818_reset(dev);
1253
4da6a1d8
MD
1254 return 0;
1255}
1256
484ecc95 1257static void pcl818_detach(struct comedi_device *dev)
4da6a1d8 1258{
9a1a6cf8
HS
1259 struct pcl818_private *devpriv = dev->private;
1260
1261 if (devpriv) {
89dac49e 1262 pcl818_ai_cancel(dev, dev->read_subdev);
484ecc95
HS
1263 pcl818_reset(dev);
1264 if (devpriv->dma)
1265 free_dma(devpriv->dma);
1266 if (devpriv->dmabuf[0])
f5cc425a 1267 free_pages(devpriv->dmabuf[0], devpriv->dmapages);
484ecc95 1268 if (devpriv->dmabuf[1])
f5cc425a 1269 free_pages(devpriv->dmabuf[1], devpriv->dmapages);
484ecc95 1270 }
a32c6d00 1271 comedi_legacy_detach(dev);
4da6a1d8 1272}
90f703d3 1273
294f930d 1274static struct comedi_driver pcl818_driver = {
f6aafa10
HS
1275 .driver_name = "pcl818",
1276 .module = THIS_MODULE,
1277 .attach = pcl818_attach,
1278 .detach = pcl818_detach,
1279 .board_name = &boardtypes[0].name,
1280 .num_names = ARRAY_SIZE(boardtypes),
1281 .offset = sizeof(struct pcl818_board),
1282};
294f930d 1283module_comedi_driver(pcl818_driver);
f6aafa10 1284
90f703d3
AT
1285MODULE_AUTHOR("Comedi http://www.comedi.org");
1286MODULE_DESCRIPTION("Comedi low-level driver");
1287MODULE_LICENSE("GPL");
This page took 1.204505 seconds and 5 git commands to generate.