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