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