hwdrv_apci3120.c revision 3672effdeae5395d661a2103f69082146ef949fc
1/**
2@verbatim
3
4Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
5
6	ADDI-DATA GmbH
7	Dieselstrasse 3
8	D-77833 Ottersweier
9	Tel: +19(0)7223/9493-0
10	Fax: +49(0)7223/9493-92
11	http://www.addi-data.com
12	info@addi-data.com
13
14This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17
18@endverbatim
19*/
20/*
21  +-----------------------------------------------------------------------+
22  | (C) ADDI-DATA GmbH          Dieselstrasse 3      D-77833 Ottersweier  |
23  +-----------------------------------------------------------------------+
24  | Tel : +49 (0) 7223/9493-0     | email    : info@addi-data.com         |
25  | Fax : +49 (0) 7223/9493-92    | Internet : http://www.addi-data.com   |
26  +-----------------------------------------------------------------------+
27  | Project     : APCI-3120       | Compiler   : GCC                      |
28  | Module name : hwdrv_apci3120.c| Version    : 2.96                     |
29  +-------------------------------+---------------------------------------+
30  | Project manager: Eric Stolz   | Date       :  02/12/2002              |
31  +-----------------------------------------------------------------------+
32  | Description :APCI3120 Module.  Hardware abstraction Layer for APCI3120|
33  +-----------------------------------------------------------------------+
34  |                             UPDATE'S                                  |
35  +-----------------------------------------------------------------------+
36  |   Date   |   Author  |          Description of updates                |
37  +----------+-----------+------------------------------------------------+
38  |          | 		 | 						  |
39  |          |           |						  |
40  +----------+-----------+------------------------------------------------+
41*/
42
43#include <linux/delay.h>
44
45/*
46 * ADDON RELATED ADDITIONS
47 */
48/* Constant */
49#define APCI3120_ENABLE_TRANSFER_ADD_ON_LOW		0x00
50#define APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH		0x1200
51#define APCI3120_A2P_FIFO_MANAGEMENT			0x04000400L
52#define APCI3120_AMWEN_ENABLE				0x02
53#define APCI3120_A2P_FIFO_WRITE_ENABLE			0x01
54#define APCI3120_FIFO_ADVANCE_ON_BYTE_2			0x20000000L
55#define APCI3120_ENABLE_WRITE_TC_INT			0x00004000L
56#define APCI3120_CLEAR_WRITE_TC_INT			0x00040000L
57#define APCI3120_DISABLE_AMWEN_AND_A2P_FIFO_WRITE	0x0
58#define APCI3120_DISABLE_BUS_MASTER_ADD_ON		0x0
59#define APCI3120_DISABLE_BUS_MASTER_PCI			0x0
60
61/* ADD_ON ::: this needed since apci supports 16 bit interface to add on */
62#define APCI3120_ADD_ON_AGCSTS_LOW	0x3C
63#define APCI3120_ADD_ON_AGCSTS_HIGH	(APCI3120_ADD_ON_AGCSTS_LOW + 2)
64#define APCI3120_ADD_ON_MWAR_LOW	0x24
65#define APCI3120_ADD_ON_MWAR_HIGH	(APCI3120_ADD_ON_MWAR_LOW + 2)
66#define APCI3120_ADD_ON_MWTC_LOW	0x058
67#define APCI3120_ADD_ON_MWTC_HIGH	(APCI3120_ADD_ON_MWTC_LOW + 2)
68
69/* AMCC */
70#define APCI3120_AMCC_OP_MCSR		0x3C
71#define APCI3120_AMCC_OP_REG_INTCSR	0x38
72
73/* for transfer count enable bit */
74#define AGCSTS_TC_ENABLE	0x10000000
75
76/* used for test on mixture of BIP/UNI ranges */
77#define APCI3120_BIPOLAR_RANGES		4
78
79#define APCI3120_ADDRESS_RANGE		16
80
81#define APCI3120_DISABLE		0
82#define APCI3120_ENABLE			1
83
84#define APCI3120_START			1
85#define APCI3120_STOP			0
86
87#define APCI3120_EOC_MODE		1
88#define APCI3120_EOS_MODE		2
89#define APCI3120_DMA_MODE		3
90
91/* DIGITAL INPUT-OUTPUT DEFINE */
92
93#define APCI3120_DIGITAL_OUTPUT		0x0d
94#define APCI3120_RD_STATUS		0x02
95#define APCI3120_RD_FIFO		0x00
96
97/* digital output insn_write ON /OFF selection */
98#define	APCI3120_SET4DIGITALOUTPUTON	1
99#define APCI3120_SET4DIGITALOUTPUTOFF	0
100
101/* analog output SELECT BIT */
102#define APCI3120_ANALOG_OP_CHANNEL_1	0x0000
103#define APCI3120_ANALOG_OP_CHANNEL_2	0x4000
104#define APCI3120_ANALOG_OP_CHANNEL_3	0x8000
105#define APCI3120_ANALOG_OP_CHANNEL_4	0xc000
106#define APCI3120_ANALOG_OP_CHANNEL_5	0x0000
107#define APCI3120_ANALOG_OP_CHANNEL_6	0x4000
108#define APCI3120_ANALOG_OP_CHANNEL_7	0x8000
109#define APCI3120_ANALOG_OP_CHANNEL_8	0xc000
110
111/* Enable external trigger bit in nWrAddress */
112#define APCI3120_ENABLE_EXT_TRIGGER	0x8000
113
114/* ANALOG OUTPUT AND INPUT DEFINE */
115#define APCI3120_UNIPOLAR		0x80
116#define APCI3120_BIPOLAR		0x00
117#define APCI3120_ANALOG_OUTPUT_1	0x08
118#define APCI3120_ANALOG_OUTPUT_2	0x0a
119#define APCI3120_1_GAIN			0x00
120#define APCI3120_2_GAIN			0x10
121#define APCI3120_5_GAIN			0x20
122#define APCI3120_10_GAIN		0x30
123#define APCI3120_SEQ_RAM_ADDRESS	0x06
124#define APCI3120_RESET_FIFO		0x0c
125#define APCI3120_TIMER_0_MODE_2		0x01
126#define APCI3120_TIMER_0_MODE_4		0x2
127#define APCI3120_SELECT_TIMER_0_WORD	0x00
128#define APCI3120_ENABLE_TIMER0		0x1000
129#define APCI3120_CLEAR_PR		0xf0ff
130#define APCI3120_CLEAR_PA		0xfff0
131#define APCI3120_CLEAR_PA_PR		(APCI3120_CLEAR_PR & APCI3120_CLEAR_PA)
132
133/* nWrMode_Select */
134#define APCI3120_ENABLE_SCAN		0x8
135#define APCI3120_DISABLE_SCAN		(~APCI3120_ENABLE_SCAN)
136#define APCI3120_ENABLE_EOS_INT		0x2
137
138#define APCI3120_DISABLE_EOS_INT	(~APCI3120_ENABLE_EOS_INT)
139#define APCI3120_ENABLE_EOC_INT		0x1
140#define APCI3120_DISABLE_EOC_INT	(~APCI3120_ENABLE_EOC_INT)
141#define APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER	\
142	(APCI3120_DISABLE_EOS_INT & APCI3120_DISABLE_EOC_INT)
143#define APCI3120_DISABLE_ALL_INTERRUPT			\
144	(APCI3120_DISABLE_TIMER_INT & APCI3120_DISABLE_EOS_INT & APCI3120_DISABLE_EOC_INT)
145
146/* status register bits */
147#define APCI3120_EOC			0x8000
148#define APCI3120_EOS			0x2000
149
150/* software trigger dummy register */
151#define APCI3120_START_CONVERSION	0x02
152
153/* TIMER DEFINE */
154#define APCI3120_QUARTZ_A		70
155#define APCI3120_QUARTZ_B		50
156#define APCI3120_TIMER			1
157#define APCI3120_WATCHDOG		2
158#define APCI3120_TIMER_DISABLE		0
159#define APCI3120_TIMER_ENABLE		1
160#define APCI3120_ENABLE_TIMER2		0x4000
161#define APCI3120_DISABLE_TIMER2		(~APCI3120_ENABLE_TIMER2)
162#define APCI3120_ENABLE_TIMER_INT	0x04
163#define APCI3120_DISABLE_TIMER_INT	(~APCI3120_ENABLE_TIMER_INT)
164#define APCI3120_WRITE_MODE_SELECT	0x0e
165#define APCI3120_SELECT_TIMER_0_WORD	0x00
166#define APCI3120_SELECT_TIMER_1_WORD	0x01
167#define APCI3120_TIMER_1_MODE_2		0x4
168
169/* $$ BIT FOR MODE IN nCsTimerCtr1 */
170#define APCI3120_TIMER_2_MODE_0		0x0
171#define APCI3120_TIMER_2_MODE_2		0x10
172#define APCI3120_TIMER_2_MODE_5		0x30
173
174/* $$ BIT FOR MODE IN nCsTimerCtr0 */
175#define APCI3120_SELECT_TIMER_2_LOW_WORD	0x02
176#define APCI3120_SELECT_TIMER_2_HIGH_WORD	0x03
177
178#define APCI3120_TIMER_CRT0		0x0d
179#define APCI3120_TIMER_CRT1		0x0c
180
181#define APCI3120_TIMER_VALUE		0x04
182#define APCI3120_TIMER_STATUS_REGISTER	0x0d
183#define APCI3120_RD_STATUS		0x02
184#define APCI3120_WR_ADDRESS		0x00
185#define APCI3120_ENABLE_WATCHDOG	0x20
186#define APCI3120_DISABLE_WATCHDOG	(~APCI3120_ENABLE_WATCHDOG)
187#define APCI3120_ENABLE_TIMER_COUNTER	0x10
188#define APCI3120_DISABLE_TIMER_COUNTER	(~APCI3120_ENABLE_TIMER_COUNTER)
189#define APCI3120_FC_TIMER		0x1000
190#define APCI3120_ENABLE_TIMER0		0x1000
191#define APCI3120_ENABLE_TIMER1		0x2000
192#define APCI3120_ENABLE_TIMER2		0x4000
193#define APCI3120_DISABLE_TIMER0		(~APCI3120_ENABLE_TIMER0)
194#define APCI3120_DISABLE_TIMER1		(~APCI3120_ENABLE_TIMER1)
195#define APCI3120_DISABLE_TIMER2		(~APCI3120_ENABLE_TIMER2)
196
197#define APCI3120_TIMER2_SELECT_EOS	0xc0
198#define APCI3120_COUNTER		3
199#define APCI3120_DISABLE_ALL_TIMER	(APCI3120_DISABLE_TIMER0 &	\
200					 APCI3120_DISABLE_TIMER1 &	\
201					 APCI3120_DISABLE_TIMER2)
202
203#define MAX_ANALOGINPUT_CHANNELS	32
204
205struct str_AnalogReadInformation {
206	/* EOC or EOS */
207	unsigned char b_Type;
208	/* Interrupt use or not */
209	unsigned char b_InterruptFlag;
210	/* Selection of the conversion time */
211	unsigned int ui_ConvertTiming;
212	/* Number of channel to read */
213	unsigned char b_NbrOfChannel;
214	/* Number of the channel to be read */
215	unsigned int ui_ChannelList[MAX_ANALOGINPUT_CHANNELS];
216	/* Gain of each channel */
217	unsigned int ui_RangeList[MAX_ANALOGINPUT_CHANNELS];
218};
219
220/* ANALOG INPUT RANGE */
221static const struct comedi_lrange range_apci3120_ai = {
222	8, {
223		BIP_RANGE(10),
224		BIP_RANGE(5),
225		BIP_RANGE(2),
226		BIP_RANGE(1),
227		UNI_RANGE(10),
228		UNI_RANGE(5),
229		UNI_RANGE(2),
230		UNI_RANGE(1)
231	}
232};
233
234/* ANALOG OUTPUT RANGE */
235static const struct comedi_lrange range_apci3120_ao = {
236	2, {
237		BIP_RANGE(10),
238		UNI_RANGE(10)
239	}
240};
241
242
243/* FUNCTION DEFINITIONS */
244
245/*
246+----------------------------------------------------------------------------+
247|                           ANALOG INPUT SUBDEVICE   		                 |
248+----------------------------------------------------------------------------+
249*/
250
251static int apci3120_ai_insn_config(struct comedi_device *dev,
252				   struct comedi_subdevice *s,
253				   struct comedi_insn *insn,
254				   unsigned int *data)
255{
256	const struct addi_board *this_board = comedi_board(dev);
257	struct addi_private *devpriv = dev->private;
258	unsigned int i;
259
260	if ((data[0] != APCI3120_EOC_MODE) && (data[0] != APCI3120_EOS_MODE))
261		return -1;
262
263	/*  Check for Conversion time to be added ?? */
264	devpriv->ui_EocEosConversionTime = data[2];
265
266	if (data[0] == APCI3120_EOS_MODE) {
267
268		/* Test the number of the channel */
269		for (i = 0; i < data[3]; i++) {
270
271			if (CR_CHAN(data[4 + i]) >=
272				this_board->i_NbrAiChannel) {
273				printk("bad channel list\n");
274				return -2;
275			}
276		}
277
278		devpriv->b_InterruptMode = APCI3120_EOS_MODE;
279
280		if (data[1])
281			devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
282		else
283			devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
284		/*  Copy channel list and Range List to devpriv */
285
286		devpriv->ui_AiNbrofChannels = data[3];
287		for (i = 0; i < devpriv->ui_AiNbrofChannels; i++)
288			devpriv->ui_AiChannelList[i] = data[4 + i];
289
290	} else {			/*  EOC */
291		devpriv->b_InterruptMode = APCI3120_EOC_MODE;
292		if (data[1])
293			devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
294		else
295			devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
296	}
297
298	return insn->n;
299}
300
301/*
302 * This function will first check channel list is ok or not and then
303 * initialize the sequence RAM with the polarity, Gain,Channel number.
304 * If the last argument of function "check"is 1 then it only checks
305 * the channel list is ok or not.
306 */
307static int apci3120_setup_chan_list(struct comedi_device *dev,
308				    struct comedi_subdevice *s,
309				    int n_chan,
310				    unsigned int *chanlist,
311				    char check)
312{
313	struct addi_private *devpriv = dev->private;
314	unsigned int i;		/* , differencial=0, bipolar=0; */
315	unsigned int gain;
316	unsigned short us_TmpValue;
317
318	/* correct channel and range number check itself comedi/range.c */
319	if (n_chan < 1) {
320		if (!check)
321			comedi_error(dev, "range/channel list is empty!");
322		return 0;
323	}
324	/*  All is ok, so we can setup channel/range list */
325	if (check)
326		return 1;
327
328	/* Code  to set the PA and PR...Here it set PA to 0.. */
329	devpriv->us_OutputRegister =
330		devpriv->us_OutputRegister & APCI3120_CLEAR_PA_PR;
331	devpriv->us_OutputRegister = ((n_chan - 1) & 0xf) << 8;
332	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
333
334	for (i = 0; i < n_chan; i++) {
335		/*  store range list to card */
336		us_TmpValue = CR_CHAN(chanlist[i]);	/*  get channel number; */
337
338		if (CR_RANGE(chanlist[i]) < APCI3120_BIPOLAR_RANGES)
339			us_TmpValue &= ((~APCI3120_UNIPOLAR) & 0xff);	/*  set bipolar */
340		else
341			us_TmpValue |= APCI3120_UNIPOLAR;	/*  enable unipolar...... */
342
343		gain = CR_RANGE(chanlist[i]);	/*  get gain number */
344		us_TmpValue |= ((gain & 0x03) << 4);	/* <<4 for G0 and G1 bit in RAM */
345		us_TmpValue |= i << 8;	/* To select the RAM LOCATION.... */
346		outw(us_TmpValue, dev->iobase + APCI3120_SEQ_RAM_ADDRESS);
347
348		printk("\n Gain = %i",
349			(((unsigned char)CR_RANGE(chanlist[i]) & 0x03) << 2));
350		printk("\n Channel = %i", CR_CHAN(chanlist[i]));
351		printk("\n Polarity = %i", us_TmpValue & APCI3120_UNIPOLAR);
352	}
353	return 1;		/*  we can serve this with scan logic */
354}
355
356/*
357 * Reads analog input in synchronous mode EOC and EOS is selected
358 * as per configured if no conversion time is set uses default
359 * conversion time 10 microsec.
360 */
361static int apci3120_ai_insn_read(struct comedi_device *dev,
362				 struct comedi_subdevice *s,
363				 struct comedi_insn *insn,
364				 unsigned int *data)
365{
366	const struct addi_board *this_board = comedi_board(dev);
367	struct addi_private *devpriv = dev->private;
368	unsigned short us_ConvertTiming, us_TmpValue, i;
369	unsigned char b_Tmp;
370
371	/*  fix conversion time to 10 us */
372	if (!devpriv->ui_EocEosConversionTime) {
373		printk("No timer0 Value using 10 us\n");
374		us_ConvertTiming = 10;
375	} else
376		us_ConvertTiming = (unsigned short) (devpriv->ui_EocEosConversionTime / 1000);	/*  nano to useconds */
377
378	/*  this_board->ai_read(dev,us_ConvertTiming,insn->n,&insn->chanspec,data,insn->unused[0]); */
379
380	/*  Clear software registers */
381	devpriv->b_TimerSelectMode = 0;
382	devpriv->b_ModeSelectRegister = 0;
383	devpriv->us_OutputRegister = 0;
384/* devpriv->b_DigitalOutputRegister=0; */
385
386	if (insn->unused[0] == 222) {	/*  second insn read */
387		for (i = 0; i < insn->n; i++)
388			data[i] = devpriv->ui_AiReadData[i];
389	} else {
390		devpriv->tsk_Current = current;	/*  Save the current process task structure */
391/*
392 * Testing if board have the new Quartz and calculate the time value
393 * to set in the timer
394 */
395
396		us_TmpValue =
397			(unsigned short) inw(devpriv->iobase + APCI3120_RD_STATUS);
398
399		/* EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 */
400		if ((us_TmpValue & 0x00B0) == 0x00B0
401			|| !strcmp(this_board->pc_DriverName, "apci3001")) {
402			us_ConvertTiming = (us_ConvertTiming * 2) - 2;
403		} else {
404			us_ConvertTiming =
405				((us_ConvertTiming * 12926) / 10000) - 1;
406		}
407
408		us_TmpValue = (unsigned short) devpriv->b_InterruptMode;
409
410		switch (us_TmpValue) {
411
412		case APCI3120_EOC_MODE:
413
414/*
415 * Testing the interrupt flag and set the EOC bit Clears the FIFO
416 */
417			inw(devpriv->iobase + APCI3120_RESET_FIFO);
418
419			/*  Initialize the sequence array */
420			if (!apci3120_setup_chan_list(dev, s, 1,
421					&insn->chanspec, 0))
422				return -EINVAL;
423
424			/* Initialize Timer 0 mode 4 */
425			devpriv->b_TimerSelectMode =
426				(devpriv->
427				b_TimerSelectMode & 0xFC) |
428				APCI3120_TIMER_0_MODE_4;
429			outb(devpriv->b_TimerSelectMode,
430				devpriv->iobase + APCI3120_TIMER_CRT1);
431
432			/*  Reset the scan bit and Disables the  EOS, DMA, EOC interrupt */
433			devpriv->b_ModeSelectRegister =
434				devpriv->
435				b_ModeSelectRegister & APCI3120_DISABLE_SCAN;
436
437			if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
438
439				/* Disables the EOS,DMA and enables the EOC interrupt */
440				devpriv->b_ModeSelectRegister =
441					(devpriv->
442					b_ModeSelectRegister &
443					APCI3120_DISABLE_EOS_INT) |
444					APCI3120_ENABLE_EOC_INT;
445				inw(devpriv->iobase);
446
447			} else {
448				devpriv->b_ModeSelectRegister =
449					devpriv->
450					b_ModeSelectRegister &
451					APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER;
452			}
453
454			outb(devpriv->b_ModeSelectRegister,
455				devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
456
457			/*  Sets gate 0 */
458			devpriv->us_OutputRegister =
459				(devpriv->
460				us_OutputRegister & APCI3120_CLEAR_PA_PR) |
461				APCI3120_ENABLE_TIMER0;
462			outw(devpriv->us_OutputRegister,
463				devpriv->iobase + APCI3120_WR_ADDRESS);
464
465			/*  Select Timer 0 */
466			b_Tmp = ((devpriv->
467					b_DigitalOutputRegister) & 0xF0) |
468				APCI3120_SELECT_TIMER_0_WORD;
469			outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
470
471			/* Set the conversion time */
472			outw(us_ConvertTiming,
473				devpriv->iobase + APCI3120_TIMER_VALUE);
474
475			us_TmpValue =
476				(unsigned short) inw(dev->iobase + APCI3120_RD_STATUS);
477
478			if (devpriv->b_EocEosInterrupt == APCI3120_DISABLE) {
479
480				do {
481					/*  Waiting for the end of conversion */
482					us_TmpValue =
483						inw(devpriv->iobase +
484						APCI3120_RD_STATUS);
485				} while ((us_TmpValue & APCI3120_EOC) ==
486					APCI3120_EOC);
487
488				/* Read the result in FIFO  and put it in insn data pointer */
489				us_TmpValue = inw(devpriv->iobase + 0);
490				*data = us_TmpValue;
491
492				inw(devpriv->iobase + APCI3120_RESET_FIFO);
493			}
494
495			break;
496
497		case APCI3120_EOS_MODE:
498
499			inw(devpriv->iobase);
500			/*  Clears the FIFO */
501			inw(devpriv->iobase + APCI3120_RESET_FIFO);
502			/*  clear PA PR  and disable timer 0 */
503
504			devpriv->us_OutputRegister =
505				(devpriv->
506				us_OutputRegister & APCI3120_CLEAR_PA_PR) |
507				APCI3120_DISABLE_TIMER0;
508
509			outw(devpriv->us_OutputRegister,
510				devpriv->iobase + APCI3120_WR_ADDRESS);
511
512			if (!apci3120_setup_chan_list(dev, s,
513					devpriv->ui_AiNbrofChannels,
514					devpriv->ui_AiChannelList, 0))
515				return -EINVAL;
516
517			/* Initialize Timer 0 mode 2 */
518			devpriv->b_TimerSelectMode =
519				(devpriv->
520				b_TimerSelectMode & 0xFC) |
521				APCI3120_TIMER_0_MODE_2;
522			outb(devpriv->b_TimerSelectMode,
523				devpriv->iobase + APCI3120_TIMER_CRT1);
524
525			/* Select Timer 0 */
526			b_Tmp = ((devpriv->
527					b_DigitalOutputRegister) & 0xF0) |
528				APCI3120_SELECT_TIMER_0_WORD;
529			outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
530
531			/* Set the conversion time */
532			outw(us_ConvertTiming,
533				devpriv->iobase + APCI3120_TIMER_VALUE);
534
535			/* Set the scan bit */
536			devpriv->b_ModeSelectRegister =
537				devpriv->
538				b_ModeSelectRegister | APCI3120_ENABLE_SCAN;
539			outb(devpriv->b_ModeSelectRegister,
540				devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
541
542			/* If Interrupt function is loaded */
543			if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
544				/* Disables the EOC,DMA and enables the EOS interrupt */
545				devpriv->b_ModeSelectRegister =
546					(devpriv->
547					b_ModeSelectRegister &
548					APCI3120_DISABLE_EOC_INT) |
549					APCI3120_ENABLE_EOS_INT;
550				inw(devpriv->iobase);
551
552			} else
553				devpriv->b_ModeSelectRegister =
554					devpriv->
555					b_ModeSelectRegister &
556					APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER;
557
558			outb(devpriv->b_ModeSelectRegister,
559				devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
560
561			inw(devpriv->iobase + APCI3120_RD_STATUS);
562
563			/* Sets gate 0 */
564
565			devpriv->us_OutputRegister =
566				devpriv->
567				us_OutputRegister | APCI3120_ENABLE_TIMER0;
568			outw(devpriv->us_OutputRegister,
569				devpriv->iobase + APCI3120_WR_ADDRESS);
570
571			/* Start conversion */
572			outw(0, devpriv->iobase + APCI3120_START_CONVERSION);
573
574			/* Waiting of end of conversion if interrupt is not installed */
575			if (devpriv->b_EocEosInterrupt == APCI3120_DISABLE) {
576				/* Waiting the end of conversion */
577				do {
578					us_TmpValue =
579						inw(devpriv->iobase +
580						APCI3120_RD_STATUS);
581				} while ((us_TmpValue & APCI3120_EOS) !=
582					 APCI3120_EOS);
583
584				for (i = 0; i < devpriv->ui_AiNbrofChannels;
585					i++) {
586					/* Read the result in FIFO and write them in shared memory */
587					us_TmpValue = inw(devpriv->iobase);
588					data[i] = (unsigned int) us_TmpValue;
589				}
590
591				devpriv->b_InterruptMode = APCI3120_EOC_MODE;	/*  Restore defaults. */
592			}
593			break;
594
595		default:
596			printk("inputs wrong\n");
597
598		}
599		devpriv->ui_EocEosConversionTime = 0;	/*  re initializing the variable; */
600	}
601
602	return insn->n;
603
604}
605
606static int apci3120_reset(struct comedi_device *dev)
607{
608	struct addi_private *devpriv = dev->private;
609	unsigned int i;
610	unsigned short us_TmpValue;
611
612	devpriv->ai_running = 0;
613	devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
614	devpriv->b_InterruptMode = APCI3120_EOC_MODE;
615	devpriv->ui_EocEosConversionTime = 0;	/*  set eoc eos conv time to 0 */
616
617	/*  variables used in timer subdevice */
618	devpriv->b_Timer2Mode = 0;
619	devpriv->b_Timer2Interrupt = 0;
620	devpriv->b_ExttrigEnable = 0;	/*  Disable ext trigger */
621
622	/* Disable all interrupts, watchdog for the anolog output */
623	devpriv->b_ModeSelectRegister = 0;
624	outb(devpriv->b_ModeSelectRegister,
625		dev->iobase + APCI3120_WRITE_MODE_SELECT);
626
627	/*  Disables all counters, ext trigger and clears PA, PR */
628	devpriv->us_OutputRegister = 0;
629	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
630
631/*
632 * Code to set the all anolog o/p channel to 0v 8191 is decimal
633 * value for zero(0 v)volt in bipolar mode(default)
634 */
635	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_1, dev->iobase + APCI3120_ANALOG_OUTPUT_1);	/* channel 1 */
636	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_2, dev->iobase + APCI3120_ANALOG_OUTPUT_1);	/* channel 2 */
637	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_3, dev->iobase + APCI3120_ANALOG_OUTPUT_1);	/* channel 3 */
638	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_4, dev->iobase + APCI3120_ANALOG_OUTPUT_1);	/* channel 4 */
639
640	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_5, dev->iobase + APCI3120_ANALOG_OUTPUT_2);	/* channel 5 */
641	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_6, dev->iobase + APCI3120_ANALOG_OUTPUT_2);	/* channel 6 */
642	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_7, dev->iobase + APCI3120_ANALOG_OUTPUT_2);	/* channel 7 */
643	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_8, dev->iobase + APCI3120_ANALOG_OUTPUT_2);	/* channel 8 */
644
645	/*   Reset digital output to L0W */
646
647/* ES05  outb(0x0,dev->iobase+APCI3120_DIGITAL_OUTPUT); */
648	udelay(10);
649
650	inw(dev->iobase + 0);	/* make a dummy read */
651	inb(dev->iobase + APCI3120_RESET_FIFO);	/*  flush FIFO */
652	inw(dev->iobase + APCI3120_RD_STATUS);	/*  flush A/D status register */
653
654	/* code to reset the RAM sequence */
655	for (i = 0; i < 16; i++) {
656		us_TmpValue = i << 8;	/* select the location */
657		outw(us_TmpValue, dev->iobase + APCI3120_SEQ_RAM_ADDRESS);
658	}
659	return 0;
660}
661
662static int apci3120_exttrig_enable(struct comedi_device *dev)
663{
664	struct addi_private *devpriv = dev->private;
665
666	devpriv->us_OutputRegister |= APCI3120_ENABLE_EXT_TRIGGER;
667	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
668	return 0;
669}
670
671static int apci3120_exttrig_disable(struct comedi_device *dev)
672{
673	struct addi_private *devpriv = dev->private;
674
675	devpriv->us_OutputRegister &= ~APCI3120_ENABLE_EXT_TRIGGER;
676	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
677	return 0;
678}
679
680static int apci3120_cancel(struct comedi_device *dev,
681			   struct comedi_subdevice *s)
682{
683	struct addi_private *devpriv = dev->private;
684
685	/*  Disable A2P Fifo write and AMWEN signal */
686	outw(0, devpriv->i_IobaseAddon + 4);
687
688	/* Disable Bus Master ADD ON */
689	outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
690	outw(0, devpriv->i_IobaseAddon + 2);
691	outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
692	outw(0, devpriv->i_IobaseAddon + 2);
693
694	/* Disable BUS Master PCI */
695	outl(0, devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
696
697	/* outl(inl(devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR)&(~AINT_WRITE_COMPL),
698	 * devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR);  stop amcc irqs */
699
700	/* outl(inl(devpriv->i_IobaseAmcc+AMCC_OP_REG_MCSR)&(~EN_A2P_TRANSFERS),
701	 * devpriv->i_IobaseAmcc+AMCC_OP_REG_MCSR);  stop DMA */
702
703	/* Disable ext trigger */
704	apci3120_exttrig_disable(dev);
705
706	devpriv->us_OutputRegister = 0;
707	/* stop  counters */
708	outw(devpriv->
709		us_OutputRegister & APCI3120_DISABLE_TIMER0 &
710		APCI3120_DISABLE_TIMER1, dev->iobase + APCI3120_WR_ADDRESS);
711
712	outw(APCI3120_DISABLE_ALL_TIMER, dev->iobase + APCI3120_WR_ADDRESS);
713
714	/* DISABLE_ALL_INTERRUPT */
715	outb(APCI3120_DISABLE_ALL_INTERRUPT,
716		dev->iobase + APCI3120_WRITE_MODE_SELECT);
717	/* Flush FIFO */
718	inb(dev->iobase + APCI3120_RESET_FIFO);
719	inw(dev->iobase + APCI3120_RD_STATUS);
720	devpriv->ui_AiActualScan = 0;
721	s->async->cur_chan = 0;
722	devpriv->ui_DmaActualBuffer = 0;
723
724	devpriv->ai_running = 0;
725	devpriv->b_InterruptMode = APCI3120_EOC_MODE;
726	devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
727	apci3120_reset(dev);
728	return 0;
729}
730
731static int apci3120_ai_cmdtest(struct comedi_device *dev,
732			       struct comedi_subdevice *s,
733			       struct comedi_cmd *cmd)
734{
735	int err = 0;
736
737	/* Step 1 : check if triggers are trivially valid */
738
739	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
740	err |= cfc_check_trigger_src(&cmd->scan_begin_src,
741					TRIG_TIMER | TRIG_FOLLOW);
742	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
743	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
744	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
745
746	if (err)
747		return 1;
748
749	/* Step 2a : make sure trigger sources are unique */
750
751	err |= cfc_check_trigger_is_unique(cmd->start_src);
752	err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
753	err |= cfc_check_trigger_is_unique(cmd->stop_src);
754
755	/* Step 2b : and mutually compatible */
756
757	if (err)
758		return 2;
759
760	/* Step 3: check if arguments are trivially valid */
761
762	err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
763
764	if (cmd->scan_begin_src == TRIG_TIMER)	/* Test Delay timing */
765		err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, 100000);
766
767	if (cmd->scan_begin_src == TRIG_TIMER) {
768		if (cmd->convert_arg)
769			err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
770							 10000);
771	} else {
772		err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
773	}
774
775	err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
776	err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
777
778	if (cmd->stop_src == TRIG_COUNT)
779		err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
780	else	/*  TRIG_NONE */
781		err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
782
783	if (err)
784		return 3;
785
786	/*  step 4: fix up any arguments */
787
788	if (cmd->scan_begin_src == TRIG_TIMER &&
789	    cmd->scan_begin_arg < cmd->convert_arg * cmd->scan_end_arg) {
790		cmd->scan_begin_arg = cmd->convert_arg * cmd->scan_end_arg;
791		err |= -EINVAL;
792	}
793
794	if (err)
795		return 4;
796
797	return 0;
798}
799
800/*
801 * This is used for analog input cyclic acquisition.
802 * Performs the command operations.
803 * If DMA is configured does DMA initialization otherwise does the
804 * acquisition with EOS interrupt.
805 */
806static int apci3120_cyclic_ai(int mode,
807			      struct comedi_device *dev,
808			      struct comedi_subdevice *s)
809{
810	const struct addi_board *this_board = comedi_board(dev);
811	struct addi_private *devpriv = dev->private;
812	struct comedi_cmd *cmd = &s->async->cmd;
813	unsigned char b_Tmp;
814	unsigned int ui_Tmp, ui_DelayTiming = 0, ui_TimerValue1 = 0, dmalen0 =
815		0, dmalen1 = 0, ui_TimerValue2 =
816		0, ui_TimerValue0, ui_ConvertTiming;
817	unsigned short us_TmpValue;
818
819	/*******************/
820	/* Resets the FIFO */
821	/*******************/
822	inb(dev->iobase + APCI3120_RESET_FIFO);
823
824	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
825	/* inw(dev->iobase+APCI3120_RD_STATUS); */
826	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
827
828	devpriv->ai_running = 1;
829
830	/*  clear software  registers */
831	devpriv->b_TimerSelectMode = 0;
832	devpriv->us_OutputRegister = 0;
833	devpriv->b_ModeSelectRegister = 0;
834	/* devpriv->b_DigitalOutputRegister=0; */
835
836	/* COMMENT JK 07.05.04: Followings calls are in i_APCI3120_StartAnalogInputAcquisition */
837
838	/****************************/
839	/* Clear Timer Write TC int */
840	/****************************/
841	outl(APCI3120_CLEAR_WRITE_TC_INT,
842		devpriv->i_IobaseAmcc + APCI3120_AMCC_OP_REG_INTCSR);
843
844	/************************************/
845	/* Clears the timer status register */
846	/************************************/
847
848	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
849	/* inw(dev->iobase+APCI3120_TIMER_STATUS_REGISTER); */
850	/* inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER); */
851	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
852
853	/**************************/
854	/* Disables All Timer     */
855	/* Sets PR and PA to 0    */
856	/**************************/
857	devpriv->us_OutputRegister = devpriv->us_OutputRegister &
858		APCI3120_DISABLE_TIMER0 &
859		APCI3120_DISABLE_TIMER1 & APCI3120_CLEAR_PA_PR;
860
861	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
862
863	/*******************/
864	/* Resets the FIFO */
865	/*******************/
866	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
867	inb(devpriv->iobase + APCI3120_RESET_FIFO);
868	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
869
870	devpriv->ui_AiActualScan = 0;
871	s->async->cur_chan = 0;
872	devpriv->ui_DmaActualBuffer = 0;
873
874	/*  value for timer2  minus -2 has to be done .....dunno y?? */
875	ui_TimerValue2 = cmd->stop_arg - 2;
876	ui_ConvertTiming = cmd->convert_arg;
877
878	if (mode == 2)
879		ui_DelayTiming = cmd->scan_begin_arg;
880
881   /**********************************/
882	/* Initializes the sequence array */
883   /**********************************/
884	if (!apci3120_setup_chan_list(dev, s, devpriv->ui_AiNbrofChannels,
885			cmd->chanlist, 0))
886		return -EINVAL;
887
888	us_TmpValue = (unsigned short) inw(dev->iobase + APCI3120_RD_STATUS);
889/*** EL241003 : add this section in comment because floats must not be used
890	if((us_TmpValue & 0x00B0)==0x00B0)
891	 {
892		f_ConvertValue=(((float)ui_ConvertTiming * 0.002) - 2);
893		ui_TimerValue0=(unsigned int)f_ConvertValue;
894		if (mode==2)
895		{
896			f_DelayValue     = (((float)ui_DelayTiming * 0.00002) - 2);
897			ui_TimerValue1  =   (unsigned int) f_DelayValue;
898		}
899	 }
900	else
901	 {
902		f_ConvertValue=(((float)ui_ConvertTiming * 0.0012926) - 1);
903		ui_TimerValue0=(unsigned int)f_ConvertValue;
904		if (mode == 2)
905		{
906		     f_DelayValue     = (((float)ui_DelayTiming * 0.000012926) - 1);
907		     ui_TimerValue1  =   (unsigned int) f_DelayValue;
908		}
909	}
910***********************************************************************************************/
911/*** EL241003 Begin : add this section to replace floats calculation by integer calculations **/
912	/* EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 */
913	if ((us_TmpValue & 0x00B0) == 0x00B0
914		|| !strcmp(this_board->pc_DriverName, "apci3001")) {
915		ui_TimerValue0 = ui_ConvertTiming * 2 - 2000;
916		ui_TimerValue0 = ui_TimerValue0 / 1000;
917
918		if (mode == 2) {
919			ui_DelayTiming = ui_DelayTiming / 1000;
920			ui_TimerValue1 = ui_DelayTiming * 2 - 200;
921			ui_TimerValue1 = ui_TimerValue1 / 100;
922		}
923	} else {
924		ui_ConvertTiming = ui_ConvertTiming / 1000;
925		ui_TimerValue0 = ui_ConvertTiming * 12926 - 10000;
926		ui_TimerValue0 = ui_TimerValue0 / 10000;
927
928		if (mode == 2) {
929			ui_DelayTiming = ui_DelayTiming / 1000;
930			ui_TimerValue1 = ui_DelayTiming * 12926 - 1;
931			ui_TimerValue1 = ui_TimerValue1 / 1000000;
932		}
933	}
934/*** EL241003 End ******************************************************************************/
935
936	if (devpriv->b_ExttrigEnable == APCI3120_ENABLE)
937		apci3120_exttrig_enable(dev);	/*  activate EXT trigger */
938	switch (mode) {
939	case 1:
940		/*  init timer0 in mode 2 */
941		devpriv->b_TimerSelectMode =
942			(devpriv->
943			b_TimerSelectMode & 0xFC) | APCI3120_TIMER_0_MODE_2;
944		outb(devpriv->b_TimerSelectMode,
945			dev->iobase + APCI3120_TIMER_CRT1);
946
947		/* Select Timer 0 */
948		b_Tmp = ((devpriv->
949				b_DigitalOutputRegister) & 0xF0) |
950			APCI3120_SELECT_TIMER_0_WORD;
951		outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
952		/* Set the conversion time */
953		outw(((unsigned short) ui_TimerValue0),
954			dev->iobase + APCI3120_TIMER_VALUE);
955		break;
956
957	case 2:
958		/*  init timer1 in mode 2 */
959		devpriv->b_TimerSelectMode =
960			(devpriv->
961			b_TimerSelectMode & 0xF3) | APCI3120_TIMER_1_MODE_2;
962		outb(devpriv->b_TimerSelectMode,
963			dev->iobase + APCI3120_TIMER_CRT1);
964
965		/* Select Timer 1 */
966		b_Tmp = ((devpriv->
967				b_DigitalOutputRegister) & 0xF0) |
968			APCI3120_SELECT_TIMER_1_WORD;
969		outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
970		/* Set the conversion time */
971		outw(((unsigned short) ui_TimerValue1),
972			dev->iobase + APCI3120_TIMER_VALUE);
973
974		/*  init timer0 in mode 2 */
975		devpriv->b_TimerSelectMode =
976			(devpriv->
977			b_TimerSelectMode & 0xFC) | APCI3120_TIMER_0_MODE_2;
978		outb(devpriv->b_TimerSelectMode,
979			dev->iobase + APCI3120_TIMER_CRT1);
980
981		/* Select Timer 0 */
982		b_Tmp = ((devpriv->
983				b_DigitalOutputRegister) & 0xF0) |
984			APCI3120_SELECT_TIMER_0_WORD;
985		outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
986
987		/* Set the conversion time */
988		outw(((unsigned short) ui_TimerValue0),
989			dev->iobase + APCI3120_TIMER_VALUE);
990		break;
991
992	}
993	/*    ##########common for all modes################# */
994
995	/***********************/
996	/* Clears the SCAN bit */
997	/***********************/
998
999	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
1000	/* devpriv->b_ModeSelectRegister=devpriv->b_ModeSelectRegister | APCI3120_DISABLE_SCAN; */
1001
1002	devpriv->b_ModeSelectRegister = devpriv->b_ModeSelectRegister &
1003		APCI3120_DISABLE_SCAN;
1004	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1005
1006	outb(devpriv->b_ModeSelectRegister,
1007		dev->iobase + APCI3120_WRITE_MODE_SELECT);
1008
1009	/*  If DMA is disabled */
1010	if (devpriv->us_UseDma == APCI3120_DISABLE) {
1011		/*  disable EOC and enable EOS */
1012		devpriv->b_InterruptMode = APCI3120_EOS_MODE;
1013		devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
1014
1015		devpriv->b_ModeSelectRegister =
1016			(devpriv->
1017			b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT) |
1018			APCI3120_ENABLE_EOS_INT;
1019		outb(devpriv->b_ModeSelectRegister,
1020			dev->iobase + APCI3120_WRITE_MODE_SELECT);
1021
1022		if (cmd->stop_src == TRIG_COUNT) {
1023/*
1024 * configure Timer2 For counting EOS Reset gate 2 of Timer 2 to
1025 * disable it (Set Bit D14 to 0)
1026 */
1027			devpriv->us_OutputRegister =
1028				devpriv->
1029				us_OutputRegister & APCI3120_DISABLE_TIMER2;
1030			outw(devpriv->us_OutputRegister,
1031				dev->iobase + APCI3120_WR_ADDRESS);
1032
1033			/*  DISABLE TIMER intERRUPT */
1034			devpriv->b_ModeSelectRegister =
1035				devpriv->
1036				b_ModeSelectRegister &
1037				APCI3120_DISABLE_TIMER_INT & 0xEF;
1038			outb(devpriv->b_ModeSelectRegister,
1039				dev->iobase + APCI3120_WRITE_MODE_SELECT);
1040
1041			/* (1) Init timer 2 in mode 0 and write timer value */
1042			devpriv->b_TimerSelectMode =
1043				(devpriv->
1044				b_TimerSelectMode & 0x0F) |
1045				APCI3120_TIMER_2_MODE_0;
1046			outb(devpriv->b_TimerSelectMode,
1047				dev->iobase + APCI3120_TIMER_CRT1);
1048
1049			/* Writing LOW unsigned short */
1050			b_Tmp = ((devpriv->
1051					b_DigitalOutputRegister) & 0xF0) |
1052				APCI3120_SELECT_TIMER_2_LOW_WORD;
1053			outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
1054			outw(LOWORD(ui_TimerValue2),
1055				dev->iobase + APCI3120_TIMER_VALUE);
1056
1057			/* Writing HIGH unsigned short */
1058			b_Tmp = ((devpriv->
1059					b_DigitalOutputRegister) & 0xF0) |
1060				APCI3120_SELECT_TIMER_2_HIGH_WORD;
1061			outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
1062			outw(HIWORD(ui_TimerValue2),
1063				dev->iobase + APCI3120_TIMER_VALUE);
1064
1065			/* (2) Reset FC_TIMER BIT  Clearing timer status register */
1066			inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
1067			/*  enable timer counter and disable watch dog */
1068			devpriv->b_ModeSelectRegister =
1069				(devpriv->
1070				b_ModeSelectRegister |
1071				APCI3120_ENABLE_TIMER_COUNTER) &
1072				APCI3120_DISABLE_WATCHDOG;
1073			/*  select EOS clock input for timer 2 */
1074			devpriv->b_ModeSelectRegister =
1075				devpriv->
1076				b_ModeSelectRegister |
1077				APCI3120_TIMER2_SELECT_EOS;
1078			/*  Enable timer2  interrupt */
1079			devpriv->b_ModeSelectRegister =
1080				devpriv->
1081				b_ModeSelectRegister |
1082				APCI3120_ENABLE_TIMER_INT;
1083			outb(devpriv->b_ModeSelectRegister,
1084				dev->iobase + APCI3120_WRITE_MODE_SELECT);
1085			devpriv->b_Timer2Mode = APCI3120_COUNTER;
1086			devpriv->b_Timer2Interrupt = APCI3120_ENABLE;
1087		}
1088	} else {
1089		/* If DMA Enabled */
1090		unsigned int scan_bytes = cmd->scan_end_arg * sizeof(short);
1091
1092		/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
1093		/* inw(dev->iobase+0); reset EOC bit */
1094		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1095		devpriv->b_InterruptMode = APCI3120_DMA_MODE;
1096
1097		/************************************/
1098		/* Disables the EOC, EOS interrupt  */
1099		/************************************/
1100		devpriv->b_ModeSelectRegister = devpriv->b_ModeSelectRegister &
1101			APCI3120_DISABLE_EOC_INT & APCI3120_DISABLE_EOS_INT;
1102
1103		outb(devpriv->b_ModeSelectRegister,
1104			dev->iobase + APCI3120_WRITE_MODE_SELECT);
1105
1106		dmalen0 = devpriv->ui_DmaBufferSize[0];
1107		dmalen1 = devpriv->ui_DmaBufferSize[1];
1108
1109		if (cmd->stop_src == TRIG_COUNT) {
1110			/*
1111			 * Must we fill full first buffer? And must we fill
1112			 * full second buffer when first is once filled?
1113			 */
1114			if (dmalen0 > (cmd->stop_arg * scan_bytes)) {
1115				dmalen0 = cmd->stop_arg * scan_bytes;
1116			} else if (dmalen1 > (cmd->stop_arg * scan_bytes -
1117					      dmalen0))
1118				dmalen1 = cmd->stop_arg * scan_bytes -
1119					  dmalen0;
1120		}
1121
1122		if (cmd->flags & TRIG_WAKE_EOS) {
1123			/*  don't we want wake up every scan? */
1124			if (dmalen0 > scan_bytes) {
1125				dmalen0 = scan_bytes;
1126				if (cmd->scan_end_arg & 1)
1127					dmalen0 += 2;
1128			}
1129			if (dmalen1 > scan_bytes) {
1130				dmalen1 = scan_bytes;
1131				if (cmd->scan_end_arg & 1)
1132					dmalen1 -= 2;
1133				if (dmalen1 < 4)
1134					dmalen1 = 4;
1135			}
1136		} else {	/*  isn't output buff smaller that our DMA buff? */
1137			if (dmalen0 > s->async->prealloc_bufsz)
1138				dmalen0 = s->async->prealloc_bufsz;
1139			if (dmalen1 > s->async->prealloc_bufsz)
1140				dmalen1 = s->async->prealloc_bufsz;
1141		}
1142		devpriv->ui_DmaBufferUsesize[0] = dmalen0;
1143		devpriv->ui_DmaBufferUsesize[1] = dmalen1;
1144
1145		/* Initialize DMA */
1146
1147/*
1148 * Set Transfer count enable bit and A2P_fifo reset bit in AGCSTS
1149 * register 1
1150 */
1151		ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
1152		outl(ui_Tmp, devpriv->i_IobaseAmcc + AMCC_OP_REG_AGCSTS);
1153
1154		/*  changed  since 16 bit interface for add on */
1155		/*********************/
1156		/* ENABLE BUS MASTER */
1157		/*********************/
1158		outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
1159		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
1160			devpriv->i_IobaseAddon + 2);
1161
1162		outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
1163		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH,
1164			devpriv->i_IobaseAddon + 2);
1165
1166/*
1167 * TO VERIFIED BEGIN JK 07.05.04: Comparison between WIN32 and Linux
1168 * driver
1169 */
1170		outw(0x1000, devpriv->i_IobaseAddon + 2);
1171		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1172
1173		/* 2 No change */
1174		/* A2P FIFO MANAGEMENT */
1175		/* A2P fifo reset & transfer control enable */
1176
1177		/***********************/
1178		/* A2P FIFO MANAGEMENT */
1179		/***********************/
1180		outl(APCI3120_A2P_FIFO_MANAGEMENT, devpriv->i_IobaseAmcc +
1181			APCI3120_AMCC_OP_MCSR);
1182
1183/*
1184 * 3
1185 * beginning address of dma buf The 32 bit address of dma buffer
1186 * is converted into two 16 bit addresses Can done by using _attach
1187 * and put into into an array array used may be for differnet pages
1188 */
1189
1190		/*  DMA Start Address Low */
1191		outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
1192		outw((devpriv->ul_DmaBufferHw[0] & 0xFFFF),
1193			devpriv->i_IobaseAddon + 2);
1194
1195		/*************************/
1196		/* DMA Start Address High */
1197		/*************************/
1198		outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
1199		outw((devpriv->ul_DmaBufferHw[0] / 65536),
1200			devpriv->i_IobaseAddon + 2);
1201
1202/*
1203 * 4
1204 * amount of bytes to be transferred set transfer count used ADDON
1205 * MWTC register commented testing
1206 * outl(devpriv->ui_DmaBufferUsesize[0],
1207 * devpriv->i_IobaseAddon+AMCC_OP_REG_AMWTC);
1208 */
1209
1210		/**************************/
1211		/* Nbr of acquisition LOW */
1212		/**************************/
1213		outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
1214		outw((devpriv->ui_DmaBufferUsesize[0] & 0xFFFF),
1215			devpriv->i_IobaseAddon + 2);
1216
1217		/***************************/
1218		/* Nbr of acquisition HIGH */
1219		/***************************/
1220		outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
1221		outw((devpriv->ui_DmaBufferUsesize[0] / 65536),
1222			devpriv->i_IobaseAddon + 2);
1223
1224/*
1225 * 5
1226 * To configure A2P FIFO testing outl(
1227 * FIFO_ADVANCE_ON_BYTE_2,devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR);
1228 */
1229
1230		/******************/
1231		/* A2P FIFO RESET */
1232		/******************/
1233/*
1234 * TO VERIFY BEGIN JK 07.05.04: Comparison between WIN32 and Linux
1235 * driver
1236 */
1237		outl(0x04000000UL, devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
1238		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1239
1240/*
1241 * 6
1242 * ENABLE A2P FIFO WRITE AND ENABLE AMWEN AMWEN_ENABLE |
1243 * A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
1244 */
1245
1246		/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
1247		/* outw(3,devpriv->i_IobaseAddon + 4); */
1248		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1249
1250/*
1251 * 7
1252 * initialise end of dma interrupt AINT_WRITE_COMPL =
1253 * ENABLE_WRITE_TC_INT(ADDI)
1254 */
1255		/***************************************************/
1256		/* A2P FIFO CONFIGURATE, END OF DMA intERRUPT INIT */
1257		/***************************************************/
1258		outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
1259				APCI3120_ENABLE_WRITE_TC_INT),
1260			devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
1261
1262		/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
1263		/******************************************/
1264		/* ENABLE A2P FIFO WRITE AND ENABLE AMWEN */
1265		/******************************************/
1266		outw(3, devpriv->i_IobaseAddon + 4);
1267		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1268
1269		/******************/
1270		/* A2P FIFO RESET */
1271		/******************/
1272		/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
1273		outl(0x04000000UL,
1274			devpriv->i_IobaseAmcc + APCI3120_AMCC_OP_MCSR);
1275		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1276	}
1277
1278	if (devpriv->us_UseDma == APCI3120_DISABLE &&
1279	    cmd->stop_src == TRIG_COUNT) {
1280		/*  set gate 2   to start conversion */
1281		devpriv->us_OutputRegister =
1282			devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER2;
1283		outw(devpriv->us_OutputRegister,
1284			dev->iobase + APCI3120_WR_ADDRESS);
1285	}
1286
1287	switch (mode) {
1288	case 1:
1289		/*  set gate 0   to start conversion */
1290		devpriv->us_OutputRegister =
1291			devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER0;
1292		outw(devpriv->us_OutputRegister,
1293			dev->iobase + APCI3120_WR_ADDRESS);
1294		break;
1295	case 2:
1296		/*  set  gate 0 and gate 1 */
1297		devpriv->us_OutputRegister =
1298			devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER1;
1299		devpriv->us_OutputRegister =
1300			devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER0;
1301		outw(devpriv->us_OutputRegister,
1302			dev->iobase + APCI3120_WR_ADDRESS);
1303		break;
1304
1305	}
1306
1307	return 0;
1308
1309}
1310
1311/*
1312 * Does asynchronous acquisition.
1313 * Determines the mode 1 or 2.
1314 */
1315static int apci3120_ai_cmd(struct comedi_device *dev,
1316			   struct comedi_subdevice *s)
1317{
1318	struct addi_private *devpriv = dev->private;
1319	struct comedi_cmd *cmd = &s->async->cmd;
1320
1321	/* loading private structure with cmd structure inputs */
1322	devpriv->ui_AiNbrofChannels = cmd->chanlist_len;
1323
1324	if (cmd->start_src == TRIG_EXT)
1325		devpriv->b_ExttrigEnable = APCI3120_ENABLE;
1326	else
1327		devpriv->b_ExttrigEnable = APCI3120_DISABLE;
1328
1329	if (cmd->scan_begin_src == TRIG_FOLLOW)
1330		return apci3120_cyclic_ai(1, dev, s);
1331	else	/* TRIG_TIMER */
1332		return apci3120_cyclic_ai(2, dev, s);
1333}
1334
1335/*
1336 * This function copies the data from DMA buffer to the Comedi buffer.
1337 */
1338static void v_APCI3120_InterruptDmaMoveBlock16bit(struct comedi_device *dev,
1339						  struct comedi_subdevice *s,
1340						  unsigned short *dma_buffer,
1341						  unsigned int num_samples)
1342{
1343	struct addi_private *devpriv = dev->private;
1344	struct comedi_cmd *cmd = &s->async->cmd;
1345
1346	devpriv->ui_AiActualScan +=
1347		(s->async->cur_chan + num_samples) / cmd->scan_end_arg;
1348	s->async->cur_chan += num_samples;
1349	s->async->cur_chan %= cmd->scan_end_arg;
1350
1351	cfc_write_array_to_buffer(s, dma_buffer, num_samples * sizeof(short));
1352}
1353
1354/*
1355 * This is a handler for the DMA interrupt.
1356 * This function copies the data to Comedi Buffer.
1357 * For continuous DMA it reinitializes the DMA operation.
1358 * For single mode DMA it stop the acquisition.
1359 */
1360static void apci3120_interrupt_dma(int irq, void *d)
1361{
1362	struct comedi_device *dev = d;
1363	struct addi_private *devpriv = dev->private;
1364	struct comedi_subdevice *s = dev->read_subdev;
1365	struct comedi_cmd *cmd = &s->async->cmd;
1366	unsigned int next_dma_buf, samplesinbuf;
1367	unsigned long low_word, high_word, var;
1368	unsigned int ui_Tmp;
1369
1370	samplesinbuf =
1371		devpriv->ui_DmaBufferUsesize[devpriv->ui_DmaActualBuffer] -
1372		inl(devpriv->i_IobaseAmcc + AMCC_OP_REG_MWTC);
1373
1374	if (samplesinbuf <
1375		devpriv->ui_DmaBufferUsesize[devpriv->ui_DmaActualBuffer]) {
1376		comedi_error(dev, "Interrupted DMA transfer!");
1377	}
1378	if (samplesinbuf & 1) {
1379		comedi_error(dev, "Odd count of bytes in DMA ring!");
1380		apci3120_cancel(dev, s);
1381		return;
1382	}
1383	samplesinbuf = samplesinbuf >> 1;	/*  number of received samples */
1384	if (devpriv->b_DmaDoubleBuffer) {
1385		/*  switch DMA buffers if is used double buffering */
1386		next_dma_buf = 1 - devpriv->ui_DmaActualBuffer;
1387
1388		ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
1389		outl(ui_Tmp, devpriv->i_IobaseAddon + AMCC_OP_REG_AGCSTS);
1390
1391		/*  changed  since 16 bit interface for add on */
1392		outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
1393		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
1394			devpriv->i_IobaseAddon + 2);
1395		outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
1396		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, devpriv->i_IobaseAddon + 2);	/*  0x1000 is out putted in windows driver */
1397
1398		var = devpriv->ul_DmaBufferHw[next_dma_buf];
1399		low_word = var & 0xffff;
1400		var = devpriv->ul_DmaBufferHw[next_dma_buf];
1401		high_word = var / 65536;
1402
1403		/* DMA Start Address Low */
1404		outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
1405		outw(low_word, devpriv->i_IobaseAddon + 2);
1406
1407		/* DMA Start Address High */
1408		outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
1409		outw(high_word, devpriv->i_IobaseAddon + 2);
1410
1411		var = devpriv->ui_DmaBufferUsesize[next_dma_buf];
1412		low_word = var & 0xffff;
1413		var = devpriv->ui_DmaBufferUsesize[next_dma_buf];
1414		high_word = var / 65536;
1415
1416		/* Nbr of acquisition LOW */
1417		outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
1418		outw(low_word, devpriv->i_IobaseAddon + 2);
1419
1420		/* Nbr of acquisition HIGH */
1421		outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
1422		outw(high_word, devpriv->i_IobaseAddon + 2);
1423
1424/*
1425 * To configure A2P FIFO
1426 * ENABLE A2P FIFO WRITE AND ENABLE AMWEN
1427 * AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
1428 */
1429		outw(3, devpriv->i_IobaseAddon + 4);
1430		/* initialise end of dma interrupt  AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI) */
1431		outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
1432				APCI3120_ENABLE_WRITE_TC_INT),
1433			devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
1434
1435	}
1436	if (samplesinbuf) {
1437		v_APCI3120_InterruptDmaMoveBlock16bit(dev, s,
1438			devpriv->ul_DmaBufferVirtual[devpriv->
1439				ui_DmaActualBuffer], samplesinbuf);
1440
1441		if (!(cmd->flags & TRIG_WAKE_EOS)) {
1442			s->async->events |= COMEDI_CB_EOS;
1443			comedi_event(dev, s);
1444		}
1445	}
1446	if (cmd->stop_src == TRIG_COUNT)
1447		if (devpriv->ui_AiActualScan >= cmd->stop_arg) {
1448			/*  all data sampled */
1449			apci3120_cancel(dev, s);
1450			s->async->events |= COMEDI_CB_EOA;
1451			comedi_event(dev, s);
1452			return;
1453		}
1454
1455	if (devpriv->b_DmaDoubleBuffer) {	/*  switch dma buffers */
1456		devpriv->ui_DmaActualBuffer = 1 - devpriv->ui_DmaActualBuffer;
1457	} else {
1458/*
1459 * restart DMA if is not used double buffering
1460 * ADDED REINITIALISE THE DMA
1461 */
1462		ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
1463		outl(ui_Tmp, devpriv->i_IobaseAddon + AMCC_OP_REG_AGCSTS);
1464
1465		/*  changed  since 16 bit interface for add on */
1466		outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
1467		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
1468			devpriv->i_IobaseAddon + 2);
1469		outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
1470		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, devpriv->i_IobaseAddon + 2);	/*  */
1471/*
1472 * A2P FIFO MANAGEMENT
1473 * A2P fifo reset & transfer control enable
1474 */
1475		outl(APCI3120_A2P_FIFO_MANAGEMENT,
1476			devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
1477
1478		var = devpriv->ul_DmaBufferHw[0];
1479		low_word = var & 0xffff;
1480		var = devpriv->ul_DmaBufferHw[0];
1481		high_word = var / 65536;
1482		outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
1483		outw(low_word, devpriv->i_IobaseAddon + 2);
1484		outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
1485		outw(high_word, devpriv->i_IobaseAddon + 2);
1486
1487		var = devpriv->ui_DmaBufferUsesize[0];
1488		low_word = var & 0xffff;	/* changed */
1489		var = devpriv->ui_DmaBufferUsesize[0];
1490		high_word = var / 65536;
1491		outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
1492		outw(low_word, devpriv->i_IobaseAddon + 2);
1493		outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
1494		outw(high_word, devpriv->i_IobaseAddon + 2);
1495
1496/*
1497 * To configure A2P FIFO
1498 * ENABLE A2P FIFO WRITE AND ENABLE AMWEN
1499 * AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
1500 */
1501		outw(3, devpriv->i_IobaseAddon + 4);
1502		/* initialise end of dma interrupt  AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI) */
1503		outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
1504				APCI3120_ENABLE_WRITE_TC_INT),
1505			devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
1506	}
1507}
1508
1509/*
1510 * This function handles EOS interrupt.
1511 * This function copies the acquired data(from FIFO) to Comedi buffer.
1512 */
1513static int apci3120_interrupt_handle_eos(struct comedi_device *dev)
1514{
1515	struct addi_private *devpriv = dev->private;
1516	struct comedi_subdevice *s = dev->read_subdev;
1517	int n_chan, i;
1518	int err = 1;
1519
1520	n_chan = devpriv->ui_AiNbrofChannels;
1521
1522	for (i = 0; i < n_chan; i++)
1523		err &= comedi_buf_put(s, inw(dev->iobase + 0));
1524
1525	s->async->events |= COMEDI_CB_EOS;
1526
1527	if (err == 0)
1528		s->async->events |= COMEDI_CB_OVERFLOW;
1529
1530	comedi_event(dev, s);
1531
1532	return 0;
1533}
1534
1535static void apci3120_interrupt(int irq, void *d)
1536{
1537	struct comedi_device *dev = d;
1538	struct addi_private *devpriv = dev->private;
1539	struct comedi_subdevice *s = dev->read_subdev;
1540	unsigned short int_daq;
1541	unsigned int int_amcc, ui_Check, i;
1542	unsigned short us_TmpValue;
1543	unsigned char b_DummyRead;
1544
1545	ui_Check = 1;
1546
1547	int_daq = inw(dev->iobase + APCI3120_RD_STATUS) & 0xf000;	/*  get IRQ reasons */
1548	int_amcc = inl(devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);	/*  get AMCC int register */
1549
1550	if ((!int_daq) && (!(int_amcc & ANY_S593X_INT))) {
1551		comedi_error(dev, "IRQ from unknown source");
1552		return;
1553	}
1554
1555	outl(int_amcc | 0x00ff0000, devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);	/*  shutdown IRQ reasons in AMCC */
1556
1557	int_daq = (int_daq >> 12) & 0xF;
1558
1559	if (devpriv->b_ExttrigEnable == APCI3120_ENABLE) {
1560		/* Disable ext trigger */
1561		apci3120_exttrig_disable(dev);
1562		devpriv->b_ExttrigEnable = APCI3120_DISABLE;
1563	}
1564	/* clear the timer 2 interrupt */
1565	inb(devpriv->i_IobaseAmcc + APCI3120_TIMER_STATUS_REGISTER);
1566
1567	if (int_amcc & MASTER_ABORT_INT)
1568		comedi_error(dev, "AMCC IRQ - MASTER DMA ABORT!");
1569	if (int_amcc & TARGET_ABORT_INT)
1570		comedi_error(dev, "AMCC IRQ - TARGET DMA ABORT!");
1571
1572	/*  Ckeck if EOC interrupt */
1573	if (((int_daq & 0x8) == 0)
1574		&& (devpriv->b_InterruptMode == APCI3120_EOC_MODE)) {
1575		if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
1576
1577			/*  Read the AI Value */
1578
1579			devpriv->ui_AiReadData[0] =
1580				(unsigned int) inw(devpriv->iobase + 0);
1581			devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
1582			send_sig(SIGIO, devpriv->tsk_Current, 0);	/*  send signal to the sample */
1583		} else {
1584			/* Disable EOC Interrupt */
1585			devpriv->b_ModeSelectRegister =
1586				devpriv->
1587				b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT;
1588			outb(devpriv->b_ModeSelectRegister,
1589				devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
1590
1591		}
1592	}
1593
1594	/*  Check If EOS interrupt */
1595	if ((int_daq & 0x2) && (devpriv->b_InterruptMode == APCI3120_EOS_MODE)) {
1596
1597		if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {	/*  enable this in without DMA ??? */
1598
1599			if (devpriv->ai_running) {
1600				ui_Check = 0;
1601				apci3120_interrupt_handle_eos(dev);
1602				devpriv->ui_AiActualScan++;
1603				devpriv->b_ModeSelectRegister =
1604					devpriv->
1605					b_ModeSelectRegister |
1606					APCI3120_ENABLE_EOS_INT;
1607				outb(devpriv->b_ModeSelectRegister,
1608					dev->iobase +
1609					APCI3120_WRITE_MODE_SELECT);
1610			} else {
1611				ui_Check = 0;
1612				for (i = 0; i < devpriv->ui_AiNbrofChannels;
1613					i++) {
1614					us_TmpValue = inw(devpriv->iobase + 0);
1615					devpriv->ui_AiReadData[i] =
1616						(unsigned int) us_TmpValue;
1617				}
1618				devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
1619				devpriv->b_InterruptMode = APCI3120_EOC_MODE;
1620
1621				send_sig(SIGIO, devpriv->tsk_Current, 0);	/*  send signal to the sample */
1622
1623			}
1624
1625		} else {
1626			devpriv->b_ModeSelectRegister =
1627				devpriv->
1628				b_ModeSelectRegister & APCI3120_DISABLE_EOS_INT;
1629			outb(devpriv->b_ModeSelectRegister,
1630				dev->iobase + APCI3120_WRITE_MODE_SELECT);
1631			devpriv->b_EocEosInterrupt = APCI3120_DISABLE;	/* Default settings */
1632			devpriv->b_InterruptMode = APCI3120_EOC_MODE;
1633		}
1634
1635	}
1636	/* Timer2 interrupt */
1637	if (int_daq & 0x1) {
1638
1639		switch (devpriv->b_Timer2Mode) {
1640		case APCI3120_COUNTER:
1641			devpriv->b_ModeSelectRegister =
1642				devpriv->
1643				b_ModeSelectRegister & APCI3120_DISABLE_EOS_INT;
1644			outb(devpriv->b_ModeSelectRegister,
1645				dev->iobase + APCI3120_WRITE_MODE_SELECT);
1646
1647			/*  stop timer 2 */
1648			devpriv->us_OutputRegister =
1649				devpriv->
1650				us_OutputRegister & APCI3120_DISABLE_ALL_TIMER;
1651			outw(devpriv->us_OutputRegister,
1652				dev->iobase + APCI3120_WR_ADDRESS);
1653
1654			/* stop timer 0 and timer 1 */
1655			apci3120_cancel(dev, s);
1656
1657			/* UPDATE-0.7.57->0.7.68comedi_done(dev,s); */
1658			s->async->events |= COMEDI_CB_EOA;
1659			comedi_event(dev, s);
1660
1661			break;
1662
1663		case APCI3120_TIMER:
1664
1665			/* Send a signal to from kernel to user space */
1666			send_sig(SIGIO, devpriv->tsk_Current, 0);
1667			break;
1668
1669		case APCI3120_WATCHDOG:
1670
1671			/* Send a signal to from kernel to user space */
1672			send_sig(SIGIO, devpriv->tsk_Current, 0);
1673			break;
1674
1675		default:
1676
1677			/*  disable Timer Interrupt */
1678
1679			devpriv->b_ModeSelectRegister =
1680				devpriv->
1681				b_ModeSelectRegister &
1682				APCI3120_DISABLE_TIMER_INT;
1683
1684			outb(devpriv->b_ModeSelectRegister,
1685				dev->iobase + APCI3120_WRITE_MODE_SELECT);
1686
1687		}
1688
1689		b_DummyRead = inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
1690
1691	}
1692
1693	if ((int_daq & 0x4) && (devpriv->b_InterruptMode == APCI3120_DMA_MODE)) {
1694		if (devpriv->ai_running) {
1695
1696			/****************************/
1697			/* Clear Timer Write TC int */
1698			/****************************/
1699
1700			outl(APCI3120_CLEAR_WRITE_TC_INT,
1701				devpriv->i_IobaseAmcc +
1702				APCI3120_AMCC_OP_REG_INTCSR);
1703
1704			/************************************/
1705			/* Clears the timer status register */
1706			/************************************/
1707			inw(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
1708			/* do some data transfer */
1709			apci3120_interrupt_dma(irq, d);
1710		} else {
1711			/* Stops the Timer */
1712			outw(devpriv->
1713				us_OutputRegister & APCI3120_DISABLE_TIMER0 &
1714				APCI3120_DISABLE_TIMER1,
1715				dev->iobase + APCI3120_WR_ADDRESS);
1716		}
1717
1718	}
1719
1720	return;
1721}
1722
1723/*
1724 * Configure Timer 2
1725 *
1726 * data[0] = TIMER configure as timer
1727 *	   = WATCHDOG configure as watchdog
1728 * data[1] = Timer constant
1729 * data[2] = Timer2 interrupt (1)enable or(0) disable
1730 */
1731static int apci3120_config_insn_timer(struct comedi_device *dev,
1732				      struct comedi_subdevice *s,
1733				      struct comedi_insn *insn,
1734				      unsigned int *data)
1735{
1736	const struct addi_board *this_board = comedi_board(dev);
1737	struct addi_private *devpriv = dev->private;
1738	unsigned int ui_Timervalue2;
1739	unsigned short us_TmpValue;
1740	unsigned char b_Tmp;
1741
1742	if (!data[1])
1743		comedi_error(dev, "config:No timer constant !");
1744
1745	devpriv->b_Timer2Interrupt = (unsigned char) data[2];	/*  save info whether to enable or disable interrupt */
1746
1747	ui_Timervalue2 = data[1] / 1000;	/*  convert nano seconds  to u seconds */
1748
1749	/* this_board->timer_config(dev, ui_Timervalue2,(unsigned char)data[0]); */
1750	us_TmpValue = (unsigned short) inw(devpriv->iobase + APCI3120_RD_STATUS);
1751
1752/*
1753 * EL250804: Testing if board APCI3120 have the new Quartz or if it
1754 * is an APCI3001 and calculate the time value to set in the timer
1755 */
1756	if ((us_TmpValue & 0x00B0) == 0x00B0
1757		|| !strcmp(this_board->pc_DriverName, "apci3001")) {
1758		/* Calculate the time value to set in the timer */
1759		ui_Timervalue2 = ui_Timervalue2 / 50;
1760	} else {
1761		/* Calculate the time value to set in the timer */
1762		ui_Timervalue2 = ui_Timervalue2 / 70;
1763	}
1764
1765	/* Reset gate 2 of Timer 2 to disable it (Set Bit D14 to 0) */
1766	devpriv->us_OutputRegister =
1767		devpriv->us_OutputRegister & APCI3120_DISABLE_TIMER2;
1768	outw(devpriv->us_OutputRegister, devpriv->iobase + APCI3120_WR_ADDRESS);
1769
1770	/*  Disable TIMER Interrupt */
1771	devpriv->b_ModeSelectRegister =
1772		devpriv->
1773		b_ModeSelectRegister & APCI3120_DISABLE_TIMER_INT & 0xEF;
1774
1775	/*  Disable Eoc and Eos Interrupts */
1776	devpriv->b_ModeSelectRegister =
1777		devpriv->
1778		b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT &
1779		APCI3120_DISABLE_EOS_INT;
1780	outb(devpriv->b_ModeSelectRegister,
1781		devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
1782	if (data[0] == APCI3120_TIMER) {	/* initialize timer */
1783		/* devpriv->b_ModeSelectRegister=devpriv->b_ModeSelectRegister |
1784		 * APCI3120_ENABLE_TIMER_INT; */
1785
1786		/* outb(devpriv->b_ModeSelectRegister,devpriv->iobase+APCI3120_WRITE_MODE_SELECT); */
1787
1788		/* Set the Timer 2 in mode 2(Timer) */
1789		devpriv->b_TimerSelectMode =
1790			(devpriv->
1791			b_TimerSelectMode & 0x0F) | APCI3120_TIMER_2_MODE_2;
1792		outb(devpriv->b_TimerSelectMode,
1793			devpriv->iobase + APCI3120_TIMER_CRT1);
1794
1795/*
1796 * Configure the timer 2 for writing the LOW unsigned short of timer
1797 * is Delay value You must make a b_tmp variable with
1798 * DigitalOutPutRegister because at Address_1+APCI3120_TIMER_CRT0
1799 * you can set the digital output and configure the timer 2,and if
1800 * you don't make this, digital output are erase (Set to 0)
1801 */
1802
1803		/* Writing LOW unsigned short */
1804		b_Tmp = ((devpriv->
1805				b_DigitalOutputRegister) & 0xF0) |
1806			APCI3120_SELECT_TIMER_2_LOW_WORD;
1807		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
1808		outw(LOWORD(ui_Timervalue2),
1809			devpriv->iobase + APCI3120_TIMER_VALUE);
1810
1811		/* Writing HIGH unsigned short */
1812		b_Tmp = ((devpriv->
1813				b_DigitalOutputRegister) & 0xF0) |
1814			APCI3120_SELECT_TIMER_2_HIGH_WORD;
1815		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
1816		outw(HIWORD(ui_Timervalue2),
1817			devpriv->iobase + APCI3120_TIMER_VALUE);
1818		/*  timer2 in Timer mode enabled */
1819		devpriv->b_Timer2Mode = APCI3120_TIMER;
1820
1821	} else {			/*  Initialize Watch dog */
1822
1823		/* Set the Timer 2 in mode 5(Watchdog) */
1824
1825		devpriv->b_TimerSelectMode =
1826			(devpriv->
1827			b_TimerSelectMode & 0x0F) | APCI3120_TIMER_2_MODE_5;
1828		outb(devpriv->b_TimerSelectMode,
1829			devpriv->iobase + APCI3120_TIMER_CRT1);
1830
1831/*
1832 * Configure the timer 2 for writing the LOW unsigned short of timer
1833 * is Delay value You must make a b_tmp variable with
1834 * DigitalOutPutRegister because at Address_1+APCI3120_TIMER_CRT0
1835 * you can set the digital output and configure the timer 2,and if
1836 * you don't make this, digital output are erase (Set to 0)
1837 */
1838
1839		/* Writing LOW unsigned short */
1840		b_Tmp = ((devpriv->
1841				b_DigitalOutputRegister) & 0xF0) |
1842			APCI3120_SELECT_TIMER_2_LOW_WORD;
1843		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
1844		outw(LOWORD(ui_Timervalue2),
1845			devpriv->iobase + APCI3120_TIMER_VALUE);
1846
1847		/* Writing HIGH unsigned short */
1848		b_Tmp = ((devpriv->
1849				b_DigitalOutputRegister) & 0xF0) |
1850			APCI3120_SELECT_TIMER_2_HIGH_WORD;
1851		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
1852
1853		outw(HIWORD(ui_Timervalue2),
1854			devpriv->iobase + APCI3120_TIMER_VALUE);
1855		/* watchdog enabled */
1856		devpriv->b_Timer2Mode = APCI3120_WATCHDOG;
1857
1858	}
1859
1860	return insn->n;
1861
1862}
1863
1864/*
1865 * To start and stop the timer
1866 *
1867 * data[0] = 1 (start)
1868 *	   = 0 (stop)
1869 *	   = 2 (write new value)
1870 * data[1] = new value
1871 *
1872 * devpriv->b_Timer2Mode = 0 DISABLE
1873 *			 = 1 Timer
1874 *			 = 2 Watch dog
1875 */
1876static int apci3120_write_insn_timer(struct comedi_device *dev,
1877				     struct comedi_subdevice *s,
1878				     struct comedi_insn *insn,
1879				     unsigned int *data)
1880{
1881	const struct addi_board *this_board = comedi_board(dev);
1882	struct addi_private *devpriv = dev->private;
1883	unsigned int ui_Timervalue2 = 0;
1884	unsigned short us_TmpValue;
1885	unsigned char b_Tmp;
1886
1887	if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG)
1888		&& (devpriv->b_Timer2Mode != APCI3120_TIMER)) {
1889		comedi_error(dev, "\nwrite:timer2  not configured ");
1890		return -EINVAL;
1891	}
1892
1893	if (data[0] == 2) {	/*  write new value */
1894		if (devpriv->b_Timer2Mode != APCI3120_TIMER) {
1895			comedi_error(dev,
1896				"write :timer2  not configured  in TIMER MODE");
1897			return -EINVAL;
1898		}
1899
1900		if (data[1])
1901			ui_Timervalue2 = data[1];
1902		else
1903			ui_Timervalue2 = 0;
1904	}
1905
1906	/* this_board->timer_write(dev,data[0],ui_Timervalue2); */
1907
1908	switch (data[0]) {
1909	case APCI3120_START:
1910
1911		/*  Reset FC_TIMER BIT */
1912		inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
1913		if (devpriv->b_Timer2Mode == APCI3120_TIMER) {	/* start timer */
1914			/* Enable Timer */
1915			devpriv->b_ModeSelectRegister =
1916				devpriv->b_ModeSelectRegister & 0x0B;
1917		} else {		/* start watch dog */
1918			/* Enable WatchDog */
1919			devpriv->b_ModeSelectRegister =
1920				(devpriv->
1921				b_ModeSelectRegister & 0x0B) |
1922				APCI3120_ENABLE_WATCHDOG;
1923		}
1924
1925		/* enable disable interrupt */
1926		if ((devpriv->b_Timer2Interrupt) == APCI3120_ENABLE) {
1927
1928			devpriv->b_ModeSelectRegister =
1929				devpriv->
1930				b_ModeSelectRegister |
1931				APCI3120_ENABLE_TIMER_INT;
1932			/*  save the task structure to pass info to user */
1933			devpriv->tsk_Current = current;
1934		} else {
1935
1936			devpriv->b_ModeSelectRegister =
1937				devpriv->
1938				b_ModeSelectRegister &
1939				APCI3120_DISABLE_TIMER_INT;
1940		}
1941		outb(devpriv->b_ModeSelectRegister,
1942			devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
1943
1944		if (devpriv->b_Timer2Mode == APCI3120_TIMER) {	/* start timer */
1945			/* For Timer mode is  Gate2 must be activated   **timer started */
1946			devpriv->us_OutputRegister =
1947				devpriv->
1948				us_OutputRegister | APCI3120_ENABLE_TIMER2;
1949			outw(devpriv->us_OutputRegister,
1950				devpriv->iobase + APCI3120_WR_ADDRESS);
1951		}
1952
1953		break;
1954
1955	case APCI3120_STOP:
1956		if (devpriv->b_Timer2Mode == APCI3120_TIMER) {
1957			/* Disable timer */
1958			devpriv->b_ModeSelectRegister =
1959				devpriv->
1960				b_ModeSelectRegister &
1961				APCI3120_DISABLE_TIMER_COUNTER;
1962		} else {
1963			/* Disable WatchDog */
1964			devpriv->b_ModeSelectRegister =
1965				devpriv->
1966				b_ModeSelectRegister &
1967				APCI3120_DISABLE_WATCHDOG;
1968		}
1969		/*  Disable timer interrupt */
1970		devpriv->b_ModeSelectRegister =
1971			devpriv->
1972			b_ModeSelectRegister & APCI3120_DISABLE_TIMER_INT;
1973
1974		/*  Write above states  to register */
1975		outb(devpriv->b_ModeSelectRegister,
1976			devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
1977
1978		/*  Reset Gate 2 */
1979		devpriv->us_OutputRegister =
1980			devpriv->us_OutputRegister & APCI3120_DISABLE_TIMER_INT;
1981		outw(devpriv->us_OutputRegister,
1982			devpriv->iobase + APCI3120_WR_ADDRESS);
1983
1984		/*  Reset FC_TIMER BIT */
1985		inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
1986
1987		/* Disable timer */
1988		/* devpriv->b_Timer2Mode=APCI3120_DISABLE;  */
1989
1990		break;
1991
1992	case 2:		/* write new value to Timer */
1993		if (devpriv->b_Timer2Mode != APCI3120_TIMER) {
1994			comedi_error(dev,
1995				"write :timer2  not configured  in TIMER MODE");
1996			return -EINVAL;
1997		}
1998		/*  ui_Timervalue2=data[1]; // passed as argument */
1999		us_TmpValue =
2000			(unsigned short) inw(devpriv->iobase + APCI3120_RD_STATUS);
2001
2002/*
2003 * EL250804: Testing if board APCI3120 have the new Quartz or if it
2004 * is an APCI3001 and calculate the time value to set in the timer
2005 */
2006		if ((us_TmpValue & 0x00B0) == 0x00B0
2007			|| !strcmp(this_board->pc_DriverName, "apci3001")) {
2008			/* Calculate the time value to set in the timer */
2009			ui_Timervalue2 = ui_Timervalue2 / 50;
2010		} else {
2011			/* Calculate the time value to set in the timer */
2012			ui_Timervalue2 = ui_Timervalue2 / 70;
2013		}
2014		/* Writing LOW unsigned short */
2015		b_Tmp = ((devpriv->
2016				b_DigitalOutputRegister) & 0xF0) |
2017			APCI3120_SELECT_TIMER_2_LOW_WORD;
2018		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2019
2020		outw(LOWORD(ui_Timervalue2),
2021			devpriv->iobase + APCI3120_TIMER_VALUE);
2022
2023		/* Writing HIGH unsigned short */
2024		b_Tmp = ((devpriv->
2025				b_DigitalOutputRegister) & 0xF0) |
2026			APCI3120_SELECT_TIMER_2_HIGH_WORD;
2027		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2028
2029		outw(HIWORD(ui_Timervalue2),
2030			devpriv->iobase + APCI3120_TIMER_VALUE);
2031
2032		break;
2033	default:
2034		return -EINVAL;	/*  Not a valid input */
2035	}
2036
2037	return insn->n;
2038}
2039
2040/*
2041 * Read the Timer value
2042 *
2043 * for Timer: data[0]= Timer constant
2044 *
2045 * for watchdog: data[0] = 0 (still running)
2046 *			 = 1 (run down)
2047 */
2048static int apci3120_read_insn_timer(struct comedi_device *dev,
2049				    struct comedi_subdevice *s,
2050				    struct comedi_insn *insn,
2051				    unsigned int *data)
2052{
2053	struct addi_private *devpriv = dev->private;
2054	unsigned char b_Tmp;
2055	unsigned short us_TmpValue, us_TmpValue_2, us_StatusValue;
2056
2057	if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG)
2058		&& (devpriv->b_Timer2Mode != APCI3120_TIMER)) {
2059		comedi_error(dev, "\nread:timer2  not configured ");
2060	}
2061
2062	/* this_board->timer_read(dev,data); */
2063	if (devpriv->b_Timer2Mode == APCI3120_TIMER) {
2064
2065		/* Read the LOW unsigned short of Timer 2 register */
2066		b_Tmp = ((devpriv->
2067				b_DigitalOutputRegister) & 0xF0) |
2068			APCI3120_SELECT_TIMER_2_LOW_WORD;
2069		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2070
2071		us_TmpValue = inw(devpriv->iobase + APCI3120_TIMER_VALUE);
2072
2073		/* Read the HIGH unsigned short of Timer 2 register */
2074		b_Tmp = ((devpriv->
2075				b_DigitalOutputRegister) & 0xF0) |
2076			APCI3120_SELECT_TIMER_2_HIGH_WORD;
2077		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2078
2079		us_TmpValue_2 = inw(devpriv->iobase + APCI3120_TIMER_VALUE);
2080
2081		/*  combining both words */
2082		data[0] = (unsigned int) ((us_TmpValue) | ((us_TmpValue_2) << 16));
2083
2084	} else {			/*  Read watch dog status */
2085
2086		us_StatusValue = inw(devpriv->iobase + APCI3120_RD_STATUS);
2087		us_StatusValue =
2088			((us_StatusValue & APCI3120_FC_TIMER) >> 12) & 1;
2089		if (us_StatusValue == 1) {
2090			/*  RESET FC_TIMER BIT */
2091			inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
2092		}
2093		data[0] = us_StatusValue;	/*  when data[0] = 1 then the watch dog has rundown */
2094	}
2095	return insn->n;
2096}
2097
2098static int apci3120_di_insn_bits(struct comedi_device *dev,
2099				 struct comedi_subdevice *s,
2100				 struct comedi_insn *insn,
2101				 unsigned int *data)
2102{
2103	struct addi_private *devpriv = dev->private;
2104	unsigned int val;
2105
2106	/* the input channels are bits 11:8 of the status reg */
2107	val = inw(devpriv->iobase + APCI3120_RD_STATUS);
2108	data[1] = (val >> 8) & 0xf;
2109
2110	return insn->n;
2111}
2112
2113static int apci3120_do_insn_bits(struct comedi_device *dev,
2114				 struct comedi_subdevice *s,
2115				 struct comedi_insn *insn,
2116				 unsigned int *data)
2117{
2118	struct addi_private *devpriv = dev->private;
2119
2120	if (comedi_dio_update_state(s, data)) {
2121		/* The do channels are bits 7:4 of the do register */
2122		devpriv->b_DigitalOutputRegister = s->state << 4;
2123
2124		outb(devpriv->b_DigitalOutputRegister,
2125		     devpriv->iobase + APCI3120_DIGITAL_OUTPUT);
2126	}
2127
2128	data[1] = s->state;
2129
2130	return insn->n;
2131}
2132
2133static int apci3120_ao_insn_write(struct comedi_device *dev,
2134				  struct comedi_subdevice *s,
2135				  struct comedi_insn *insn,
2136				  unsigned int *data)
2137{
2138	struct addi_private *devpriv = dev->private;
2139	unsigned int ui_Range, ui_Channel;
2140	unsigned short us_TmpValue;
2141
2142	ui_Range = CR_RANGE(insn->chanspec);
2143	ui_Channel = CR_CHAN(insn->chanspec);
2144
2145	/* this_board->ao_write(dev, ui_Range, ui_Channel,data[0]); */
2146	if (ui_Range) {		/*  if 1 then unipolar */
2147
2148		if (data[0] != 0)
2149			data[0] =
2150				((((ui_Channel & 0x03) << 14) & 0xC000) | (1 <<
2151					13) | (data[0] + 8191));
2152		else
2153			data[0] =
2154				((((ui_Channel & 0x03) << 14) & 0xC000) | (1 <<
2155					13) | 8192);
2156
2157	} else {			/*  if 0 then   bipolar */
2158		data[0] =
2159			((((ui_Channel & 0x03) << 14) & 0xC000) | (0 << 13) |
2160			data[0]);
2161
2162	}
2163
2164/*
2165 * out put n values at the given channel. printk("\nwaiting for
2166 * DA_READY BIT");
2167 */
2168	do {			/* Waiting of DA_READY BIT */
2169		us_TmpValue =
2170			((unsigned short) inw(devpriv->iobase +
2171				APCI3120_RD_STATUS)) & 0x0001;
2172	} while (us_TmpValue != 0x0001);
2173
2174	if (ui_Channel <= 3)
2175/*
2176 * for channel 0-3 out at the register 1 (wrDac1-8) data[i]
2177 * typecasted to ushort since word write is to be done
2178 */
2179		outw((unsigned short) data[0],
2180			devpriv->iobase + APCI3120_ANALOG_OUTPUT_1);
2181	else
2182/*
2183 * for channel 4-7 out at the register 2 (wrDac5-8) data[i]
2184 * typecasted to ushort since word write is to be done
2185 */
2186		outw((unsigned short) data[0],
2187			devpriv->iobase + APCI3120_ANALOG_OUTPUT_2);
2188
2189	return insn->n;
2190}
2191