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