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