]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/staging/comedi/drivers/pcl816.c
iwlwifi: don't include iwl-dev.h from iwl-devtrace.h
[mirror_ubuntu-jammy-kernel.git] / drivers / staging / comedi / drivers / pcl816.c
CommitLineData
dd2996b3
JG
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/*
12Driver: pcl816
13Description: Advantech PCL-816 cards, PCL-814
14Author: Juan Grigera <juan@grigera.com.ar>
15Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
16Status: works
17Updated: Tue, 2 Apr 2002 23:15:21 -0800
18
19PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20Differences are at resolution (16 vs 12 bits).
21
22The driver support AI command mode, other subdevices not written.
23
24Analog output and digital input and output are not supported.
25
26Configuration 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 "../comedidev.h"
36
37#include <linux/ioport.h>
38#include <linux/mc146818rtc.h>
39#include <linux/delay.h>
40#include <asm/dma.h>
41
42#include "8253.h"
43
44#define DEBUG(x) x
45
58c0576e
BP
46/* boards constants */
47/* IO space len */
dd2996b3
JG
48#define PCLx1x_RANGE 16
49
58c0576e 50/* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
dd2996b3 51
58c0576e 52/* INTEL 8254 counters */
dd2996b3
JG
53#define PCL816_CTR0 4
54#define PCL816_CTR1 5
55#define PCL816_CTR2 6
58c0576e 56/* R: counter read-back register W: counter control */
dd2996b3
JG
57#define PCL816_CTRCTL 7
58
58c0576e 59/* R: A/D high byte W: A/D range control */
dd2996b3 60#define PCL816_RANGE 9
58c0576e 61/* W: clear INT request */
dd2996b3 62#define PCL816_CLRINT 10
58c0576e 63/* R: next mux scan channel W: mux scan channel & range control pointer */
dd2996b3 64#define PCL816_MUX 11
58c0576e 65/* R/W: operation control register */
dd2996b3
JG
66#define PCL816_CONTROL 12
67
58c0576e 68/* R: return status byte W: set DMA/IRQ */
dd2996b3
JG
69#define PCL816_STATUS 13
70#define PCL816_STATUS_DRDY_MASK 0x80
71
58c0576e 72/* R: low byte of A/D W: soft A/D trigger */
dd2996b3 73#define PCL816_AD_LO 8
58c0576e 74/* R: high byte of A/D W: A/D range control */
dd2996b3
JG
75#define PCL816_AD_HI 9
76
58c0576e 77/* type of interrupt handler */
dd2996b3
JG
78#define INT_TYPE_AI1_INT 1
79#define INT_TYPE_AI1_DMA 2
80#define INT_TYPE_AI3_INT 4
81#define INT_TYPE_AI3_DMA 5
82#ifdef unused
83#define INT_TYPE_AI1_DMA_RTC 9
84#define INT_TYPE_AI3_DMA_RTC 10
85
58c0576e 86/* RTC stuff... */
dd2996b3
JG
87#define RTC_IRQ 8
88#define RTC_IO_EXTENT 0x10
89#endif
90
91#define MAGIC_DMA_WORD 0x5a5a
92
9ced1de6 93static const struct comedi_lrange range_pcl816 = { 8, {
0a85b6f0
MT
94 BIP_RANGE(10),
95 BIP_RANGE(5),
96 BIP_RANGE(2.5),
97 BIP_RANGE(1.25),
98 UNI_RANGE(10),
99 UNI_RANGE(5),
100 UNI_RANGE(2.5),
101 UNI_RANGE(1.25),
102 }
dd2996b3 103};
0a85b6f0 104
1c7f40d9
BP
105struct pcl816_board {
106
58c0576e
BP
107 const char *name; /* board name */
108 int n_ranges; /* len of range list */
109 int n_aichan; /* num of A/D chans in diferencial mode */
110 unsigned int ai_ns_min; /* minimal alllowed delay between samples (in ns) */
111 int n_aochan; /* num of D/A chans */
112 int n_dichan; /* num of DI chans */
113 int n_dochan; /* num of DO chans */
114 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
bbc9a991 115 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
58c0576e
BP
116 unsigned int io_range; /* len of IO space */
117 unsigned int IRQbits; /* allowed interrupts */
118 unsigned int DMAbits; /* allowed DMA chans */
119 int ai_maxdata; /* maxdata for A/D */
120 int ao_maxdata; /* maxdata for D/A */
121 int ai_chanlist; /* allowed len of channel list A/D */
122 int ao_chanlist; /* allowed len of channel list D/A */
123 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
1c7f40d9
BP
124};
125
1c7f40d9 126static const struct pcl816_board boardtypes[] = {
dd2996b3 127 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
0a85b6f0
MT
128 &range_pcl816, PCLx1x_RANGE,
129 0x00fc, /* IRQ mask */
130 0x0a, /* DMA mask */
131 0xffff, /* 16-bit card */
132 0xffff, /* D/A maxdata */
133 1024,
134 1, /* ao chan list */
135 100},
dd2996b3 136 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
0a85b6f0
MT
137 &range_pcl816, PCLx1x_RANGE,
138 0x00fc,
139 0x0a,
140 0x3fff, /* 14 bit card */
141 0x3fff,
142 1024,
143 1,
144 100},
dd2996b3
JG
145};
146
1c7f40d9 147#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
fe0ff175 148#define devpriv ((struct pcl816_private *)dev->private)
1c7f40d9 149#define this_board ((const struct pcl816_board *)dev->board_ptr)
dd2996b3 150
0a85b6f0
MT
151static int pcl816_attach(struct comedi_device *dev,
152 struct comedi_devconfig *it);
da91b269 153static int pcl816_detach(struct comedi_device *dev);
dd2996b3
JG
154
155#ifdef unused
156static int RTC_lock = 0; /* RTC lock */
157static int RTC_timer_lock = 0; /* RTC int lock */
158#endif
159
139dfbdf 160static struct comedi_driver driver_pcl816 = {
68c3dbff
BP
161 .driver_name = "pcl816",
162 .module = THIS_MODULE,
163 .attach = pcl816_attach,
164 .detach = pcl816_detach,
165 .board_name = &boardtypes[0].name,
166 .num_names = n_boardtypes,
167 .offset = sizeof(struct pcl816_board),
dd2996b3
JG
168};
169
170COMEDI_INITCLEANUP(driver_pcl816);
171
fe0ff175
BP
172struct pcl816_private {
173
58c0576e
BP
174 unsigned int dma; /* used DMA, 0=don't use DMA */
175 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
dd2996b3 176#ifdef unused
58c0576e 177 unsigned long rtc_iobase; /* RTC port region */
dd2996b3
JG
178 unsigned int rtc_iosize;
179 unsigned int rtc_irq;
180#endif
58c0576e
BP
181 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
182 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
183 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
184 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
185 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
186 unsigned int last_top_dma; /* DMA pointer in last RTC int */
187 int next_dma_buf; /* which DMA buffer will be used next round */
188 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
189 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
190
191 unsigned int ai_scans; /* len of scanlist */
192 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
193 int irq_free; /* 1=have allocated IRQ */
194 int irq_blocked; /* 1=IRQ now uses any subdev */
dd2996b3 195#ifdef unused
58c0576e 196 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
dd2996b3 197#endif
58c0576e
BP
198 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
199 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
200 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
201 int ai_act_scan; /* how many scans we finished */
202 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
203 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
204 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
13de4f00 205 unsigned int ai_n_chan; /* how many channels per scan */
58c0576e
BP
206 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
207 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
dd2996b3 208#ifdef unused
58c0576e
BP
209 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
210 unsigned long rtc_freq; /* RTC int freq */
dd2996b3 211#endif
fe0ff175
BP
212};
213
dd2996b3
JG
214/*
215==============================================================================
216*/
64a1f7bd
IA
217static int check_channel_list(struct comedi_device *dev,
218 struct comedi_subdevice *s,
219 unsigned int *chanlist, unsigned int chanlen);
220static void setup_channel_list(struct comedi_device *dev,
221 struct comedi_subdevice *s,
222 unsigned int *chanlist, unsigned int seglen);
0a85b6f0
MT
223static int pcl816_ai_cancel(struct comedi_device *dev,
224 struct comedi_subdevice *s);
225static void start_pacer(struct comedi_device *dev, int mode,
226 unsigned int divisor1, unsigned int divisor2);
dd2996b3
JG
227#ifdef unused
228static int set_rtc_irq_bit(unsigned char bit);
229#endif
230
0a85b6f0
MT
231static int pcl816_ai_cmdtest(struct comedi_device *dev,
232 struct comedi_subdevice *s,
233 struct comedi_cmd *cmd);
da91b269 234static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
dd2996b3
JG
235
236/*
237==============================================================================
238 ANALOG INPUT MODE0, 816 cards, slow version
239*/
0a85b6f0
MT
240static int pcl816_ai_insn_read(struct comedi_device *dev,
241 struct comedi_subdevice *s,
242 struct comedi_insn *insn, unsigned int *data)
dd2996b3
JG
243{
244 int n;
245 int timeout;
246
247 DPRINTK("mode 0 analog input\n");
58c0576e 248 /* software trigger, DMA and INT off */
dd2996b3 249 outb(0, dev->iobase + PCL816_CONTROL);
58c0576e 250 /* clear INT (conversion end) flag */
dd2996b3
JG
251 outb(0, dev->iobase + PCL816_CLRINT);
252
58c0576e 253 /* Set the input channel */
dd2996b3
JG
254 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
255 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
256
257 for (n = 0; n < insn->n; n++) {
258
259 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
260
261 timeout = 100;
262 while (timeout--) {
263 if (!(inb(dev->iobase + PCL816_STATUS) &
0a85b6f0 264 PCL816_STATUS_DRDY_MASK)) {
58c0576e 265 /* return read value */
dd2996b3 266 data[n] =
0a85b6f0
MT
267 ((inb(dev->iobase +
268 PCL816_AD_HI) << 8) |
269 (inb(dev->iobase + PCL816_AD_LO)));
dd2996b3
JG
270
271 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
272 break;
273 }
5f74ea14 274 udelay(1);
dd2996b3 275 }
58c0576e 276 /* Return timeout error */
dd2996b3
JG
277 if (!timeout) {
278 comedi_error(dev, "A/D insn timeout\n");
279 data[0] = 0;
280 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
281 return -EIO;
282 }
283
284 }
285 return n;
286}
287
288/*
289==============================================================================
290 analog input interrupt mode 1 & 3, 818 cards
291 one sample per interrupt version
292*/
293static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
294{
71b5f4f1 295 struct comedi_device *dev = d;
34c43922 296 struct comedi_subdevice *s = dev->subdevices + 0;
dd2996b3
JG
297 int low, hi;
298 int timeout = 50; /* wait max 50us */
299
300 while (timeout--) {
301 if (!(inb(dev->iobase + PCL816_STATUS) &
0a85b6f0 302 PCL816_STATUS_DRDY_MASK))
dd2996b3 303 break;
5f74ea14 304 udelay(1);
dd2996b3 305 }
58c0576e 306 if (!timeout) { /* timeout, bail error */
dd2996b3
JG
307 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
308 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
309 pcl816_ai_cancel(dev, s);
310 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
311 comedi_event(dev, s);
312 return IRQ_HANDLED;
313
314 }
315
58c0576e 316 /* get the sample */
dd2996b3
JG
317 low = inb(dev->iobase + PCL816_AD_LO);
318 hi = inb(dev->iobase + PCL816_AD_HI);
319
320 comedi_buf_put(s->async, (hi << 8) | low);
321
322 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
323
324 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
325 devpriv->ai_act_chanlist_pos = 0;
326
13de4f00
IA
327 s->async->cur_chan++;
328 if (s->async->cur_chan >= devpriv->ai_n_chan) {
329 s->async->cur_chan = 0;
dd2996b3
JG
330 devpriv->ai_act_scan++;
331 }
332
333 if (!devpriv->ai_neverending)
334 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
335 /* all data sampled */
336 pcl816_ai_cancel(dev, s);
337 s->async->events |= COMEDI_CB_EOA;
338 }
339 comedi_event(dev, s);
340 return IRQ_HANDLED;
341}
342
343/*
344==============================================================================
345 analog input dma mode 1 & 3, 816 cards
346*/
0a85b6f0
MT
347static void transfer_from_dma_buf(struct comedi_device *dev,
348 struct comedi_subdevice *s, short *ptr,
349 unsigned int bufptr, unsigned int len)
dd2996b3
JG
350{
351 int i;
352
353 s->async->events = 0;
354
355 for (i = 0; i < len; i++) {
356
357 comedi_buf_put(s->async, ptr[bufptr++]);
358
359 if (++devpriv->ai_act_chanlist_pos >=
0a85b6f0 360 devpriv->ai_act_chanlist_len) {
dd2996b3 361 devpriv->ai_act_chanlist_pos = 0;
13de4f00
IA
362 }
363
364 s->async->cur_chan++;
365 if (s->async->cur_chan >= devpriv->ai_n_chan) {
366 s->async->cur_chan = 0;
dd2996b3
JG
367 devpriv->ai_act_scan++;
368 }
369
370 if (!devpriv->ai_neverending)
58c0576e 371 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
dd2996b3
JG
372 pcl816_ai_cancel(dev, s);
373 s->async->events |= COMEDI_CB_EOA;
374 s->async->events |= COMEDI_CB_BLOCK;
375 break;
376 }
377 }
378
379 comedi_event(dev, s);
380}
381
382static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
383{
71b5f4f1 384 struct comedi_device *dev = d;
34c43922 385 struct comedi_subdevice *s = dev->subdevices + 0;
dd2996b3
JG
386 int len, bufptr, this_dma_buf;
387 unsigned long dma_flags;
790c5541 388 short *ptr;
dd2996b3
JG
389
390 disable_dma(devpriv->dma);
391 this_dma_buf = devpriv->next_dma_buf;
392
58c0576e 393 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { /* switch dma bufs */
dd2996b3
JG
394
395 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
396 set_dma_mode(devpriv->dma, DMA_MODE_READ);
397 dma_flags = claim_dma_lock();
58c0576e 398/* clear_dma_ff (devpriv->dma); */
dd2996b3 399 set_dma_addr(devpriv->dma,
0a85b6f0 400 devpriv->hwdmaptr[devpriv->next_dma_buf]);
dd2996b3
JG
401 if (devpriv->dma_runs_to_end) {
402 set_dma_count(devpriv->dma,
0a85b6f0
MT
403 devpriv->hwdmasize[devpriv->
404 next_dma_buf]);
dd2996b3
JG
405 } else {
406 set_dma_count(devpriv->dma, devpriv->last_dma_run);
407 }
408 release_dma_lock(dma_flags);
409 enable_dma(devpriv->dma);
410 }
411
412 devpriv->dma_runs_to_end--;
413 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
414
0a85b6f0 415 ptr = (short *)devpriv->dmabuf[this_dma_buf];
dd2996b3
JG
416
417 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
418 bufptr = devpriv->ai_poll_ptr;
419 devpriv->ai_poll_ptr = 0;
420
421 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
422 return IRQ_HANDLED;
423}
424
425/*
426==============================================================================
427 INT procedure
428*/
70265d24 429static irqreturn_t interrupt_pcl816(int irq, void *d)
dd2996b3 430{
71b5f4f1 431 struct comedi_device *dev = d;
dd2996b3
JG
432 DPRINTK("<I>");
433
434 if (!dev->attached) {
435 comedi_error(dev, "premature interrupt");
436 return IRQ_HANDLED;
437 }
438
439 switch (devpriv->int816_mode) {
440 case INT_TYPE_AI1_DMA:
441 case INT_TYPE_AI3_DMA:
442 return interrupt_pcl816_ai_mode13_dma(irq, d);
443 case INT_TYPE_AI1_INT:
444 case INT_TYPE_AI3_INT:
445 return interrupt_pcl816_ai_mode13_int(irq, d);
446 }
447
448 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
449 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
0a85b6f0 450 (!devpriv->int816_mode)) {
dd2996b3
JG
451 if (devpriv->irq_was_now_closed) {
452 devpriv->irq_was_now_closed = 0;
58c0576e 453 /* comedi_error(dev,"last IRQ.."); */
dd2996b3
JG
454 return IRQ_HANDLED;
455 }
456 comedi_error(dev, "bad IRQ!");
457 return IRQ_NONE;
458 }
bbc9a991 459 comedi_error(dev, "IRQ from unknown source!");
dd2996b3
JG
460 return IRQ_NONE;
461}
462
463/*
464==============================================================================
465 COMMAND MODE
466*/
da91b269 467static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
dd2996b3 468{
5f74ea14 469 printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
0a85b6f0 470 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
5f74ea14 471 printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
0a85b6f0 472 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
5f74ea14 473 printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
0a85b6f0 474 cmd->scan_end_src);
5f74ea14 475 printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
0a85b6f0 476 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
dd2996b3
JG
477}
478
479/*
480==============================================================================
481*/
0a85b6f0
MT
482static int pcl816_ai_cmdtest(struct comedi_device *dev,
483 struct comedi_subdevice *s, struct comedi_cmd *cmd)
dd2996b3
JG
484{
485 int err = 0;
48b1aff5 486 int tmp, divisor1 = 0, divisor2 = 0;
dd2996b3 487
0a85b6f0
MT
488 DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
489 );
dd2996b3
JG
490
491 /* step 1: make sure trigger sources are trivially valid */
492 tmp = cmd->start_src;
493 cmd->start_src &= TRIG_NOW;
494 if (!cmd->start_src || tmp != cmd->start_src)
495 err++;
496
497 tmp = cmd->scan_begin_src;
498 cmd->scan_begin_src &= TRIG_FOLLOW;
499 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
500 err++;
501
efdf83c1
IA
502 tmp = cmd->convert_src;
503 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
504 if (!cmd->convert_src || tmp != cmd->convert_src)
dd2996b3
JG
505 err++;
506
507 tmp = cmd->scan_end_src;
508 cmd->scan_end_src &= TRIG_COUNT;
509 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
510 err++;
511
512 tmp = cmd->stop_src;
513 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
514 if (!cmd->stop_src || tmp != cmd->stop_src)
515 err++;
516
517 if (err) {
518 return 1;
519 }
520
521 /* step 2: make sure trigger sources are unique and mutually compatible */
522
523 if (cmd->start_src != TRIG_NOW) {
524 cmd->start_src = TRIG_NOW;
525 err++;
526 }
527
528 if (cmd->scan_begin_src != TRIG_FOLLOW) {
529 cmd->scan_begin_src = TRIG_FOLLOW;
530 err++;
531 }
532
533 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
534 cmd->convert_src = TRIG_TIMER;
535 err++;
536 }
537
538 if (cmd->scan_end_src != TRIG_COUNT) {
539 cmd->scan_end_src = TRIG_COUNT;
540 err++;
541 }
542
543 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
544 err++;
545
546 if (err) {
547 return 2;
548 }
549
550 /* step 3: make sure arguments are trivially compatible */
551 if (cmd->start_arg != 0) {
552 cmd->start_arg = 0;
553 err++;
554 }
555
556 if (cmd->scan_begin_arg != 0) {
557 cmd->scan_begin_arg = 0;
558 err++;
559 }
560 if (cmd->convert_src == TRIG_TIMER) {
561 if (cmd->convert_arg < this_board->ai_ns_min) {
562 cmd->convert_arg = this_board->ai_ns_min;
563 err++;
564 }
565 } else { /* TRIG_EXT */
566 if (cmd->convert_arg != 0) {
567 cmd->convert_arg = 0;
568 err++;
569 }
570 }
571
dd2996b3
JG
572 if (cmd->scan_end_arg != cmd->chanlist_len) {
573 cmd->scan_end_arg = cmd->chanlist_len;
574 err++;
575 }
576 if (cmd->stop_src == TRIG_COUNT) {
577 if (!cmd->stop_arg) {
578 cmd->stop_arg = 1;
579 err++;
580 }
581 } else { /* TRIG_NONE */
582 if (cmd->stop_arg != 0) {
583 cmd->stop_arg = 0;
584 err++;
585 }
586 }
587
588 if (err) {
589 return 3;
590 }
591
592 /* step 4: fix up any arguments */
593 if (cmd->convert_src == TRIG_TIMER) {
594 tmp = cmd->convert_arg;
595 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
0a85b6f0
MT
596 &divisor1, &divisor2,
597 &cmd->convert_arg,
598 cmd->flags & TRIG_ROUND_MASK);
dd2996b3
JG
599 if (cmd->convert_arg < this_board->ai_ns_min)
600 cmd->convert_arg = this_board->ai_ns_min;
601 if (tmp != cmd->convert_arg)
602 err++;
603 }
604
605 if (err) {
606 return 4;
607 }
608
64a1f7bd
IA
609 /* step 5: complain about special chanlist considerations */
610
611 if (cmd->chanlist) {
612 if (!check_channel_list(dev, s, cmd->chanlist,
613 cmd->chanlist_len))
614 return 5; /* incorrect channels list */
615 }
616
dd2996b3
JG
617 return 0;
618}
619
da91b269 620static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
dd2996b3
JG
621{
622 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
ea6d0d4c 623 struct comedi_cmd *cmd = &s->async->cmd;
64a1f7bd 624 unsigned int seglen;
dd2996b3
JG
625
626 if (cmd->start_src != TRIG_NOW)
627 return -EINVAL;
628 if (cmd->scan_begin_src != TRIG_FOLLOW)
629 return -EINVAL;
630 if (cmd->scan_end_src != TRIG_COUNT)
631 return -EINVAL;
632 if (cmd->scan_end_arg != cmd->chanlist_len)
633 return -EINVAL;
58c0576e 634/* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
dd2996b3
JG
635 if (devpriv->irq_blocked)
636 return -EBUSY;
637
638 if (cmd->convert_src == TRIG_TIMER) {
639 if (cmd->convert_arg < this_board->ai_ns_min)
640 cmd->convert_arg = this_board->ai_ns_min;
641
642 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
0a85b6f0
MT
643 &divisor2, &cmd->convert_arg,
644 cmd->flags & TRIG_ROUND_MASK);
58c0576e 645 if (divisor1 == 1) { /* PCL816 crash if any divisor is set to 1 */
dd2996b3
JG
646 divisor1 = 2;
647 divisor2 /= 2;
648 }
649 if (divisor2 == 1) {
650 divisor2 = 2;
651 divisor1 /= 2;
652 }
653 }
654
58c0576e 655 start_pacer(dev, -1, 0, 0); /* stop pacer */
dd2996b3 656
64a1f7bd
IA
657 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
658 if (seglen < 1)
dd2996b3 659 return -EINVAL;
64a1f7bd 660 setup_channel_list(dev, s, cmd->chanlist, seglen);
5f74ea14 661 udelay(1);
dd2996b3 662
13de4f00 663 devpriv->ai_n_chan = cmd->chanlist_len;
dd2996b3
JG
664 devpriv->ai_act_scan = 0;
665 s->async->cur_chan = 0;
666 devpriv->irq_blocked = 1;
667 devpriv->ai_poll_ptr = 0;
668 devpriv->irq_was_now_closed = 0;
669
670 if (cmd->stop_src == TRIG_COUNT) {
671 devpriv->ai_scans = cmd->stop_arg;
672 devpriv->ai_neverending = 0;
673 } else {
674 devpriv->ai_scans = 0;
675 devpriv->ai_neverending = 1;
676 }
677
58c0576e 678 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
dd2996b3 679 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
58c0576e
BP
680 /* devpriv->ai_eos=1; */
681 /* if (devpriv->ai_n_chan==1) */
682 /* devpriv->dma=0; // DMA is useless for this situation */
dd2996b3
JG
683 }
684
685 if (devpriv->dma) {
686 bytes = devpriv->hwdmasize[0];
687 if (!devpriv->ai_neverending) {
58c0576e
BP
688 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
689 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
690 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
dd2996b3
JG
691 devpriv->dma_runs_to_end--;
692 if (devpriv->dma_runs_to_end >= 0)
693 bytes = devpriv->hwdmasize[0];
694 } else
695 devpriv->dma_runs_to_end = -1;
696
697 devpriv->next_dma_buf = 0;
698 set_dma_mode(devpriv->dma, DMA_MODE_READ);
699 dma_flags = claim_dma_lock();
700 clear_dma_ff(devpriv->dma);
701 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
702 set_dma_count(devpriv->dma, bytes);
703 release_dma_lock(dma_flags);
704 enable_dma(devpriv->dma);
705 }
706
707 start_pacer(dev, 1, divisor1, divisor2);
708 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
709
710 switch (cmd->convert_src) {
711 case TRIG_TIMER:
712 devpriv->int816_mode = INT_TYPE_AI1_DMA;
58c0576e
BP
713 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
714 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
dd2996b3
JG
715 break;
716
717 default:
718 devpriv->int816_mode = INT_TYPE_AI3_DMA;
58c0576e
BP
719 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
720 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
dd2996b3
JG
721 break;
722 }
723
724 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
725 return 0;
726}
727
da91b269 728static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
dd2996b3
JG
729{
730 unsigned long flags;
731 unsigned int top1, top2, i;
732
733 if (!devpriv->dma)
58c0576e 734 return 0; /* poll is valid only for DMA transfer */
dd2996b3 735
5f74ea14 736 spin_lock_irqsave(&dev->spinlock, flags);
dd2996b3
JG
737
738 for (i = 0; i < 20; i++) {
58c0576e 739 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
dd2996b3
JG
740 top2 = get_dma_residue(devpriv->dma);
741 if (top1 == top2)
742 break;
743 }
744 if (top1 != top2) {
5f74ea14 745 spin_unlock_irqrestore(&dev->spinlock, flags);
dd2996b3
JG
746 return 0;
747 }
748
58c0576e
BP
749 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
750 top1 >>= 1; /* sample position */
dd2996b3 751 top2 = top1 - devpriv->ai_poll_ptr;
58c0576e 752 if (top2 < 1) { /* no new samples */
5f74ea14 753 spin_unlock_irqrestore(&dev->spinlock, flags);
dd2996b3
JG
754 return 0;
755 }
756
757 transfer_from_dma_buf(dev, s,
0a85b6f0
MT
758 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
759 devpriv->ai_poll_ptr, top2);
dd2996b3 760
58c0576e 761 devpriv->ai_poll_ptr = top1; /* new buffer position */
5f74ea14 762 spin_unlock_irqrestore(&dev->spinlock, flags);
dd2996b3
JG
763
764 return s->async->buf_write_count - s->async->buf_read_count;
765}
766
767/*
768==============================================================================
769 cancel any mode 1-4 AI
770*/
0a85b6f0
MT
771static int pcl816_ai_cancel(struct comedi_device *dev,
772 struct comedi_subdevice *s)
dd2996b3 773{
5f74ea14 774/* DEBUG(printk("pcl816_ai_cancel()\n");) */
dd2996b3
JG
775
776 if (devpriv->irq_blocked > 0) {
777 switch (devpriv->int816_mode) {
778#ifdef unused
779 case INT_TYPE_AI1_DMA_RTC:
780 case INT_TYPE_AI3_DMA_RTC:
58c0576e 781 set_rtc_irq_bit(0); /* stop RTC */
dd2996b3
JG
782 del_timer(&devpriv->rtc_irq_timer);
783#endif
784 case INT_TYPE_AI1_DMA:
785 case INT_TYPE_AI3_DMA:
786 disable_dma(devpriv->dma);
787 case INT_TYPE_AI1_INT:
788 case INT_TYPE_AI3_INT:
789 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
5f74ea14 790 udelay(1);
dd2996b3
JG
791 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
792 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
793 outb(0x70, dev->iobase + PCL816_CTRCTL);
794 outb(0, dev->iobase + PCL816_AD_LO);
795 inb(dev->iobase + PCL816_AD_LO);
796 inb(dev->iobase + PCL816_AD_HI);
797 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
798 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
799 devpriv->irq_blocked = 0;
800 devpriv->irq_was_now_closed = devpriv->int816_mode;
801 devpriv->int816_mode = 0;
802 devpriv->last_int_sub = s;
58c0576e 803/* s->busy = 0; */
dd2996b3
JG
804 break;
805 }
806 }
807
0a85b6f0
MT
808 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
809 return 0;
dd2996b3
JG
810}
811
812/*
813==============================================================================
814 chech for PCL816
815*/
816static int pcl816_check(unsigned long iobase)
817{
818 outb(0x00, iobase + PCL816_MUX);
5f74ea14 819 udelay(1);
dd2996b3 820 if (inb(iobase + PCL816_MUX) != 0x00)
58c0576e 821 return 1; /* there isn't card */
dd2996b3 822 outb(0x55, iobase + PCL816_MUX);
5f74ea14 823 udelay(1);
dd2996b3 824 if (inb(iobase + PCL816_MUX) != 0x55)
58c0576e 825 return 1; /* there isn't card */
dd2996b3 826 outb(0x00, iobase + PCL816_MUX);
5f74ea14 827 udelay(1);
dd2996b3 828 outb(0x18, iobase + PCL816_CONTROL);
5f74ea14 829 udelay(1);
dd2996b3 830 if (inb(iobase + PCL816_CONTROL) != 0x18)
58c0576e
BP
831 return 1; /* there isn't card */
832 return 0; /* ok, card exist */
dd2996b3
JG
833}
834
835/*
836==============================================================================
837 reset whole PCL-816 cards
838*/
da91b269 839static void pcl816_reset(struct comedi_device *dev)
dd2996b3 840{
58c0576e
BP
841/* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
842/* outb (0, dev->iobase + PCL818_DA_HI); */
5f74ea14 843/* udelay (1); */
58c0576e
BP
844/* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
845/* outb (0, dev->iobase + PCL818_DO_LO); */
5f74ea14 846/* udelay (1); */
dd2996b3
JG
847 outb(0, dev->iobase + PCL816_CONTROL);
848 outb(0, dev->iobase + PCL816_MUX);
849 outb(0, dev->iobase + PCL816_CLRINT);
850 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
851 outb(0x70, dev->iobase + PCL816_CTRCTL);
852 outb(0x30, dev->iobase + PCL816_CTRCTL);
853 outb(0, dev->iobase + PCL816_RANGE);
854}
855
856/*
857==============================================================================
858 Start/stop pacer onboard pacer
859*/
860static void
da91b269 861start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
0a85b6f0 862 unsigned int divisor2)
dd2996b3
JG
863{
864 outb(0x32, dev->iobase + PCL816_CTRCTL);
865 outb(0xff, dev->iobase + PCL816_CTR0);
866 outb(0x00, dev->iobase + PCL816_CTR0);
5f74ea14 867 udelay(1);
58c0576e
BP
868 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
869 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
5f74ea14 870 udelay(1);
dd2996b3
JG
871
872 if (mode == 1) {
873 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
874 divisor2);
875 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
876 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
877 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
878 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
879 }
880
881 /* clear pending interrupts (just in case) */
58c0576e 882/* outb(0, dev->iobase + PCL816_CLRINT); */
dd2996b3
JG
883}
884
885/*
886==============================================================================
887 Check if channel list from user is builded correctly
64a1f7bd 888 If it's ok, then return non-zero length of repeated segment of channel list
dd2996b3
JG
889*/
890static int
64a1f7bd
IA
891check_channel_list(struct comedi_device *dev,
892 struct comedi_subdevice *s, unsigned int *chanlist,
893 unsigned int chanlen)
dd2996b3
JG
894{
895 unsigned int chansegment[16];
896 unsigned int i, nowmustbechan, seglen, segpos;
897
58c0576e 898 /* correct channel and range number check itself comedi/range.c */
dd2996b3
JG
899 if (chanlen < 1) {
900 comedi_error(dev, "range/channel list is empty!");
901 return 0;
902 }
903
904 if (chanlen > 1) {
58c0576e 905 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
dd2996b3 906 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
58c0576e 907 /* build part of chanlist */
5f74ea14 908 DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
0a85b6f0
MT
909 CR_RANGE(chanlist[i]));)
910 if (chanlist[0] == chanlist[i])
58c0576e 911 break; /* we detect loop, this must by finish */
dd2996b3 912 nowmustbechan =
0a85b6f0 913 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
dd2996b3 914 if (nowmustbechan != CR_CHAN(chanlist[i])) {
58c0576e 915 /* channel list isn't continous :-( */
5f74ea14 916 printk
0a85b6f0
MT
917 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
918 dev->minor, i, CR_CHAN(chanlist[i]),
919 nowmustbechan, CR_CHAN(chanlist[0]));
dd2996b3
JG
920 return 0;
921 }
58c0576e 922 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
dd2996b3
JG
923 }
924
58c0576e 925 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
5f74ea14 926 DEBUG(printk("%d %d=%d %d\n",
0a85b6f0
MT
927 CR_CHAN(chansegment[i % seglen]),
928 CR_RANGE(chansegment[i % seglen]),
929 CR_CHAN(chanlist[i]),
930 CR_RANGE(chanlist[i]));)
931 if (chanlist[i] != chansegment[i % seglen]) {
5f74ea14 932 printk
0a85b6f0
MT
933 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
934 dev->minor, i, CR_CHAN(chansegment[i]),
935 CR_RANGE(chansegment[i]),
936 CR_AREF(chansegment[i]),
937 CR_CHAN(chanlist[i % seglen]),
938 CR_RANGE(chanlist[i % seglen]),
939 CR_AREF(chansegment[i % seglen]));
58c0576e 940 return 0; /* chan/gain list is strange */
dd2996b3
JG
941 }
942 }
943 } else {
944 seglen = 1;
945 }
946
64a1f7bd
IA
947 return seglen; /* we can serve this with MUX logic */
948}
949
950/*
951==============================================================================
952 Program scan/gain logic with channel list.
953*/
954static void
955setup_channel_list(struct comedi_device *dev,
956 struct comedi_subdevice *s, unsigned int *chanlist,
957 unsigned int seglen)
958{
959 unsigned int i;
960
dd2996b3
JG
961 devpriv->ai_act_chanlist_len = seglen;
962 devpriv->ai_act_chanlist_pos = 0;
963
58c0576e 964 for (i = 0; i < seglen; i++) { /* store range list to card */
dd2996b3
JG
965 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
966 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
967 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
968 }
969
5f74ea14 970 udelay(1);
dd2996b3
JG
971
972 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
dd2996b3
JG
973}
974
975#ifdef unused
976/*
977==============================================================================
978 Enable(1)/disable(0) periodic interrupts from RTC
979*/
980static int set_rtc_irq_bit(unsigned char bit)
981{
982 unsigned char val;
983 unsigned long flags;
984
985 if (bit == 1) {
986 RTC_timer_lock++;
987 if (RTC_timer_lock > 1)
988 return 0;
989 } else {
990 RTC_timer_lock--;
991 if (RTC_timer_lock < 0)
992 RTC_timer_lock = 0;
993 if (RTC_timer_lock > 0)
994 return 0;
995 }
996
997 save_flags(flags);
998 cli();
999 val = CMOS_READ(RTC_CONTROL);
1000 if (bit) {
1001 val |= RTC_PIE;
1002 } else {
1003 val &= ~RTC_PIE;
1004 }
1005 CMOS_WRITE(val, RTC_CONTROL);
1006 CMOS_READ(RTC_INTR_FLAGS);
1007 restore_flags(flags);
1008 return 0;
1009}
1010#endif
1011
1012/*
1013==============================================================================
1014 Free any resources that we have claimed
1015*/
da91b269 1016static void free_resources(struct comedi_device *dev)
dd2996b3 1017{
5f74ea14 1018 /* printk("free_resource()\n"); */
dd2996b3
JG
1019 if (dev->private) {
1020 pcl816_ai_cancel(dev, devpriv->sub_ai);
1021 pcl816_reset(dev);
1022 if (devpriv->dma)
1023 free_dma(devpriv->dma);
1024 if (devpriv->dmabuf[0])
1025 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1026 if (devpriv->dmabuf[1])
1027 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1028#ifdef unused
1029 if (devpriv->rtc_irq)
5f74ea14 1030 free_irq(devpriv->rtc_irq, dev);
dd2996b3
JG
1031 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1032 if (devpriv->rtc_iobase)
1033 release_region(devpriv->rtc_iobase,
0a85b6f0 1034 devpriv->rtc_iosize);
dd2996b3
JG
1035 }
1036#endif
1037 }
1038
1039 if (dev->irq)
1040 free_irq(dev->irq, dev);
1041 if (dev->iobase)
1042 release_region(dev->iobase, this_board->io_range);
5f74ea14 1043 /* printk("free_resource() end\n"); */
dd2996b3
JG
1044}
1045
1046/*
1047==============================================================================
1048
1049 Initialization
1050
1051*/
da91b269 1052static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
dd2996b3
JG
1053{
1054 int ret;
1055 unsigned long iobase;
1056 unsigned int irq, dma;
1057 unsigned long pages;
58c0576e 1058 /* int i; */
34c43922 1059 struct comedi_subdevice *s;
dd2996b3
JG
1060
1061 /* claim our I/O space */
1062 iobase = it->options[0];
1063 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
0a85b6f0 1064 this_board->name, iobase);
dd2996b3
JG
1065
1066 if (!request_region(iobase, this_board->io_range, "pcl816")) {
5f74ea14 1067 printk("I/O port conflict\n");
dd2996b3
JG
1068 return -EIO;
1069 }
1070
1071 dev->iobase = iobase;
1072
1073 if (pcl816_check(iobase)) {
5f74ea14 1074 printk(", I cann't detect board. FAIL!\n");
dd2996b3
JG
1075 return -EIO;
1076 }
1077
c3744138
BP
1078 ret = alloc_private(dev, sizeof(struct pcl816_private));
1079 if (ret < 0)
dd2996b3
JG
1080 return ret; /* Can't alloc mem */
1081
1082 /* set up some name stuff */
1083 dev->board_name = this_board->name;
1084
1085 /* grab our IRQ */
1086 irq = 0;
1087 if (this_board->IRQbits != 0) { /* board support IRQ */
1088 irq = it->options[1];
1089 if (irq) { /* we want to use IRQ */
1090 if (((1 << irq) & this_board->IRQbits) == 0) {
5f74ea14 1091 printk
0a85b6f0
MT
1092 (", IRQ %u is out of allowed range, DISABLING IT",
1093 irq);
dd2996b3
JG
1094 irq = 0; /* Bad IRQ */
1095 } else {
0a85b6f0
MT
1096 if (request_irq
1097 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
5f74ea14 1098 printk
0a85b6f0
MT
1099 (", unable to allocate IRQ %u, DISABLING IT",
1100 irq);
dd2996b3
JG
1101 irq = 0; /* Can't use IRQ */
1102 } else {
5f74ea14 1103 printk(", irq=%u", irq);
dd2996b3
JG
1104 }
1105 }
1106 }
1107 }
1108
1109 dev->irq = irq;
1110 if (irq) {
1111 devpriv->irq_free = 1;
1112 } /* 1=we have allocated irq */
1113 else {
1114 devpriv->irq_free = 0;
1115 }
1116 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1117 devpriv->int816_mode = 0; /* mode of irq */
1118
1119#ifdef unused
1120 /* grab RTC for DMA operations */
1121 devpriv->dma_rtc = 0;
58c0576e 1122 if (it->options[2] > 0) { /* we want to use DMA */
dd2996b3
JG
1123 if (RTC_lock == 0) {
1124 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
0a85b6f0 1125 "pcl816 (RTC)"))
dd2996b3
JG
1126 goto no_rtc;
1127 }
1128 devpriv->rtc_iobase = RTC_PORT(0);
1129 devpriv->rtc_iosize = RTC_IO_EXTENT;
1130 RTC_lock++;
1131#ifdef UNTESTED_CODE
5f74ea14 1132 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
0a85b6f0 1133 "pcl816 DMA (RTC)", dev)) {
dd2996b3
JG
1134 devpriv->dma_rtc = 1;
1135 devpriv->rtc_irq = RTC_IRQ;
5f74ea14 1136 printk(", dma_irq=%u", devpriv->rtc_irq);
dd2996b3
JG
1137 } else {
1138 RTC_lock--;
1139 if (RTC_lock == 0) {
1140 if (devpriv->rtc_iobase)
1141 release_region(devpriv->rtc_iobase,
0a85b6f0 1142 devpriv->rtc_iosize);
dd2996b3
JG
1143 }
1144 devpriv->rtc_iobase = 0;
1145 devpriv->rtc_iosize = 0;
1146 }
1147#else
1148 printk("pcl816: RTC code missing");
1149#endif
1150
1151 }
1152
0a85b6f0 1153no_rtc:
dd2996b3
JG
1154#endif
1155 /* grab our DMA */
1156 dma = 0;
1157 devpriv->dma = dma;
1158 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1159 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1160
1161 if (this_board->DMAbits != 0) { /* board support DMA */
1162 dma = it->options[2];
1163 if (dma < 1)
1164 goto no_dma; /* DMA disabled */
1165
1166 if (((1 << dma) & this_board->DMAbits) == 0) {
5f74ea14 1167 printk(", DMA is out of allowed range, FAIL!\n");
dd2996b3
JG
1168 return -EINVAL; /* Bad DMA */
1169 }
1170 ret = request_dma(dma, "pcl816");
1171 if (ret) {
5f74ea14 1172 printk(", unable to allocate DMA %u, FAIL!\n", dma);
dd2996b3
JG
1173 return -EBUSY; /* DMA isn't free */
1174 }
1175
1176 devpriv->dma = dma;
5f74ea14 1177 printk(", dma=%u", dma);
dd2996b3
JG
1178 pages = 2; /* we need 16KB */
1179 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1180
1181 if (!devpriv->dmabuf[0]) {
5f74ea14 1182 printk(", unable to allocate DMA buffer, FAIL!\n");
dd2996b3
JG
1183 /* maybe experiment with try_to_free_pages() will help .... */
1184 return -EBUSY; /* no buffer :-( */
1185 }
1186 devpriv->dmapages[0] = pages;
1187 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1188 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
5f74ea14 1189 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
dd2996b3 1190
58c0576e 1191 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
dd2996b3
JG
1192 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1193 if (!devpriv->dmabuf[1]) {
5f74ea14 1194 printk
0a85b6f0 1195 (", unable to allocate DMA buffer, FAIL!\n");
dd2996b3
JG
1196 return -EBUSY;
1197 }
1198 devpriv->dmapages[1] = pages;
1199 devpriv->hwdmaptr[1] =
0a85b6f0 1200 virt_to_bus((void *)devpriv->dmabuf[1]);
dd2996b3
JG
1201 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1202 }
1203 }
1204
0a85b6f0 1205no_dma:
dd2996b3
JG
1206
1207/* if (this_board->n_aochan > 0)
1208 subdevs[1] = COMEDI_SUBD_AO;
1209 if (this_board->n_dichan > 0)
1210 subdevs[2] = COMEDI_SUBD_DI;
1211 if (this_board->n_dochan > 0)
1212 subdevs[3] = COMEDI_SUBD_DO;
1213*/
c3744138
BP
1214
1215 ret = alloc_subdevices(dev, 1);
1216 if (ret < 0)
dd2996b3
JG
1217 return ret;
1218
1219 s = dev->subdevices + 0;
1220 if (this_board->n_aichan > 0) {
1221 s->type = COMEDI_SUBD_AI;
1222 devpriv->sub_ai = s;
1223 dev->read_subdev = s;
1224 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1225 s->n_chan = this_board->n_aichan;
1226 s->subdev_flags |= SDF_DIFF;
58c0576e 1227 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
dd2996b3
JG
1228 s->maxdata = this_board->ai_maxdata;
1229 s->len_chanlist = this_board->ai_chanlist;
1230 s->range_table = this_board->ai_range_type;
1231 s->cancel = pcl816_ai_cancel;
1232 s->do_cmdtest = pcl816_ai_cmdtest;
1233 s->do_cmd = pcl816_ai_cmd;
1234 s->poll = pcl816_ai_poll;
1235 s->insn_read = pcl816_ai_insn_read;
1236 } else {
1237 s->type = COMEDI_SUBD_UNUSED;
1238 }
1239
1240#if 0
1241case COMEDI_SUBD_AO:
1242 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1243 s->n_chan = this_board->n_aochan;
1244 s->maxdata = this_board->ao_maxdata;
1245 s->len_chanlist = this_board->ao_chanlist;
1246 s->range_table = this_board->ao_range_type;
1247 break;
1248
1249case COMEDI_SUBD_DI:
1250 s->subdev_flags = SDF_READABLE;
1251 s->n_chan = this_board->n_dichan;
1252 s->maxdata = 1;
1253 s->len_chanlist = this_board->n_dichan;
1254 s->range_table = &range_digital;
1255 break;
1256
1257case COMEDI_SUBD_DO:
1258 s->subdev_flags = SDF_WRITABLE;
1259 s->n_chan = this_board->n_dochan;
1260 s->maxdata = 1;
1261 s->len_chanlist = this_board->n_dochan;
1262 s->range_table = &range_digital;
1263 break;
1264#endif
1265
1266 pcl816_reset(dev);
1267
5f74ea14 1268 printk("\n");
dd2996b3
JG
1269
1270 return 0;
1271}
1272
1273/*
1274==============================================================================
1275 Removes device
1276 */
da91b269 1277static int pcl816_detach(struct comedi_device *dev)
dd2996b3 1278{
0a85b6f0
MT
1279 DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1280 free_resources(dev);
dd2996b3
JG
1281#ifdef unused
1282 if (devpriv->dma_rtc)
1283 RTC_lock--;
1284#endif
1285 return 0;
1286}