staging: comedi: pcl816: only init command support if irq is available
[deliverable/linux.git] / drivers / staging / comedi / drivers / pcl816.c
1 /*
2 comedi/drivers/pcl816.c
3
4 Author: Juan Grigera <juan@grigera.com.ar>
5 based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
6
7 hardware driver for Advantech cards:
8 card: PCL-816, PCL814B
9 driver: pcl816
10 */
11 /*
12 Driver: pcl816
13 Description: Advantech PCL-816 cards, PCL-814
14 Author: Juan Grigera <juan@grigera.com.ar>
15 Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
16 Status: works
17 Updated: Tue, 2 Apr 2002 23:15:21 -0800
18
19 PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20 Differences are at resolution (16 vs 12 bits).
21
22 The driver support AI command mode, other subdevices not written.
23
24 Analog output and digital input and output are not supported.
25
26 Configuration Options:
27 [0] - IO Base
28 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
29 [2] - DMA (0=disable, 1, 3)
30 [3] - 0, 10=10MHz clock for 8254
31 1= 1MHz clock for 8254
32
33 */
34
35 #include <linux/module.h>
36 #include "../comedidev.h"
37
38 #include <linux/gfp.h>
39 #include <linux/delay.h>
40 #include <linux/io.h>
41 #include <linux/interrupt.h>
42 #include <asm/dma.h>
43
44 #include "comedi_fc.h"
45 #include "8253.h"
46
47 /* boards constants */
48 /* IO space len */
49 #define PCLx1x_RANGE 16
50
51 /* INTEL 8254 counters */
52 #define PCL816_CTR0 4
53 #define PCL816_CTR1 5
54 #define PCL816_CTR2 6
55 /* R: counter read-back register W: counter control */
56 #define PCL816_CTRCTL 7
57
58 /* R: A/D high byte W: A/D range control */
59 #define PCL816_RANGE 9
60 /* W: clear INT request */
61 #define PCL816_CLRINT 10
62 /* R: next mux scan channel W: mux scan channel & range control pointer */
63 #define PCL816_MUX 11
64 /* R/W: operation control register */
65 #define PCL816_CONTROL 12
66
67 /* R: return status byte W: set DMA/IRQ */
68 #define PCL816_STATUS 13
69 #define PCL816_STATUS_DRDY_MASK 0x80
70
71 /* R: low byte of A/D W: soft A/D trigger */
72 #define PCL816_AD_LO 8
73 /* R: high byte of A/D W: A/D range control */
74 #define PCL816_AD_HI 9
75
76 /* type of interrupt handler */
77 #define INT_TYPE_AI1_INT 1
78 #define INT_TYPE_AI1_DMA 2
79 #define INT_TYPE_AI3_INT 4
80 #define INT_TYPE_AI3_DMA 5
81
82 #define MAGIC_DMA_WORD 0x5a5a
83
84 static const struct comedi_lrange range_pcl816 = { 8, {
85 BIP_RANGE(10),
86 BIP_RANGE(5),
87 BIP_RANGE(2.5),
88 BIP_RANGE(1.25),
89 UNI_RANGE(10),
90 UNI_RANGE(5),
91 UNI_RANGE(2.5),
92 UNI_RANGE(1.25),
93 }
94 };
95
96 struct pcl816_board {
97
98 const char *name; /* board name */
99 int n_ranges; /* len of range list */
100 int n_aichan; /* num of A/D chans in diferencial mode */
101 unsigned int ai_ns_min; /* minimal allowed delay between samples (in ns) */
102 int n_aochan; /* num of D/A chans */
103 int n_dichan; /* num of DI chans */
104 int n_dochan; /* num of DO chans */
105 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
106 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
107 unsigned int io_range; /* len of IO space */
108 unsigned int IRQbits; /* allowed interrupts */
109 unsigned int DMAbits; /* allowed DMA chans */
110 int ai_maxdata; /* maxdata for A/D */
111 int ao_maxdata; /* maxdata for D/A */
112 int ai_chanlist; /* allowed len of channel list A/D */
113 int ao_chanlist; /* allowed len of channel list D/A */
114 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
115 };
116
117 struct pcl816_private {
118
119 unsigned int dma; /* used DMA, 0=don't use DMA */
120 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
121 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
122 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
123 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
124 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
125 int next_dma_buf; /* which DMA buffer will be used next round */
126 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
127 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
128
129 unsigned int ai_scans; /* len of scanlist */
130 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
131 int irq_blocked; /* 1=IRQ now uses any subdev */
132 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
133 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
134 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
135 int ai_act_scan; /* how many scans we finished */
136 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
137 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
138 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
139 unsigned int ai_n_chan; /* how many channels per scan */
140 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
141 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
142 };
143
144 /*
145 ==============================================================================
146 */
147 static int check_channel_list(struct comedi_device *dev,
148 struct comedi_subdevice *s,
149 unsigned int *chanlist, unsigned int chanlen);
150 static void setup_channel_list(struct comedi_device *dev,
151 struct comedi_subdevice *s,
152 unsigned int *chanlist, unsigned int seglen);
153 static int pcl816_ai_cancel(struct comedi_device *dev,
154 struct comedi_subdevice *s);
155 static void start_pacer(struct comedi_device *dev, int mode,
156 unsigned int divisor1, unsigned int divisor2);
157
158 static int pcl816_ai_cmdtest(struct comedi_device *dev,
159 struct comedi_subdevice *s,
160 struct comedi_cmd *cmd);
161 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
162
163 /*
164 ==============================================================================
165 ANALOG INPUT MODE0, 816 cards, slow version
166 */
167 static int pcl816_ai_insn_read(struct comedi_device *dev,
168 struct comedi_subdevice *s,
169 struct comedi_insn *insn, unsigned int *data)
170 {
171 int n;
172 int timeout;
173
174 /* software trigger, DMA and INT off */
175 outb(0, dev->iobase + PCL816_CONTROL);
176 /* clear INT (conversion end) flag */
177 outb(0, dev->iobase + PCL816_CLRINT);
178
179 /* Set the input channel */
180 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
181 /* select gain */
182 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);
183
184 for (n = 0; n < insn->n; n++) {
185
186 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
187
188 timeout = 100;
189 while (timeout--) {
190 if (!(inb(dev->iobase + PCL816_STATUS) &
191 PCL816_STATUS_DRDY_MASK)) {
192 /* return read value */
193 data[n] =
194 ((inb(dev->iobase +
195 PCL816_AD_HI) << 8) |
196 (inb(dev->iobase + PCL816_AD_LO)));
197 /* clear INT (conversion end) flag */
198 outb(0, dev->iobase + PCL816_CLRINT);
199 break;
200 }
201 udelay(1);
202 }
203 /* Return timeout error */
204 if (!timeout) {
205 comedi_error(dev, "A/D insn timeout\n");
206 data[0] = 0;
207 /* clear INT (conversion end) flag */
208 outb(0, dev->iobase + PCL816_CLRINT);
209 return -EIO;
210 }
211
212 }
213 return n;
214 }
215
216 /*
217 ==============================================================================
218 analog input interrupt mode 1 & 3, 818 cards
219 one sample per interrupt version
220 */
221 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
222 {
223 struct comedi_device *dev = d;
224 struct pcl816_private *devpriv = dev->private;
225 struct comedi_subdevice *s = &dev->subdevices[0];
226 unsigned char low, hi;
227 int timeout = 50; /* wait max 50us */
228
229 while (timeout--) {
230 if (!(inb(dev->iobase + PCL816_STATUS) &
231 PCL816_STATUS_DRDY_MASK))
232 break;
233 udelay(1);
234 }
235 if (!timeout) { /* timeout, bail error */
236 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
237 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
238 pcl816_ai_cancel(dev, s);
239 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
240 comedi_event(dev, s);
241 return IRQ_HANDLED;
242
243 }
244
245 /* get the sample */
246 low = inb(dev->iobase + PCL816_AD_LO);
247 hi = inb(dev->iobase + PCL816_AD_HI);
248
249 comedi_buf_put(s->async, (hi << 8) | low);
250
251 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
252
253 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
254 devpriv->ai_act_chanlist_pos = 0;
255
256 s->async->cur_chan++;
257 if (s->async->cur_chan >= devpriv->ai_n_chan) {
258 s->async->cur_chan = 0;
259 devpriv->ai_act_scan++;
260 }
261
262 if (!devpriv->ai_neverending)
263 /* all data sampled */
264 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
265 /* all data sampled */
266 pcl816_ai_cancel(dev, s);
267 s->async->events |= COMEDI_CB_EOA;
268 }
269 comedi_event(dev, s);
270 return IRQ_HANDLED;
271 }
272
273 /*
274 ==============================================================================
275 analog input dma mode 1 & 3, 816 cards
276 */
277 static void transfer_from_dma_buf(struct comedi_device *dev,
278 struct comedi_subdevice *s,
279 unsigned short *ptr,
280 unsigned int bufptr, unsigned int len)
281 {
282 struct pcl816_private *devpriv = dev->private;
283 int i;
284
285 s->async->events = 0;
286
287 for (i = 0; i < len; i++) {
288
289 comedi_buf_put(s->async, ptr[bufptr++]);
290
291 if (++devpriv->ai_act_chanlist_pos >=
292 devpriv->ai_act_chanlist_len) {
293 devpriv->ai_act_chanlist_pos = 0;
294 }
295
296 s->async->cur_chan++;
297 if (s->async->cur_chan >= devpriv->ai_n_chan) {
298 s->async->cur_chan = 0;
299 devpriv->ai_act_scan++;
300 }
301
302 if (!devpriv->ai_neverending)
303 /* all data sampled */
304 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
305 pcl816_ai_cancel(dev, s);
306 s->async->events |= COMEDI_CB_EOA;
307 s->async->events |= COMEDI_CB_BLOCK;
308 break;
309 }
310 }
311
312 comedi_event(dev, s);
313 }
314
315 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
316 {
317 struct comedi_device *dev = d;
318 struct pcl816_private *devpriv = dev->private;
319 struct comedi_subdevice *s = &dev->subdevices[0];
320 int len, bufptr, this_dma_buf;
321 unsigned long dma_flags;
322 unsigned short *ptr;
323
324 disable_dma(devpriv->dma);
325 this_dma_buf = devpriv->next_dma_buf;
326
327 /* switch dma bufs */
328 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {
329
330 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
331 set_dma_mode(devpriv->dma, DMA_MODE_READ);
332 dma_flags = claim_dma_lock();
333 /* clear_dma_ff (devpriv->dma); */
334 set_dma_addr(devpriv->dma,
335 devpriv->hwdmaptr[devpriv->next_dma_buf]);
336 if (devpriv->dma_runs_to_end) {
337 set_dma_count(devpriv->dma,
338 devpriv->hwdmasize[devpriv->
339 next_dma_buf]);
340 } else {
341 set_dma_count(devpriv->dma, devpriv->last_dma_run);
342 }
343 release_dma_lock(dma_flags);
344 enable_dma(devpriv->dma);
345 }
346
347 devpriv->dma_runs_to_end--;
348 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
349
350 ptr = (unsigned short *)devpriv->dmabuf[this_dma_buf];
351
352 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
353 bufptr = devpriv->ai_poll_ptr;
354 devpriv->ai_poll_ptr = 0;
355
356 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
357 return IRQ_HANDLED;
358 }
359
360 /*
361 ==============================================================================
362 INT procedure
363 */
364 static irqreturn_t interrupt_pcl816(int irq, void *d)
365 {
366 struct comedi_device *dev = d;
367 struct pcl816_private *devpriv = dev->private;
368
369 if (!dev->attached) {
370 comedi_error(dev, "premature interrupt");
371 return IRQ_HANDLED;
372 }
373
374 switch (devpriv->int816_mode) {
375 case INT_TYPE_AI1_DMA:
376 case INT_TYPE_AI3_DMA:
377 return interrupt_pcl816_ai_mode13_dma(irq, d);
378 case INT_TYPE_AI1_INT:
379 case INT_TYPE_AI3_INT:
380 return interrupt_pcl816_ai_mode13_int(irq, d);
381 }
382
383 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
384 if (!dev->irq || !devpriv->irq_blocked || !devpriv->int816_mode) {
385 if (devpriv->irq_was_now_closed) {
386 devpriv->irq_was_now_closed = 0;
387 /* comedi_error(dev,"last IRQ.."); */
388 return IRQ_HANDLED;
389 }
390 comedi_error(dev, "bad IRQ!");
391 return IRQ_NONE;
392 }
393 comedi_error(dev, "IRQ from unknown source!");
394 return IRQ_NONE;
395 }
396
397 /*
398 ==============================================================================
399 */
400 static int pcl816_ai_cmdtest(struct comedi_device *dev,
401 struct comedi_subdevice *s, struct comedi_cmd *cmd)
402 {
403 const struct pcl816_board *board = comedi_board(dev);
404 int err = 0;
405 int tmp, divisor1 = 0, divisor2 = 0;
406
407 /* Step 1 : check if triggers are trivially valid */
408
409 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
410 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
411 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
412 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
413 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
414
415 if (err)
416 return 1;
417
418 /* Step 2a : make sure trigger sources are unique */
419
420 err |= cfc_check_trigger_is_unique(cmd->convert_src);
421 err |= cfc_check_trigger_is_unique(cmd->stop_src);
422
423 /* Step 2b : and mutually compatible */
424
425 if (err)
426 return 2;
427
428
429 /* Step 3: check if arguments are trivially valid */
430
431 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
432 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
433
434 if (cmd->convert_src == TRIG_TIMER)
435 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
436 board->ai_ns_min);
437 else /* TRIG_EXT */
438 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
439
440 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
441
442 if (cmd->stop_src == TRIG_COUNT)
443 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
444 else /* TRIG_NONE */
445 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
446
447 if (err)
448 return 3;
449
450
451 /* step 4: fix up any arguments */
452 if (cmd->convert_src == TRIG_TIMER) {
453 tmp = cmd->convert_arg;
454 i8253_cascade_ns_to_timer(board->i8254_osc_base,
455 &divisor1, &divisor2,
456 &cmd->convert_arg, cmd->flags);
457 if (cmd->convert_arg < board->ai_ns_min)
458 cmd->convert_arg = board->ai_ns_min;
459 if (tmp != cmd->convert_arg)
460 err++;
461 }
462
463 if (err)
464 return 4;
465
466
467 /* step 5: complain about special chanlist considerations */
468
469 if (cmd->chanlist) {
470 if (!check_channel_list(dev, s, cmd->chanlist,
471 cmd->chanlist_len))
472 return 5; /* incorrect channels list */
473 }
474
475 return 0;
476 }
477
478 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
479 {
480 const struct pcl816_board *board = comedi_board(dev);
481 struct pcl816_private *devpriv = dev->private;
482 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
483 struct comedi_cmd *cmd = &s->async->cmd;
484 unsigned int seglen;
485
486 if (cmd->start_src != TRIG_NOW)
487 return -EINVAL;
488 if (cmd->scan_begin_src != TRIG_FOLLOW)
489 return -EINVAL;
490 if (cmd->scan_end_src != TRIG_COUNT)
491 return -EINVAL;
492 if (cmd->scan_end_arg != cmd->chanlist_len)
493 return -EINVAL;
494 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
495 if (devpriv->irq_blocked)
496 return -EBUSY;
497
498 if (cmd->convert_src == TRIG_TIMER) {
499 if (cmd->convert_arg < board->ai_ns_min)
500 cmd->convert_arg = board->ai_ns_min;
501
502 i8253_cascade_ns_to_timer(board->i8254_osc_base,
503 &divisor1, &divisor2,
504 &cmd->convert_arg, cmd->flags);
505
506 /* PCL816 crash if any divisor is set to 1 */
507 if (divisor1 == 1) {
508 divisor1 = 2;
509 divisor2 /= 2;
510 }
511 if (divisor2 == 1) {
512 divisor2 = 2;
513 divisor1 /= 2;
514 }
515 }
516
517 start_pacer(dev, -1, 0, 0); /* stop pacer */
518
519 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
520 if (seglen < 1)
521 return -EINVAL;
522 setup_channel_list(dev, s, cmd->chanlist, seglen);
523 udelay(1);
524
525 devpriv->ai_n_chan = cmd->chanlist_len;
526 devpriv->ai_act_scan = 0;
527 s->async->cur_chan = 0;
528 devpriv->irq_blocked = 1;
529 devpriv->ai_poll_ptr = 0;
530 devpriv->irq_was_now_closed = 0;
531
532 if (cmd->stop_src == TRIG_COUNT) {
533 devpriv->ai_scans = cmd->stop_arg;
534 devpriv->ai_neverending = 0;
535 } else {
536 devpriv->ai_scans = 0;
537 devpriv->ai_neverending = 1;
538 }
539
540 if (devpriv->dma) {
541 bytes = devpriv->hwdmasize[0];
542 if (!devpriv->ai_neverending) {
543 /* how many */
544 bytes = s->async->cmd.chanlist_len *
545 s->async->cmd.chanlist_len *
546 sizeof(short);
547
548 /* how many DMA pages we must fill */
549 devpriv->dma_runs_to_end = bytes /
550 devpriv->hwdmasize[0];
551
552 /* on last dma transfer must be moved */
553 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];
554 devpriv->dma_runs_to_end--;
555 if (devpriv->dma_runs_to_end >= 0)
556 bytes = devpriv->hwdmasize[0];
557 } else
558 devpriv->dma_runs_to_end = -1;
559
560 devpriv->next_dma_buf = 0;
561 set_dma_mode(devpriv->dma, DMA_MODE_READ);
562 dma_flags = claim_dma_lock();
563 clear_dma_ff(devpriv->dma);
564 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
565 set_dma_count(devpriv->dma, bytes);
566 release_dma_lock(dma_flags);
567 enable_dma(devpriv->dma);
568 }
569
570 start_pacer(dev, 1, divisor1, divisor2);
571 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
572
573 switch (cmd->convert_src) {
574 case TRIG_TIMER:
575 devpriv->int816_mode = INT_TYPE_AI1_DMA;
576
577 /* Pacer+IRQ+DMA */
578 outb(0x32, dev->iobase + PCL816_CONTROL);
579
580 /* write irq and DMA to card */
581 outb(dmairq, dev->iobase + PCL816_STATUS);
582 break;
583
584 default:
585 devpriv->int816_mode = INT_TYPE_AI3_DMA;
586
587 /* Ext trig+IRQ+DMA */
588 outb(0x34, dev->iobase + PCL816_CONTROL);
589
590 /* write irq to card */
591 outb(dmairq, dev->iobase + PCL816_STATUS);
592 break;
593 }
594
595 return 0;
596 }
597
598 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
599 {
600 struct pcl816_private *devpriv = dev->private;
601 unsigned long flags;
602 unsigned int top1, top2, i;
603
604 if (!devpriv->dma)
605 return 0; /* poll is valid only for DMA transfer */
606
607 spin_lock_irqsave(&dev->spinlock, flags);
608
609 for (i = 0; i < 20; i++) {
610 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
611 top2 = get_dma_residue(devpriv->dma);
612 if (top1 == top2)
613 break;
614 }
615 if (top1 != top2) {
616 spin_unlock_irqrestore(&dev->spinlock, flags);
617 return 0;
618 }
619
620 /* where is now DMA in buffer */
621 top1 = devpriv->hwdmasize[0] - top1;
622 top1 >>= 1; /* sample position */
623 top2 = top1 - devpriv->ai_poll_ptr;
624 if (top2 < 1) { /* no new samples */
625 spin_unlock_irqrestore(&dev->spinlock, flags);
626 return 0;
627 }
628
629 transfer_from_dma_buf(dev, s,
630 (unsigned short *)devpriv->dmabuf[devpriv->
631 next_dma_buf],
632 devpriv->ai_poll_ptr, top2);
633
634 devpriv->ai_poll_ptr = top1; /* new buffer position */
635 spin_unlock_irqrestore(&dev->spinlock, flags);
636
637 return s->async->buf_write_count - s->async->buf_read_count;
638 }
639
640 /*
641 ==============================================================================
642 cancel any mode 1-4 AI
643 */
644 static int pcl816_ai_cancel(struct comedi_device *dev,
645 struct comedi_subdevice *s)
646 {
647 struct pcl816_private *devpriv = dev->private;
648
649 if (devpriv->irq_blocked > 0) {
650 switch (devpriv->int816_mode) {
651 case INT_TYPE_AI1_DMA:
652 case INT_TYPE_AI3_DMA:
653 disable_dma(devpriv->dma);
654 case INT_TYPE_AI1_INT:
655 case INT_TYPE_AI3_INT:
656 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73,
657 dev->iobase + PCL816_CONTROL); /* Stop A/D */
658 udelay(1);
659 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
660
661 /* Stop pacer */
662 outb(0xb0, dev->iobase + PCL816_CTRCTL);
663 outb(0x70, dev->iobase + PCL816_CTRCTL);
664 outb(0, dev->iobase + PCL816_AD_LO);
665 inb(dev->iobase + PCL816_AD_LO);
666 inb(dev->iobase + PCL816_AD_HI);
667
668 /* clear INT request */
669 outb(0, dev->iobase + PCL816_CLRINT);
670
671 /* Stop A/D */
672 outb(0, dev->iobase + PCL816_CONTROL);
673 devpriv->irq_blocked = 0;
674 devpriv->irq_was_now_closed = devpriv->int816_mode;
675 devpriv->int816_mode = 0;
676 devpriv->last_int_sub = s;
677 /* s->busy = 0; */
678 break;
679 }
680 }
681 return 0;
682 }
683
684 /*
685 ==============================================================================
686 chech for PCL816
687 */
688 static int pcl816_check(unsigned long iobase)
689 {
690 outb(0x00, iobase + PCL816_MUX);
691 udelay(1);
692 if (inb(iobase + PCL816_MUX) != 0x00)
693 return 1; /* there isn't card */
694 outb(0x55, iobase + PCL816_MUX);
695 udelay(1);
696 if (inb(iobase + PCL816_MUX) != 0x55)
697 return 1; /* there isn't card */
698 outb(0x00, iobase + PCL816_MUX);
699 udelay(1);
700 outb(0x18, iobase + PCL816_CONTROL);
701 udelay(1);
702 if (inb(iobase + PCL816_CONTROL) != 0x18)
703 return 1; /* there isn't card */
704 return 0; /* ok, card exist */
705 }
706
707 /*
708 ==============================================================================
709 reset whole PCL-816 cards
710 */
711 static void pcl816_reset(struct comedi_device *dev)
712 {
713 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
714 /* outb (0, dev->iobase + PCL818_DA_HI); */
715 /* udelay (1); */
716 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
717 /* outb (0, dev->iobase + PCL818_DO_LO); */
718 /* udelay (1); */
719 outb(0, dev->iobase + PCL816_CONTROL);
720 outb(0, dev->iobase + PCL816_MUX);
721 outb(0, dev->iobase + PCL816_CLRINT);
722 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
723 outb(0x70, dev->iobase + PCL816_CTRCTL);
724 outb(0x30, dev->iobase + PCL816_CTRCTL);
725 outb(0, dev->iobase + PCL816_RANGE);
726 }
727
728 /*
729 ==============================================================================
730 Start/stop pacer onboard pacer
731 */
732 static void
733 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
734 unsigned int divisor2)
735 {
736 outb(0x32, dev->iobase + PCL816_CTRCTL);
737 outb(0xff, dev->iobase + PCL816_CTR0);
738 outb(0x00, dev->iobase + PCL816_CTR0);
739 udelay(1);
740
741 /* set counter 2 as mode 3 */
742 outb(0xb4, dev->iobase + PCL816_CTRCTL);
743 /* set counter 1 as mode 3 */
744 outb(0x74, dev->iobase + PCL816_CTRCTL);
745 udelay(1);
746
747 if (mode == 1) {
748 dev_dbg(dev->class_dev, "mode %d, divisor1 %d, divisor2 %d\n",
749 mode, divisor1, divisor2);
750 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
751 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
752 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
753 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
754 }
755
756 /* clear pending interrupts (just in case) */
757 /* outb(0, dev->iobase + PCL816_CLRINT); */
758 }
759
760 /*
761 ==============================================================================
762 Check if channel list from user is built correctly
763 If it's ok, then return non-zero length of repeated segment of channel list
764 */
765 static int
766 check_channel_list(struct comedi_device *dev,
767 struct comedi_subdevice *s, unsigned int *chanlist,
768 unsigned int chanlen)
769 {
770 unsigned int chansegment[16];
771 unsigned int i, nowmustbechan, seglen, segpos;
772
773 /* correct channel and range number check itself comedi/range.c */
774 if (chanlen < 1) {
775 comedi_error(dev, "range/channel list is empty!");
776 return 0;
777 }
778
779 if (chanlen > 1) {
780 /* first channel is every time ok */
781 chansegment[0] = chanlist[0];
782 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
783 /* we detect loop, this must by finish */
784 if (chanlist[0] == chanlist[i])
785 break;
786 nowmustbechan =
787 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
788 if (nowmustbechan != CR_CHAN(chanlist[i])) {
789 /* channel list isn't continuous :-( */
790 dev_dbg(dev->class_dev,
791 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
792 i, CR_CHAN(chanlist[i]), nowmustbechan,
793 CR_CHAN(chanlist[0]));
794 return 0;
795 }
796 /* well, this is next correct channel in list */
797 chansegment[i] = chanlist[i];
798 }
799
800 /* check whole chanlist */
801 for (i = 0, segpos = 0; i < chanlen; i++) {
802 if (chanlist[i] != chansegment[i % seglen]) {
803 dev_dbg(dev->class_dev,
804 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
805 i, CR_CHAN(chansegment[i]),
806 CR_RANGE(chansegment[i]),
807 CR_AREF(chansegment[i]),
808 CR_CHAN(chanlist[i % seglen]),
809 CR_RANGE(chanlist[i % seglen]),
810 CR_AREF(chansegment[i % seglen]));
811 return 0; /* chan/gain list is strange */
812 }
813 }
814 } else {
815 seglen = 1;
816 }
817
818 return seglen; /* we can serve this with MUX logic */
819 }
820
821 /*
822 ==============================================================================
823 Program scan/gain logic with channel list.
824 */
825 static void
826 setup_channel_list(struct comedi_device *dev,
827 struct comedi_subdevice *s, unsigned int *chanlist,
828 unsigned int seglen)
829 {
830 struct pcl816_private *devpriv = dev->private;
831 unsigned int i;
832
833 devpriv->ai_act_chanlist_len = seglen;
834 devpriv->ai_act_chanlist_pos = 0;
835
836 for (i = 0; i < seglen; i++) { /* store range list to card */
837 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
838 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
839 /* select gain */
840 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);
841 }
842
843 udelay(1);
844 /* select channel interval to scan */
845 outb(devpriv->ai_act_chanlist[0] |
846 (devpriv->ai_act_chanlist[seglen - 1] << 4),
847 dev->iobase + PCL816_MUX);
848 }
849
850 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
851 {
852 const struct pcl816_board *board = comedi_board(dev);
853 struct pcl816_private *devpriv;
854 int ret;
855 unsigned int dma;
856 unsigned long pages;
857 /* int i; */
858 struct comedi_subdevice *s;
859
860 ret = comedi_request_region(dev, it->options[0], board->io_range);
861 if (ret)
862 return ret;
863
864 if (pcl816_check(dev->iobase)) {
865 dev_err(dev->class_dev, "I can't detect board. FAIL!\n");
866 return -EIO;
867 }
868
869 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
870 if (!devpriv)
871 return -ENOMEM;
872
873 if ((1 << it->options[1]) & board->IRQbits) {
874 ret = request_irq(it->options[1], interrupt_pcl816, 0,
875 dev->board_name, dev);
876 if (ret == 0)
877 dev->irq = it->options[1];
878 }
879
880 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
881 devpriv->int816_mode = 0; /* mode of irq */
882
883 /* grab our DMA */
884 dma = 0;
885 devpriv->dma = dma;
886 if (!dev->irq)
887 goto no_dma; /* if we haven't IRQ, we can't use DMA */
888
889 if (board->DMAbits != 0) { /* board support DMA */
890 dma = it->options[2];
891 if (dma < 1)
892 goto no_dma; /* DMA disabled */
893
894 if (((1 << dma) & board->DMAbits) == 0) {
895 dev_err(dev->class_dev,
896 "DMA is out of allowed range, FAIL!\n");
897 return -EINVAL; /* Bad DMA */
898 }
899 ret = request_dma(dma, dev->board_name);
900 if (ret) {
901 dev_err(dev->class_dev,
902 "unable to allocate DMA %u, FAIL!\n", dma);
903 return -EBUSY; /* DMA isn't free */
904 }
905
906 devpriv->dma = dma;
907 pages = 2; /* we need 16KB */
908 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
909
910 if (!devpriv->dmabuf[0]) {
911 dev_err(dev->class_dev,
912 "unable to allocate DMA buffer, FAIL!\n");
913 /*
914 * maybe experiment with try_to_free_pages()
915 * will help ....
916 */
917 return -EBUSY; /* no buffer :-( */
918 }
919 devpriv->dmapages[0] = pages;
920 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
921 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
922
923 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
924 if (!devpriv->dmabuf[1]) {
925 dev_err(dev->class_dev,
926 "unable to allocate DMA buffer, FAIL!\n");
927 return -EBUSY;
928 }
929 devpriv->dmapages[1] = pages;
930 devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]);
931 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
932 }
933
934 no_dma:
935
936 /* if (board->n_aochan > 0)
937 subdevs[1] = COMEDI_SUBD_AO;
938 if (board->n_dichan > 0)
939 subdevs[2] = COMEDI_SUBD_DI;
940 if (board->n_dochan > 0)
941 subdevs[3] = COMEDI_SUBD_DO;
942 */
943
944 ret = comedi_alloc_subdevices(dev, 1);
945 if (ret)
946 return ret;
947
948 s = &dev->subdevices[0];
949 if (board->n_aichan > 0) {
950 s->type = COMEDI_SUBD_AI;
951 devpriv->sub_ai = s;
952 s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
953 s->n_chan = board->n_aichan;
954 s->maxdata = board->ai_maxdata;
955 s->range_table = board->ai_range_type;
956 s->insn_read = pcl816_ai_insn_read;
957 if (dev->irq) {
958 dev->read_subdev = s;
959 s->subdev_flags |= SDF_CMD_READ;
960 s->len_chanlist = board->ai_chanlist;
961 s->do_cmdtest = pcl816_ai_cmdtest;
962 s->do_cmd = pcl816_ai_cmd;
963 s->poll = pcl816_ai_poll;
964 s->cancel = pcl816_ai_cancel;
965 }
966 } else {
967 s->type = COMEDI_SUBD_UNUSED;
968 }
969
970 #if 0
971 case COMEDI_SUBD_AO:
972 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
973 s->n_chan = board->n_aochan;
974 s->maxdata = board->ao_maxdata;
975 s->len_chanlist = board->ao_chanlist;
976 s->range_table = board->ao_range_type;
977 break;
978
979 case COMEDI_SUBD_DI:
980 s->subdev_flags = SDF_READABLE;
981 s->n_chan = board->n_dichan;
982 s->maxdata = 1;
983 s->len_chanlist = board->n_dichan;
984 s->range_table = &range_digital;
985 break;
986
987 case COMEDI_SUBD_DO:
988 s->subdev_flags = SDF_WRITABLE;
989 s->n_chan = board->n_dochan;
990 s->maxdata = 1;
991 s->len_chanlist = board->n_dochan;
992 s->range_table = &range_digital;
993 break;
994 #endif
995
996 pcl816_reset(dev);
997
998 return 0;
999 }
1000
1001 static void pcl816_detach(struct comedi_device *dev)
1002 {
1003 struct pcl816_private *devpriv = dev->private;
1004
1005 if (dev->private) {
1006 pcl816_ai_cancel(dev, devpriv->sub_ai);
1007 pcl816_reset(dev);
1008 if (devpriv->dma)
1009 free_dma(devpriv->dma);
1010 if (devpriv->dmabuf[0])
1011 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1012 if (devpriv->dmabuf[1])
1013 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1014 }
1015 comedi_legacy_detach(dev);
1016 }
1017
1018 static const struct pcl816_board boardtypes[] = {
1019 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1020 &range_pcl816, PCLx1x_RANGE,
1021 0x00fc, /* IRQ mask */
1022 0x0a, /* DMA mask */
1023 0xffff, /* 16-bit card */
1024 0xffff, /* D/A maxdata */
1025 1024,
1026 1, /* ao chan list */
1027 I8254_OSC_BASE_10MHZ},
1028 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1029 &range_pcl816, PCLx1x_RANGE,
1030 0x00fc,
1031 0x0a,
1032 0x3fff, /* 14 bit card */
1033 0x3fff,
1034 1024,
1035 1,
1036 I8254_OSC_BASE_10MHZ},
1037 };
1038
1039 static struct comedi_driver pcl816_driver = {
1040 .driver_name = "pcl816",
1041 .module = THIS_MODULE,
1042 .attach = pcl816_attach,
1043 .detach = pcl816_detach,
1044 .board_name = &boardtypes[0].name,
1045 .num_names = ARRAY_SIZE(boardtypes),
1046 .offset = sizeof(struct pcl816_board),
1047 };
1048 module_comedi_driver(pcl816_driver);
1049
1050 MODULE_AUTHOR("Comedi http://www.comedi.org");
1051 MODULE_DESCRIPTION("Comedi low-level driver");
1052 MODULE_LICENSE("GPL");
This page took 0.052437 seconds and 5 git commands to generate.